能否在ASP.NET Core(Swashbuckle)中按HTTP状态码设置响应Content-Type?含OpenAPI3.0场景
application/json for Success, application/problem+json for 400 Errors Great question—let’s break this down clearly, since this is a common scenario that doesn’t work out of the box as you’ve noticed.
First: Confirmation on "Out of the Box" Support
You’re absolutely right: ASP.NET Core 3.0 and Swashbuckle.AspNetCore (versions compatible with 3.0, typically 5.x) do NOT support this scenario natively.
By default:
- Swashbuckle generates uniform response content types for most status codes (often including both
application/jsonand plain text variants) - While ASP.NET Core’s
ProblemDetailsreturnsapplication/problem+jsonby default for validation errors, Swagger docs won’t automatically distinguish this from success responses or restrict the 400 response to only that media type.
Systemized Implementation Solution
Here’s a complete, maintainable way to set this up:
1. Configure ASP.NET Core to Return application/problem+json for 400 Errors
First, ensure your API properly sets the correct Content-Type when returning validation errors. Use ApiBehaviorOptions to override the default invalid model state response:
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.ModelBinding; public void ConfigureServices(IServiceCollection services) { services.AddControllers() .ConfigureApiBehaviorOptions(options => { options.InvalidModelStateResponseFactory = context => { var validationProblem = new ValidationProblemDetails(context.ModelState) { Type = "https://tools.ietf.org/html/rfc7231#section-6.5.1", Title = "Validation Failed", Status = StatusCodes.Status400BadRequest, Detail = "Check the errors field for specific issues.", Instance = context.HttpContext.Request.Path }; // Add a trace ID for debugging (optional but useful) validationProblem.Extensions.Add("traceId", context.HttpContext.TraceIdentifier); // Explicitly set the Content-Type to application/problem+json return new BadRequestObjectResult(validationProblem) { ContentTypes = { "application/problem+json" } }; }; }); }
This ensures any 400 from model validation returns the correct media type.
2. Customize Swashbuckle to Update OpenAPI Docs
Next, create a custom IOperationFilter to modify the Swagger document:
- Remove default 400 response definitions
- Add a 400 response that only includes
application/problem+json - Ensure success responses only show
application/json
using Microsoft.OpenApi.Models; using Swashbuckle.AspNetCore.SwaggerGen; using System.Linq; public class ProblemDetailsOperationFilter : IOperationFilter { public void Apply(OpenApiOperation operation, OperationFilterContext context) { // Clean up existing 400 response if present if (operation.Responses.ContainsKey("400")) { operation.Responses.Remove("400"); } // Generate schema for ProblemDetails var problemSchema = context.SchemaGenerator.GenerateSchema(typeof(ProblemDetails), context.SchemaRepository); // Add custom 400 response with application/problem+json only operation.Responses.Add("400", new OpenApiResponse { Description = "Validation Error", Content = new Dictionary<string, OpenApiMediaType> { ["application/problem+json"] = new OpenApiMediaType { Schema = problemSchema } } }); // Ensure success responses only use application/json foreach (var (statusCode, response) in operation.Responses.Where(r => r.Key != "400")) { // Remove unwanted media types (like text/plain) var unwantedContentTypes = response.Content.Keys.Where(ct => ct != "application/json").ToList(); foreach (var ct in unwantedContentTypes) { response.Content.Remove(ct); } // If no application/json exists, add it with the existing schema if (!response.Content.ContainsKey("application/json") && response.Content.Any()) { response.Content.Add("application/json", response.Content.Values.First()); } } } }
Register this filter in your Swagger setup:
public void ConfigureServices(IServiceCollection services) { // ... other services ... services.AddSwaggerGen(c => { c.SwaggerDoc("v1", new OpenApiInfo { Title = "Your API", Version = "v1" }); // Register the custom filter c.OperationFilter<ProblemDetailsOperationFilter>(); // Optional: Explicitly define the ProblemDetails schema for consistency c.MapType<ProblemDetails>(() => new OpenApiSchema { Type = "object", Properties = new Dictionary<string, OpenApiSchema> { ["type"] = new OpenApiSchema { Type = "string" }, ["title"] = new OpenApiSchema { Type = "string" }, ["status"] = new OpenApiSchema { Type = "integer", Format = "int32" }, ["detail"] = new OpenApiSchema { Type = "string" }, ["instance"] = new OpenApiSchema { Type = "string" }, ["traceId"] = new OpenApiSchema { Type = "string" } } }); }); }
3. Test and Validate
- Run your API and trigger a 400 error (e.g., submit invalid model data) — check the response headers to confirm
Content-Type: application/problem+json - Open the Swagger UI: Verify that 400 responses only show
application/problem+json, while success responses (200, 201, etc.) only listapplication/json
OpenAPI 3.0 Specification Support
Your hunch is correct: this is fully supported in OpenAPI 3.0. The spec explicitly allows defining different media types per response status code.
In the generated OpenAPI document, the structure will look like this:
responses: '200': description: Success content: application/json: schema: $ref: '#/components/schemas/YourSuccessModel' '400': description: Validation Error content: application/problem+json: schema: $ref: '#/components/schemas/ProblemDetails'
This aligns perfectly with your requirement, and our implementation ensures Swashbuckle generates exactly this structure.
内容的提问来源于stack exchange,提问作者Veksi

