如何阻止Swashbuckle自动设置Accept请求头?
我有一个.NET 7 Web API项目,使用Swashbuckle.AspNetCore生成Swagger UI。其中一个接口会根据响应状态码返回不同的Content-Type:200状态返回application/xml,404状态返回application/json。
我已通过在控制器方法上添加SwaggerResponse特性,让Swagger UI正确展示200的XML示例响应和404的JSON示例响应,这部分正常。
但实际从Swagger UI调用该接口时,即便返回404状态,响应始终是XML格式;而用Postman调用时,404状态会返回JSON。经排查发现:Swagger UI会自动传递Accept: application/xml请求头,触发了内容协商。
Swagger界面中第一个示例旁标注的“Controls Accept header”也印证了这一点。
请问是否可以修改该行为,让Swashbuckle不再自动设置Accept请求头?
复现问题的极简代码示例
Program.cs
var builder = WebApplication.CreateBuilder(args); // Add services to the container. builder.Services.AddControllers(); builder.Services .AddMvc() .AddXmlSerializerFormatters(); // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle builder.Services.AddEndpointsApiExplorer(); builder.Services.AddSwaggerGen(c => c.EnableAnnotations()); var app = builder.Build(); // Configure the HTTP request pipeline. if (app.Environment.IsDevelopment()) { app.UseSwagger(); app.UseSwaggerUI(); } app.UseHttpsRedirection(); app.UseAuthorization(); app.MapControllers(); app.Run();
WeatherForecastController.cs
using System.Runtime.Serialization; using System.Xml.Linq; using Microsoft.AspNetCore.Mvc; using Swashbuckle.AspNetCore.Annotations; namespace WebApplication1.Controllers { [ApiController] [Route("[controller]")] public class WeatherForecastController : ControllerBase { private static readonly string[] Summaries = new[] { "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching" }; private readonly ILogger<WeatherForecastController> _logger; public WeatherForecastController(ILogger<WeatherForecastController> logger) { _logger = logger; } [HttpGet(Name = "GetWeatherForecast")] [Produces("application/xml", "application/json")] [SwaggerResponse(200, Type = typeof(XDocument), ContentTypes = new[] { "application/xml" })] [SwaggerResponse(404, Type = typeof(ProblemDetails), ContentTypes = new[] { "application/json" })] public ActionResult<WeatherForecast> Get(string region) { if (region is not "Yorkshire") { return this.NotFound(); } var forecast = Enumerable.Range(1, 5).Select(index => new WeatherForecast { Date = DateOnly.FromDateTime(DateTime.Now.AddDays(index)), TemperatureC = Random.Shared.Next(-20, 55), Summary = Summaries[Random.Shared.Next(Summaries.Length)] }) .ToArray(); var doc = new XDocument(); using (var writer = doc.CreateWriter()) { // write xml into the writer var serializer = new DataContractSerializer(forecast.GetType()); serializer.WriteObject(writer, forecast); } return this.Content(doc.ToString(), "application/xml"); } } }
WeatherForecast.cs
namespace WebApplication1 { public class WeatherForecast { public DateOnly Date { get; set; } public int TemperatureC { get; set; } public int TemperatureF => 32 + (int)(TemperatureC / 0.5556); public string? Summary { get; set; } } }
有两种可行方式修改Swashbuckle的自动设置Accept头行为:
方法1:全局禁用Swagger UI的Accept头自动设置
在配置SwaggerUI时,通过添加请求拦截器移除自动生成的Accept头:
修改Program.cs中的UseSwaggerUI部分:
app.UseSwaggerUI(c => { c.RequestInterceptor = "function(request) { delete request.headers['Accept']; return request; }"; });
这段JavaScript会在请求发送前删除Swagger UI自动添加的Accept头,让ASP.NET Core的内容协商逻辑根据接口实际返回的Content-Type响应,不再被强制头干扰。
方法2:针对单个接口调整
如果不想全局修改,可通过自定义IOperationFilter修改目标接口的OpenAPI元数据,移除Swagger UI关联Accept头的标识:
首先创建自定义过滤器:
public class RemoveAcceptHeaderControlFilter : IOperationFilter { public void Apply(OpenApiOperation operation, OperationFilterContext context) { // 根据Action名称定位目标接口 if (context.ApiDescription.ActionDescriptor.DisplayName == "WebApplication1.Controllers.WeatherForecastController.Get") { // 移除控制Accept头的扩展标识 foreach (var response in operation.Responses.Values) { foreach (var content in response.Content.Values) { content.Extensions.Remove("x-ms-media-type"); } } } } }
然后在注册SwaggerGen时添加该过滤器:
builder.Services.AddSwaggerGen(c => { c.EnableAnnotations(); c.OperationFilter<RemoveAcceptHeaderControlFilter>(); });
此方式仅对指定接口生效,不影响其他接口的Swagger UI行为。
补充方案(硬编码响应头)
如果只是想确保404返回JSON,也可以在返回NotFound时直接指定响应头:
return NotFound(new ProblemDetails { Title = "Region not found" }) .WithHeaders(new Dictionary<string, string> { { "Content-Type", "application/json" } });
不过这种方式属于硬编码,不如前两种从Swagger层面解决彻底。
内容的提问来源于stack exchange,提问作者exploring.cheerily.impresses

