自定义NewtonsoftJsonOutputFormatter仅Swagger可用,浏览器/Axios失效求助
问题描述
自定义了继承自NewtonsoftJsonOutputFormatter的WrappedJsonOutputFormatter,代码如下:
public class WrappedJsonOutputFormatter : NewtonsoftJsonOutputFormatter { public ApiVersion Version { get; set; } public WrappedJsonOutputFormatter(JsonSerializerSettings serializerSettings, ArrayPool<char> charPool, MvcOptions options, MvcNewtonsoftJsonOptions? jsonOptions) : base(serializerSettings, charPool, options, jsonOptions) { } public override Task WriteResponseBodyAsync(OutputFormatterWriteContext context, Encoding selectedEncoding) { if (context.HttpContext.Response.StatusCode == (int)HttpStatusCode.OK) { var @object = new ApiResponse { Data = context.Object, ApiVersion = context.HttpContext?.GetRequestedApiVersion()?.ToString() ?? "1.0", StatusCode = context.HttpContext.Response.StatusCode }; var newContext = new OutputFormatterWriteContext(context.HttpContext, context.WriterFactory, typeof(ApiResponse), @object) { ContentType = context.ContentType, ContentTypeIsServerDefined = context.ContentTypeIsServerDefined }; TimeSpan responseTime = DateTime.Now - (DateTime)context.HttpContext.Items["RequestTime"]; @object.ResponseTime = responseTime.TotalMilliseconds; newContext.HttpContext.Response.Headers.Add(ResponseTimeMiddleware.RESPONSE_HEADER_RESPONSE_TIME, responseTime.TotalMilliseconds.ToString("0.00")); return base.WriteResponseBodyAsync(newContext, selectedEncoding); } return base.WriteResponseBodyAsync(context, selectedEncoding); } }
在Program.cs中的配置如下:
builder.Services.AddControllers(options => { jsonSettings.Converters.Add(new StringEnumConverter()); var formatter = options.InputFormatters.OfType<SystemTextJsonInputFormatter>().Single(); formatter.SerializerOptions.Converters.Add(new JsonStringEnumConverter()); formatter.SerializerOptions.ReadCommentHandling = System.Text.Json.JsonCommentHandling.Skip; formatter.SerializerOptions.AllowTrailingCommas = true; options.OutputFormatters.RemoveType<SystemTextJsonOutputFormatter>(); options.OutputFormatters.Add(new WrappedJsonOutputFormatter(jsonSettings, ArrayPool<char>.Shared, options, null)); });
遇到的问题:通过Swagger调用API时,自定义格式化器可正常工作,但用Axios或直接浏览器访问时,格式化器失效。请问差异原因是什么?该如何解决?
原因分析
核心问题是请求的Accept头差异:
- Swagger发起请求时,默认在
Accept头中指定application/json,该类型正好匹配WrappedJsonOutputFormatter(继承自NewtonsoftJsonOutputFormatter,默认支持application/json和text/json),因此MVC会选中自定义格式化器处理响应。 - 浏览器或Axios请求时,
Accept头包含更多类型:比如浏览器通常发送text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8,Axios默认发送application/json, text/plain, */*。此时MVC会优先选择匹配度最高的输出格式化器,若自定义格式化器未声明支持text/html或*/*这类类型,MVC就会选择其他格式化器(比如内置Html格式化器),导致自定义逻辑不生效。
解决方法
方法1:扩展自定义格式化器支持的媒体类型
在WrappedJsonOutputFormatter构造函数中添加更多支持的媒体类型,覆盖常见请求场景:
public WrappedJsonOutputFormatter(JsonSerializerSettings serializerSettings, ArrayPool<char> charPool, MvcOptions options, MvcNewtonsoftJsonOptions? jsonOptions) : base(serializerSettings, charPool, options, jsonOptions) { // 添加对text/html和*/*的支持,适配浏览器/Axios的Accept头 SupportedMediaTypes.Add(MediaTypeHeaderValue.Parse("text/html")); SupportedMediaTypes.Add(MediaTypeHeaderValue.Parse("*/*")); }
方法2:调整格式化器优先级,确保优先被选中
将自定义格式化器插入到输出格式化器列表的最前面,让MVC优先选择它:
// 替换原Add方法,改为插入到第一个位置 options.OutputFormatters.Insert(0, new WrappedJsonOutputFormatter(jsonSettings, ArrayPool<char>.Shared, options, null));
方法3:全局强制API返回JSON格式
在AddControllers配置中添加全局Produces特性,强制所有API返回application/json,忽略请求的Accept头:
builder.Services.AddControllers(options => { // 全局设置Produces特性,强制返回application/json options.Filters.Add(new ProducesAttribute("application/json")); // 其他原有配置... });
可根据实际场景选择一种或多种方案组合,比如同时扩展媒体类型并调整优先级,能覆盖更多边缘情况。
内容的提问来源于stack exchange,提问作者Mustafa Ulukaya
相关产品推荐
相关产品推荐

