到目前为止,我们一直在使用字符串创建请求体,并读取响应的内容。但是我们可以通过使用流提高性能和优化内存。因此,在本文中,我们将学习如何在请求和响应中使用HttpClient流。

什么是流

流是以文件、输入/输出设备或网络流量的形式表示一个字节序列的抽象。C#中的Stream类是一个抽象类,它可以从源文件读取或写入字节。这使我们可以跳过可能增加内存使用量或降低性能的中间变量。

这里需要知道的重要一点是,在客户端处理流与API级别无关。这是一个完全独立的过程。

我们的API可能适用于流,也可能不适用,但这不会影响客户端。这无疑是一个优势,因为我们可以在客户端应用程序中使用流来提高性能和减少内存使用,同时仍然使用API。

使用HttpClient流来获取数据

在本系列的第一篇文章中,我们已经了解了在从API获取数据时,我们必须:

  • 向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。

原文链接:https://code-maze.com/using-streams-with-httpclient-to-improve-performance-and-memory-usage/

在ASP.NET Core中用HttpClient(四)——提高性能和优化内存的更多相关文章

  1. 在ASP.NET Core中用HttpClient(一)——获取数据和内容

    在本文中,我们将学习如何在ASP.NET Core中集成和使用HttpClient.在学习不同HttpClient功能的同时使用Web API的资源.如何从Web API获取数据,以及如何直接使用Ht ...

  2. 在ASP.NET Core中用HttpClient(二)——发送POST, PUT和DELETE请求

    在上一篇文章中,我们已经学习了如何在ASP.NET Core中使用HttpClient从Web API获取数据.此外,我们还学习了如何使用GetAsync方法和HttpRequestMessage类发 ...

  3. 在ASP.NET Core中用HttpClient(三)——发送HTTP PATCH请求

    在前面的两篇文章中,我们讨论了很多关于使用HttpClient进行CRUD操作的基础知识.如果你已经读过它们,你就知道如何使用HttpClient从API中获取数据,并使用HttpClient发送PO ...

  4. 在ASP.NET Core中用HttpClient(五)——通过CancellationToken取消HTTP请求

    ​用户向服务器发送HTTP请求应用程序页面是一种非常可能的情况.当我们的应用程序处理请求时,用户可以从该页面离开.在这种情况下,我们希望取消HTTP请求,因为响应对该用户不再重要.当然,这只是实际应用 ...

  5. 在ASP.NET Core中用HttpClient(六)——ASP.NET Core中使用HttpClientFactory

    ​到目前为止,我们一直直接使用HttpClient.在每个服务中,我们都创建了一个HttpClient实例和所有必需的配置.这会导致了重复代码.在这篇文章中,我们将学习如何通过使用HttpClient ...

  6. C# ASP.NET Core使用HttpClient的同步和异步请求

    引用 Newtonsoft.Json // Post请求 public string PostResponse(string url,string postData,out string status ...

  7. ASP.NET Core之跨平台的实时性能监控(2.健康检查)

    前言 上篇我们讲了如何使用App Metrics 做一个简单的APM监控,最后提到过健康检查这个东西. 这篇主要就是讲解健康检查的内容. 没看过上篇的,请移步:ASP.NET Core之跨平台的实时性 ...

  8. ASP.NET Core 之跨平台的实时性能监控

    前言 前面我们聊了一下一个应用程序 应该监控的8个关键位置. . 嗯..地址如下: 应用程序的8个关键性能指标以及测量方法 最后卖了个小关子,是关于如何监控ASP.NET Core的. 今天我们就来讲 ...

  9. Asp.Net Core 进阶(四)—— 过滤器 Filters

    一.介绍 Asp.Net Core Filter 使得可以在请求处理管道的特定阶段的前后执行代码,我们可以创建自定义的 filter 用于处理横切关注点. 横切关注点的示例包括错误处理.缓存.配置.授 ...

随机推荐

  1. 使用 js 实现一个简易版的 vue 框架

    使用 js 实现一个简易版的 vue 框架 具有挑战性的前端面试题 refs https://www.infoq.cn/article/0NUjpxGrqRX6Ss01BLLE xgqfrms 201 ...

  2. js & anti craw & crawler spam

    js & anti craw & crawler spam demo & X-Sign , function(t, e, n) { "use strict" ...

  3. Base 64 & URL & blob & FileReader & createObjectURL

    Base 64 & URL & blob & FileReader & createObjectURL /** * let blob = item.getAsFile( ...

  4. taro alipay

    taro alipay 开发指南 https://nervjs.github.io/taro/docs/GETTING-STARTED.html#支付宝小程序 { "name": ...

  5. redux & dispatch & payload

    redux & dispatch & payload react & react-redux & redux-saga // contrast dispatch({ t ...

  6. uniapp 修改meta:viewport

    onLoad(options) { this.setViewport(`width=device-width, initial-scale=1.0`); }, onUnload() { this.se ...

  7. 实用Macbook软件系列

    Macbook Software 实用Macbook软件系列 我的Mac都装了哪些软件 鉴于很多小伙伴刚刚由win系统转换到mac,一开始会有很多不适应的地方,所以本期文章准备给大家介绍下mac上一些 ...

  8. Python学习笔记_生成验证码

    import random def verification_code(): num = [str(x) for x in range(10)] # 列表生成器0-9 upper = [chr(x) ...

  9. Spark集成

    一.Spark 架构与优化器 1.Spark架构 (重点) 2.Spark优化器 二.Spark+SQL的API (重点) 1.DataSet简介 2.DataFrame简介 3.RDD与DF/DS的 ...

  10. springboot对数据库密码加密

    第一步:maven引jar包 <dependency> <groupId>com.github.ulisesbocchio</groupId> <artifa ...