到目前为止,咱们一直在使用字符串建立请求体,并读取响应的内容。可是咱们能够经过使用流提升性能和优化内存。所以,在本文中,咱们将学习如何在请求和响应中使用HttpClient流。json
什么是流api
流是以文件、输入/输出设备或网络流量的形式表示一个字节序列的抽象。C#中的Stream类是一个抽象类,它能够从源文件读取或写入字节。这使咱们能够跳过可能增长内存使用量或下降性能的中间变量。服务器
这里须要知道的重要一点是,在客户端处理流与API级别无关。这是一个彻底独立的过程。微信
咱们的API可能适用于流,也可能不适用,但这不会影响客户端。这无疑是一个优点,由于咱们能够在客户端应用程序中使用流来提升性能和减小内存使用,同时仍然使用API。网络
使用HttpClient流来获取数据app
在本系列的第一篇文章中,咱们已经了解了在从API获取数据时,咱们必须:async
-
向API的URI发送请求 -
等待响应到达 -
使用ReadAsStringAsync方法从响应体中读取内容 -
并使用System.Text.Json反序列化内容
如前所述,对于流,咱们能够删除中间的操做,即便用ReadAsStringAsync方法从响应体读取字符串内容。咱们来看看怎么作。性能
首先,咱们要在客户端应用程序中建立一个新的HttpClientStreamService:学习
public class HttpClientStreamService : IHttpClientServiceImplementation{ private static readonly HttpClient _httpClient = new HttpClient(); private readonly JsonSerializerOptions _options;
public HttpClientStreamService() { _httpClient.BaseAddress = new Uri("https://localhost:5001/api/"); _httpClient.Timeout = new TimeSpan(0, 0, 30); _httpClient.DefaultRequestHeaders.Clear();
_options = new JsonSerializerOptions { PropertyNameCaseInsensitive = true }; }
public async Task Execute() { throw new NotImplementedException(); }}
这是咱们在本系列中已经见过几回的标准配置。接下来,咱们能够建立一个使用流发送GET请求的方法:优化
private async Task GetCompaniesWithStream(){ using (var response = await _httpClient.GetAsync("companies")) { response.EnsureSuccessStatusCode();
var stream = await response.Content.ReadAsStreamAsync();
var companies = await JsonSerializer.DeserializeAsync<List<CompanyDto>>(stream, _options); }}
在这个方法中,咱们使用GetAsync方法从API中获取数据。但正如咱们在本系列的第一篇文章中解释的那样,你能够使用HttpRequestMessage类来对请求进行更高级别的控制。另外,注意此次咱们将响应包装在using指令中,由于咱们如今使用的是流。
在确保接收状态码成功以后,咱们使用ReadAsStreamAsync方法序列化HTTP内容并将其做为流返回。有了这些,咱们就再也不须要字符串序列化和建立字符串变量了。
一旦咱们有了流,咱们就调用JsonSerializer.DeserializeAsync 方法从流中读取并将结果反序列化到company对象列表中。
在启动咱们的应用程序以前,必须在Execute方法中调用这个方法:
public async Task Execute(){ await GetCompaniesWithStream();}
同时,在Program类中注册这个新服务:
private static void ConfigureServices(IServiceCollection services){ //services.AddScoped<IHttpClientServiceImplementation, HttpClientCrudService>(); //services.AddScoped<IHttpClientServiceImplementation, HttpClientPatchService>(); services.AddScoped<IHttpClientServiceImplementation, HttpClientStreamService>();}
就是这样。咱们能够同时启动两个应用程序并检查结果:
能够看到,咱们从流中读取告终果。
额外改进
在前面的示例中,当咱们从响应中读取内容时,咱们删除了一个字符串建立操做。
所以,咱们取得了进步。可是,咱们能够经过使用HttpCompletionMode来进一步改进这个解决方案。它是一个有两个值的枚举,控制HttpClient的操做在什么点上被认为已完成。
默认值是HttpCompletionMode.ResponseContentRead。这意味着只有当整个响应和内容一块儿读取时,HTTP操做才完成。
第二个值是HttpCompletionMode.ResponseHeadersRead。当咱们在HTTP请求中选择此选项时,咱们声明当响应头被彻底读取时操做就完成了。此时,响应体根本没必要被彻底处理。这显然意味着咱们将使用更少的内存,由于咱们没必要将整个内容保存在内存中。此外,这也会影响性能,由于咱们能够更快地处理数据。
为了实现这一改进,咱们须要作的就是修改GetCompaniesWithStream方法中的GetAsync方法:
private async Task GetCompaniesWithStream(){ using (var response = await _httpClient.GetAsync("companies", HttpCompletionOption.ResponseHeadersRead)) { response.EnsureSuccessStatusCode();
var stream = await response.Content.ReadAsStreamAsync();
var companies = await JsonSerializer.DeserializeAsync<List<CompanyDto>>(stream, _options); }}
若是运行咱们的应用程序,将看到与前面示例相同的结果。但这一次,作了更多的改进。
如今,让咱们看看如何在POST请求中使用流。
使用HttpClient的流发送POST请求
在本系列的第二篇文章中,学习了如何使用HttpClient发送POST请求。在这个示例中,在发送请求以前将负载序列化为JSON字符串。固然,对于流,咱们能够跳过这一部分。
首先,让咱们建立一个新方法:
private async Task CreateCompanyWithStream(){ var companyForCreation = new CompanyForCreationDto { Name = "Eagle IT Ltd.", Country = "USA", Address = "Eagle IT Street 289" };
var ms = new MemoryStream(); await JsonSerializer.SerializeAsync(ms, companyForCreation); ms.Seek(0, SeekOrigin.Begin);
var request = new HttpRequestMessage(HttpMethod.Post, "companies"); request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
using (var requestContent = new StreamContent(ms)) { request.Content = requestContent; requestContent.Headers.ContentType = new MediaTypeHeaderValue("application/json");
using (var response = await _httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead)) { response.EnsureSuccessStatusCode();
var content = await response.Content.ReadAsStreamAsync(); var createdCompany = await JsonSerializer.DeserializeAsync<CompanyDto>(content, _options); } } }
在这个方法中,咱们首先建立一个具备全部必需属性companyForCreation对象。而后,咱们须要一个内存流对象。调用JsonSerializer.SerializeAsync时,咱们将companyForCreation对象序列化到建立的内存流中。一样,使用Seek方法在流的开头设置一个位置。而后,用所需的参数初始化HttpReqestMessage对象的新实例,并将accept头设置为application/json。
在此以后,咱们使用前面的内存流建立一个名为requestContent的新流。StreamContent对象将是请求的内容,所以,咱们在代码中声明这一点,并设置请求的ContentType。
最后,咱们使用SendAsync方法发送请求,确保响应是成功的,并将内容做为流读取。读取内容后,咱们将其反序列化到createdCompany对象中。
因此,正如你所看到的,经过整个方法,咱们使用流,避免了使用大字符串时没必要要的内存使用。
咱们如今要作的就是在Execute方法中调用这个方法:
public async Task Execute(){ //await GetCompaniesWithStream(); await CreateCompanyWithStream();}
结论
在HTTP请求中使用流能够帮助咱们减小内存消耗并优化咱们的应用程序的性能。在这篇文章中,咱们看到了如何使用流从服务器获取数据,并在发送POST请求时为咱们的请求体建立一个StreamContent。
本文分享自微信公众号 - 码农译站(gh_c0d62fbb4bda)。
若有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一块儿分享。