https://mp.weixin.qq.com/s/SmWyeSdwJqgaB8v06cMJYg
使用 AsyncEnumerable 更好的处理内容流
Intro
.NET 8 针对 HttpClient 新增了一个 GetFromJsonAsAsyncEnumerable 的扩展,我们可以更加方便地处理内容流了
Sample
ASP.NET Core API
首先我们来定义一个 API
[Route("api/[controller]")]
public class JobsController : ControllerBase
{
[HttpGet]
public async IAsyncEnumerable<Jobs> Jobs()
{
for (var i = 0; i < 10; i++)
{
await Task.Delay(500);
yield return new Jobs() { Id = i + 1, Title = $"job_{i} --- {DateTimeOffset.Now}" };
}
}
}
public sealed class Job
{
public required int Id { get; set; }
public required string Title { get; set; }
public override string ToString() => $"JobId: {Id}, JobTitle: {Title}";
}
我们在浏览器里请求这个 API 看下响应时间,可以看到类似下面的截图,浏览器在 635.23ms 的时候就开始收到了服务器端的响应,并在后面的五秒内收到后面的内容
图片
HttpClient
接着我们来使用新的 GetFromJsonAsAsyncEnumerable 来获取数据
using var httpClient = new HttpClient();
httpClient.BaseAddress = new Uri("http://localhost:5297");
using var response = await httpClient.GetAsync("api/Jobs");
ArgumentNullException.ThrowIfNull(response.Content);
var stream = response.Content.ReadFromJsonAsAsyncEnumerable<Job>();
// var stream = httpClient.GetFromJsonAsAsyncEnumerable<Job>("api/Jobs");
await foreach (var job in stream)
{
Console.WriteLine(job);
Console.WriteLine(DateTimeOffset.Now);
}
为了方便效果对比,我们打印 response 之后打印一下时间
图片
从输出可以看到,我们在 response 刚返回还没结束就已经开始处理了,服务器端返回一条数据,客户端就输出一条数据
微软还封装了一个 GetFromJsonAsAsyncEnumerable 方法来更加便捷的使用,参考前面示例中被注释掉的代码,但是目前实现感觉有点问题,别的 json 扩展使用的是基于 web 的一个 JsonSerializerOptions 这个使用的确实 JsonSerializerOptions.Default,导致这个方法会导致序列化的时候是大小写敏感的和别的扩展方法不一致,所以上面改了一个 workaround 的写法
在 Github 上提了一个 issue https://github.com/dotnet/runtime/issues/92179 感兴趣的朋友可以看一下
References
https://github.com/dotnet/runtime/pull/89258
https://github.com/dotnet/runtime/issues/87577
https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/proposals/csharp-8.0/async-streams
https://learn.microsoft.com/en-us/dotnet/api/system.text.json.jsonserializer.deserializeasyncenumerable
https://github.com/dotnet/runtime/issues/92179
https://github.com/dotnet/runtime/pull/92180