在这篇文章,我将介绍一个名为 System.Net.Http.Json 的扩展库,它最近添加到了 .NET 中,我们看一下这个库能够给我们解决什么问题,今天会介绍下如何在代码中使用。
在此之前我们是如何处理JSON是一种普遍和流行的串行化格式数据来发送现代web api,我经常在我的项目中使用HttpClient 调用外部资源, 当 content type 是 “application/json”, 我拿到Json的响应内容后,我需要手动处理响应,通常会验证响应状态代码是否为200,检查内容是不是为空,然后再试图从响应内容流反序列化
如果我们使用 Newtonsoft.Json, 代码可能是像下边这样
private static async Task<User> StreamWithNewtonsoftJson(string uri, HttpClient httpClient) { using var httpResponse = await httpClient.GetAsync(uri, HttpCompletionOption.ResponseHeadersRead); httpResponse.EnsureSuccessStatusCode(); // throws if not 200-299 if (httpResponse.Content is object && httpResponse.Content.Headers.ContentType.MediaType == "application/json") { var contentStream = await httpResponse.Content.ReadAsStreamAsync(); using var streamReader = new StreamReader(contentStream); using var jsonReader = new JsonTextReader(streamReader); JsonSerializer serializer = new JsonSerializer(); try { return serializer.Deserialize<User>(jsonReader); } catch(JsonReaderException) { Console.WriteLine("Invalid JSON."); } } else { Console.WriteLine("HTTP Response was invalid and cannot be deserialised."); } return null; }虽然上面没有大量的代码, 但是我们从外部服务接收JSON数据需要都编写这些,在微服务环境中,这可能是在很多地方,不同的服务。
大家可能通常也会把 Json 序列化成 String,在 HttpClient 的 HttpContent 中调用GetStringAsync ReadAsStringAsync,可以直接使用 Newtonsoft.Json 和 System.Text.Json,现在的一个问题是我们需要多分配一个包含整个Json 数据的 String,这样会存在浪费,因为我们看上面的代码已经有一个可用的响应流,可以直接反序列化到实体,通过使用流,也可以进一步提高性能,在我的另一篇文章里, 可以利用HttpCompletionOption来改善HttpClient性能。
如果您在过去在项目中使用过 HttpClient 来处理返回的Json数据,那么您可能已经使用了Microsoft.AspNet.WebApi.Client。我在过去使用过它,因为它提供了有用的扩展方法来支持从HttpResponseMessage上的内容流进行高效的JSON反序列化,这个库依赖于Newtonsoft.Json文件并使用其基于流的API来支持数据的高效反序列化,这是一个方便的库,我用了几年了
如果我们在项目中使用这个库,上面的代码可以减少一些
private static async Task<User> WebApiClient(string uri, HttpClient httpClient) { using var httpResponse = await httpClient.GetAsync(uri, HttpCompletionOption.ResponseHeadersRead); httpResponse.EnsureSuccessStatusCode(); // throws if not 200-299 try { return await httpResponse.Content.ReadAsAsync<User>(); } catch // Could be ArgumentNullException or UnsupportedMediaTypeException { Console.WriteLine("HTTP Response was invalid or could not be deserialised."); } return null; }最近.NET 团队引入了一个内置的JSON库 System.Text.Json,这个库是使用了最新的 .NET 的性能特性, 比如 Span, 低开销, 能够快速序列化和反序列化, 并且在.NET Core 3.0 集成到了 BCL(基础库), 所以你不需要引用一个额外的包在项目中
今天,我更倾向于使用 System.Text.Json,主要是在流处理,代码跟上面 Newtonsofe.Json 相比更简洁
private static async Task<User> StreamWithSystemTextJson(string uri, HttpClient httpClient) { using var httpResponse = await httpClient.GetAsync(uri, HttpCompletionOption.ResponseHeadersRead); httpResponse.EnsureSuccessStatusCode(); // throws if not 200-299 if (httpResponse.Content is object && httpResponse.Content.Headers.ContentType.MediaType == "application/json") { var contentStream = await httpResponse.Content.ReadAsStreamAsync(); try { return await System.Text.Json.JsonSerializer.DeserializeAsync<User>(contentStream, new System.Text.Json.JsonSerializerOptions { IgnoreNullValues = true, PropertyNameCaseInsensitive = true }); } catch (JsonException) // Invalid JSON { Console.WriteLine("Invalid JSON."); } } else { Console.WriteLine("HTTP Response was invalid and cannot be deserialised."); } return null; }因为我在项目中减少了第三方库的依赖,并且有更好的性能,我更喜欢用 System.Text.Json,虽然这块代码非常简单,但是还有更好的方案,从简洁代码的角度来看,到现在为止最好的选择是使用 Microsoft.AspNet.WebApi.Client 提供的扩展方法。
System.Net.Http.Json 介绍我从今年2月份一直在关注这个库,以及首次在 github 显示的设计文档和问题,这些需求和建议的API都可以在设计文档中找到。
客户端从网络上对 JSon 内容序列化和反序列化是非常常见的操作,特别是即将到来的Blazor环境,现在,发送数据到服务端,需要写多行繁琐的代码,对使用者来说非常不方便,我们想对 HttpClient 扩展,允许做这些操作就像调用单个方法一样简单