到目前为止,我们一直在使用字符串创建请求体,并读取响应的内容。但是我们可以通过使用流提高性能和优化内存。因此,在本文中,我们将学习如何在请求和响应中使用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. React Security Best Practices All In One

    React Security Best Practices All In One Default XSS Protection with Data Binding Dangerous URLs Ren ...

  2. PyCharm 中文 字符 python 报错 的 完美 解决方案!

    PyCharm 中文 字符 python 报错 的 完美 解决方案! #_*_ coding:utf-8_*_ https://www.python.org/dev/peps/pep-0263/ 到p ...

  3. Webpack 4.x 默认支持 ES6 语法

    Webpack 4.x 默认支持 ES6 语法 Q: 为什么 webpack4 默认支持 ES6 语法的压缩? A: terser 里面实现了 ES6 语法的 AST解析. webpack 4 里使用 ...

  4. no code form generator

    no code form generator 无代码,表单生成器 H5 Drag & Drop UI => codes click copy demo https://www.forms ...

  5. js 脏检测

    参考 基础知识 <!DOCTYPE html> <html lang="en"> <head> <meta charset="U ...

  6. 数据序列化工具——flatbuffer

    flatbuffer是一款类似于protobuf的数据序列化工具.所有数据序列化,简单来说,就是将某程数据结构按照一定的格式进行编码与解码,以方便在不同的进程间传递后,能够正确的还原成之前的数据结构. ...

  7. Java并发包源码学习系列:同步组件CyclicBarrier源码解析

    目录 CyclicBarrier概述 案例学习 类图结构及重要字段 内部类Generation及相关方法 void reset() void breakBarrier() void nextGener ...

  8. JDK源码阅读-ByteBuffer

    本文转载自JDK源码阅读-ByteBuffer 导语 Buffer是Java NIO中对于缓冲区的封装.在Java BIO中,所有的读写API,都是直接使用byte数组作为缓冲区的,简单直接.但是在J ...

  9. Elasticsearch---DSL搜索实践

    Domain Specific Language 特定领域语言,基于JSON格式的数据查询,查询更灵活,有利于复杂查询 一.普通url路径参数搜索 数据准备 1.建立名字为 shop 的索引 2.手动 ...

  10. 4. Vue基本指令

    目录 1. v-on指令 2. v-if指令 3. v-show指令 4. v-for指令 5. v-model指令 一. v-on指令 1. 基础用法 v-on是事件监听的指令, 下面来看简单用法 ...