You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

自定义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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.17 09:58:08