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

ASP.NET Core API如何以流分块传输替代大型JSON数组返回

ASP.NET Core 实现大列表分块流式返回方案

你现有代码的核心性能问题是ToListAsync()会一次性将全量数据加载到服务端内存,完成全部序列化后才会向客户端发送响应,首包延迟高、内存占用大。ASP.NET Core 没有PushStreamContent不代表做不了流传输,下面两个方案都可以实现边查边发、分块传输的效果,首包延迟可以降到百毫秒以内。


方案一:普通Web API 流式JSON返回(改造成本最低,兼容现有HTTP客户端)

这个方案不需要改接口协议,返回的还是标准JSON格式,普通HTTP客户端无需特殊改造即可调用,支持流式读取的客户端可以做到边接收边处理。

实现要点

  • 必须关闭响应缓冲,否则ASP.NET Core会默认缓存全量响应后再发送
  • 用EF Core的AsAsyncEnumerable()实现数据库流式读取,不要用ToListAsync()一次性加载全量数据
  • 可以灵活控制每批发送的数据块大小,比如你要的100条/块

最简实现(自动分块序列化)

[HttpGet]
[ResponseCache(NoStore = true)]
public async IAsyncEnumerable<Price> GetPrices()
{
    // 关闭响应缓冲,数据生成一块就发送一块
    Response.Buffer = false;
    // 流式读取数据库,不会一次性加载全量数据到内存
    await foreach (var price in _context.Prices
        .AsAsyncEnumerable()
        .WithCancellation(HttpContext.RequestAborted))
    {
        yield return price;
    }
}

严格控制100条/块的手动实现

如果需要明确按100条为单位分块发送,可以直接操作响应流手动控制序列化节奏:

[HttpGet]
public async Task GetPrices()
{
    Response.Buffer = false;
    Response.ContentType = "application/json";
    Response.Headers["Transfer-Encoding"] = "chunked";

    await using var jsonWriter = new Utf8JsonWriter(Response.Body, new JsonWriterOptions { SkipValidation = true });
    jsonWriter.WriteStartArray();

    var batchBuffer = new List<Price>(100);
    await foreach (var price in _context.Prices
        .AsAsyncEnumerable()
        .WithCancellation(HttpContext.RequestAborted))
    {
        batchBuffer.Add(price);
        if (batchBuffer.Count == 100)
        {
            // 凑够100条就序列化刷入响应流,发送给客户端
            foreach (var item in batchBuffer)
            {
                JsonSerializer.Serialize(jsonWriter, item);
            }
            await jsonWriter.FlushAsync(HttpContext.RequestAborted);
            batchBuffer.Clear();
        }
    }

    // 发送最后不足100条的剩余数据
    foreach (var item in batchBuffer)
    {
        JsonSerializer.Serialize(jsonWriter, item);
    }
    jsonWriter.WriteEndArray();
    await jsonWriter.FlushAsync(HttpContext.RequestAborted);
}

方案二:gRPC 服务端流(性能最优,适合内部服务场景)

如果客户端和服务端都是内部可控系统,gRPC服务端流是更优选择,原生支持分块消息传输,用Protobuf二进制序列化,比JSON体积小、速度快3~5倍,更适合数据量持续增长的场景。

实现步骤

  1. 先定义proto协议文件,声明服务端流接口:
syntax = "proto3";
option csharp_namespace = "PriceGrpcService";
import "google/protobuf/timestamp.proto";

service PriceGrpc {
  // 服务端流返回,关键字stream标记
  rpc GetAllPrices (GetPricesRequest) returns (stream PriceMessage);
}

message GetPricesRequest {}

message PriceMessage {
  int32 id = 1;
  string product_code = 2;
  double price = 3;
  google.protobuf.Timestamp update_at = 4;
}
  1. 实现服务端逻辑:
public class PriceGrpcImpl : PriceGrpc.PriceGrpcBase
{
    private readonly AppDbContext _dbContext;
    public PriceGrpcImpl(AppDbContext dbContext)
    {
        _dbContext = dbContext;
    }

    public override async Task GetAllPrices(GetPricesRequest request, IServerStreamWriter<PriceMessage> responseStream, ServerCallContext context)
    {
        await foreach (var price in _dbContext.Prices
            .AsAsyncEnumerable()
            .WithCancellation(context.CancellationToken))
        {
            // 数据库实体转Protobuf消息
            var msg = new PriceMessage
            {
                Id = price.Id,
                ProductCode = price.ProductCode,
                Price = (double)price.Price,
                UpdateAt = Timestamp.FromDateTime(price.UpdateTime.ToUniversalTime())
            };
            // 写入流,客户端实时收到
            await responseStream.WriteAsync(msg);
        }
    }
}
  1. 客户端可以直接遍历流边收边处理,不需要等全量响应:
using var call = grpcClient.GetAllPrices(new GetPricesRequest());
await foreach (var priceMsg in call.ResponseStream.ReadAllAsync(cancellationToken: cts.Token))
{
    // 收到一条处理一条
    Console.WriteLine($"商品{priceMsg.ProductCode} 当前价格:{priceMsg.Price}");
}

避坑说明

  • 不要忘记关闭响应缓冲,否则所有流操作都会被ASP.NET Core缓存,达不到分块发送的效果
  • 数据库查询必须用AsAsyncEnumerable(),不要用ToList()/ToListAsync(),否则还是会全量加载数据到内存
  • 一定要传入取消令牌,客户端断开连接时可以及时终止数据库查询,避免无效资源占用
  • 手动写JSON流时必须补全数组结束符,否则客户端会收到格式非法的JSON

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.15 16:16:00