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

ASP.NET中Newtonsoft致IAsyncEnumerable异步失效的解决方案咨询

解决方案:针对IAsyncEnumerable或部分控制器切换JSON序列化器

问题原因

全局配置AddNewtonsoftJson()后,Newtonsoft.Json不支持IAsyncEnumerable的异步流式序列化,会先枚举完所有数据再一次性返回,导致接口需要等待所有Task.Delay完成后才响应。而System.Text.Json原生支持IAsyncEnumerable的流式输出,能逐条返回数据。


方案一:按控制器/方法指定序列化器

通过自定义条件格式化器,让带特定标记的控制器用Newtonsoft.Json,其他控制器默认用System.Text.Json。

步骤1:定义标记特性

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class UseNewtonsoftJsonAttribute : Attribute
{
}

步骤2:配置服务,设置条件筛选

builder.Services.AddControllers(options =>
{
    // 将System.Text.Json设为默认格式化器(放在最前面)
    var systemTextFormatter = options.OutputFormatters.OfType<SystemTextJsonOutputFormatter>().First();
    options.OutputFormatters.Remove(systemTextFormatter);
    options.OutputFormatters.Insert(0, systemTextFormatter);

    // 取出Newtonsoft格式化器,设置仅在带有UseNewtonsoftJsonAttribute的控制器/方法中使用
    var newtonsoftFormatter = options.OutputFormatters.OfType<NewtonsoftJsonOutputFormatter>().First();
    options.OutputFormatters.Remove(newtonsoftFormatter);
    options.OutputFormatters.Add(new ConditionalOutputFormatter(newtonsoftFormatter, context =>
    {
        return context.ActionDescriptor.EndpointMetadata.Any(em => em is UseNewtonsoftJsonAttribute);
    }));
})
.AddNewtonsoftJson() // 保留Newtonsoft的注册
.AddJsonOptions(opt =>
{
    // 按需配置System.Text.Json的序列化规则
    opt.JsonSerializerOptions.PropertyNamingPolicy = null; // 保留原始属性名
});

步骤3:在需要Newtonsoft的控制器上标记

// 这个控制器的所有接口都用Newtonsoft.Json
[UseNewtonsoftJson]
public class LegacyController : ControllerBase
{
    // ... 你的接口代码
}

// 你的IAsyncTest所在的控制器不用加标记,自动用System.Text.Json,支持流式输出
public class TestController : ControllerBase
{
    [HttpGet("IAsyncTest")]
    public IAsyncEnumerable<TestDTO> GetTestValue()
    {
        return _getValues();
    }

    // ... 其他代码
}

方案二:自定义格式化器专门处理IAsyncEnumerable

如果不想拆分控制器,只想让IAsyncEnumerable类型用System.Text.Json流式输出,其他类型继续用Newtonsoft.Json,可以写一个自定义输出格式化器。

步骤1:实现自定义格式化器

public class AsyncEnumerableSystemTextJsonFormatter : OutputFormatter
{
    private readonly JsonSerializerOptions _jsonOptions;

    public AsyncEnumerableSystemTextJsonFormatter(JsonSerializerOptions jsonOptions)
    {
        _jsonOptions = jsonOptions;
        SupportedMediaTypes.Add(MediaTypeHeaderValue.Parse("application/json"));
        SupportedEncodings.Add(Encoding.UTF8);
        SupportedEncodings.Add(Encoding.Unicode);
    }

    protected override bool CanWriteType(Type type)
    {
        // 仅处理IAsyncEnumerable<T>类型
        return type.IsGenericType && type.GetGenericTypeDefinition() == typeof(IAsyncEnumerable<>);
    }

    public override async Task WriteResponseBodyAsync(OutputFormatterWriteContext context)
    {
        var response = context.HttpContext.Response;
        var asyncEnumerable = context.Object as IAsyncEnumerable<object>;
        if (asyncEnumerable == null) return;

        response.ContentType = "application/json";
        await using var jsonWriter = new Utf8JsonWriter(response.Body, new JsonWriterOptions { Indented = _jsonOptions.WriteIndented });

        jsonWriter.WriteStartArray();
        // 异步枚举并逐条序列化
        await foreach (var item in asyncEnumerable)
        {
            JsonSerializer.Serialize(jsonWriter, item, item.GetType(), _jsonOptions);
        }
        jsonWriter.WriteEndArray();
        await jsonWriter.FlushAsync();
    }
}

步骤2:注册自定义格式化器

builder.Services.AddControllers()
.AddNewtonsoftJson() // 保留全局Newtonsoft配置
.AddMvcOptions(options =>
{
    // 获取System.Text.Json的默认配置
    var jsonOptions = builder.Services.BuildServiceProvider()
        .GetRequiredService<IOptions<JsonOptions>>().Value.JsonSerializerOptions;
    
    // 将自定义格式化器放在最前面,优先处理IAsyncEnumerable类型
    options.OutputFormatters.Insert(0, new AsyncEnumerableSystemTextJsonFormatter(jsonOptions));
});

这样,所有返回IAsyncEnumerable的接口都会自动用System.Text.Json流式输出,其他类型的返回结果依然使用Newtonsoft.Json序列化。


内容的提问来源于stack exchange,提问作者oussama benyaala

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.08 23:40:38