Home > Software design >  Swashbuckle ISchemaFilter based on method
Swashbuckle ISchemaFilter based on method

Time:08-26

I'm trying to set an ISchemaFilter for my API that will return a different model schema for different methods, depending on which properties of the model each method needs to process. Say I have

public class Dog
{
  public int Legs;
  public double GoodBoiBarksThisLoud;
}

and then 2 POST methods

public async Task<IActionResult> ShowMeThoseLegs([FromBody]Dog sexyDog);
public async Task<IActionResult> Bark([FromBody]Dog loudDog);

There's no need for Swagger UI to show property info for Legs in the description of Bark, yet both target the same model object and it makes sense (imo) to pass the whole model object class as parameter, otherwise I'd have to split into subclasses which I find somehow not the best. (Think bigger here of course, my actual model has dozens of properties.)

What I would like to achieve is the following

...AddSwaggerGen(c => {
  ...
  c.SchemaFilter<DogSwaggerFilter>();
  ...
});

public class DogSwaggerFilter : ISchemaFilter
{
  public void Apply(OpenApiSchema schema, SchemaFilterContext context)
  {
    var method_name = getMethod("But how?");

    if (method_name == "Bark")
    {
      // return sub-schema of Dog with just property GoodBoiBarksThisLoud
    }
    else if (method_name == "ShowMeThoseLegs")
    {
      // return sub-schema of Dog with just property Legs
    }
  }
}

Which seems like it should be possible, the Filter.Apply seems to be getting called for each property of the model as many times as methods my API has implemented, so the reference to the method is in the background somewhere, just not publicly exposed.

Can it be done?

CodePudding user response:

You need to use OperationFilter. you can use it like below.

    public class CustomOperationFilter : IOperationFilter
    {
        public void Apply(OpenApiOperation operation, OperationFilterContext context)
        {
            if (operation.OperationId == "Bark")
            {
                operation.RequestBody.Content["application/json"].Example =
                    OpenApiAnyFactory.CreateFromJson("{\"GoodBoiBarksThisLoud\":0}");
            }
            if (operation.OperationId == "ShowMeThoseLegs")
            {
                operation.RequestBody.Content["application/json"].Example =
                    OpenApiAnyFactory.CreateFromJson("{\"Legs\":0}");
            }
        }
    }

and in Program.cs

builder.Services.AddSwaggerGen(
    o => {
        o.CustomOperationIds(e => $"{e.ActionDescriptor.RouteValues["action"]}");
        o.OperationFilter<CustomOperationFilter>();
    });

then you will see swagger ui something like this.

enter image description here

Hope this helps.

  • Related