在ASP.NET Core中用HttpClient(四)——提升性能和優化內存


到目前爲止,咱們一直在使用字符串建立請求體,並讀取響應的內容。可是咱們能夠經過使用流提升性能和優化內存。所以,在本文中,咱們將學習如何在請求和響應中使用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源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。

相關文章
相關標籤/搜索