I have a .NET API project with the following API endpoint, but the Swagger UI and the generated swagger.json file only show options for the PetJsonRequestInfo model class, regardless of the content type. I need to configure Swagger to correctly display different model classes based on the content type (multipart/form-data and application/json).
Here's the relevant code for my API endpoint:
public class PetRequestInfo
{
// File upload specific to multipart/form-data
public IFormFile PetImage { get; set; }
// Common properties
public string PetName { get; set; }
public int PetAge { get; set; }
}
public class PetJsonRequestInfo
{
// No file upload, JSON only
// Common properties
public string PetName { get; set; }
public int PetAge { get; set; }
}
// POST: api/pets/add
[HttpPost("add")]
[Consumes("multipart/form-data", "application/json")]
public async Task<ActionResult<PetAdded>> AddPet([FromForm] PetRequestInfo petRequestInfo, [FromBody] PetJsonRequestInfo petJsonRequestInfo)
{
// Validate inputs
if (petRequestInfo == null || petJsonRequestInfo == null)
{
return BadRequest("Invalid request data.");
}
// logic will be here...
}
The Swagger file currently looks like this:
"/v1/Pet/add": {
"post": {
"summary": "Add a new pet.",
"requestBody": {
"description": "The pet request body.",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/PetJsonRequestInfo"
}
},
"multipart/form-data": {
"schema": {
"$ref": "#/components/schemas/PetJsonRequestInfo"
}
},
"application/x-www-form-urlencoded": {
"schema": {
"$ref": "#/components/schemas/PetJsonRequestInfo"
}
}
}
},
"responses": {
"201": {
"description": "Created",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/PetAdded"
}
}
}
},
"401": {
"description": "Unauthorized",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ErrorResult"
}
}
}
},
"403": {
"description": "Forbidden",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ErrorResult"
}
}
}
}
}
}
}
I want the Swagger documentation to reflect the correct model class based on the content type, like this. How can I achieve this? Any help would be greatly appreciated!
"requestBody": {
"description": "The pet request body.",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/PetJsonRequestInfo"
}
},
"multipart/form-data": {
"schema": {
"$ref": "#/components/schemas/PetRequestInfo"
}
},
"application/x-www-form-urlencoded": {
"schema": {
"$ref": "#/components/schemas/PetRequestInfo"
}
}
}
}
You could implement CustomOperationFilter
to achieve this. Such as following:
Change operation when relavatePath is "api/pets/add"
public class CustomOperationFilter : IOperationFilter
{
public void Apply(OpenApiOperation operation, OperationFilterContext context)
{
if (context.ApiDescription.RelativePath.Equals("api/pets/add", StringComparison.OrdinalIgnoreCase))
{
operation.RequestBody.Content.Clear();
operation.RequestBody.Content.Add("application/json", new OpenApiMediaType
{
Schema = context.SchemaGenerator.GenerateSchema(typeof(PetJsonRequestInfo), context.SchemaRepository)
});
operation.RequestBody.Content.Add("multipart/form-data", new OpenApiMediaType
{
Schema = context.SchemaGenerator.GenerateSchema(typeof(PetRequestInfo), context.SchemaRepository)
});
}
}
}
Add the filter in program.cs
builder.Services.AddSwaggerGen(c =>
{
c.OperationFilter<CustomOperationFilter>();
});
Create action using Dynamic type.
[Route("api/[controller]")]
[ApiController]
public class ValuesController : ControllerBase
{
[HttpPost("test")]
public void Post3(object requestBody)
{
if (JsonSerializer.Serialize(requestBody).Contains("petName"))
{
PetJsonRequestInfo result = (PetJsonRequestInfo)requestBody;
...
}
else{
PetRequestInfo result = (PetRequestInfo)requestBody;
...
}
}
}