在ASP.NET Core中用HttpClient(一)——获取数据和内容
在本文中,我们将学习如何在ASP.NET Core中集成和使用HttpClient。在学习不同HttpClient功能的同时使用Web API的资源。如何从Web API获取数据,以及如何直接使用HttpRequestMessage类来实现这些功能。在以后的文章中,我们将学习如何发送POST、PUT和DELETE请求,以及如何使用HttpClient发送PATCH请求。
要下载源代码,可以访问https://github.com/CodeMazeBlog/httpclient-aspnetcore/tree/fetching-data-with-httpclient以获取项目。
概述
如果你打开HttpClient存储库的主分支,您将发现两个项目:CompanyEmployees和CompanyEmployees. client。第一个项目是ASP.NET Core Web API项目,它将是本教程的服务器端项目。它由几个项目组成:

API项目对于我们的文章和整个系列来说也不是最重要的。重要的是它使用SQL数据库,所以你需要做的就是修改appsettings中的连接字符串。然后运行Update-Migration命令。所有需要的数据都将被迁移到数据库中。
然后,还有客户端应用程序——ASP.NET Core控制台应用程序。它有一个单独的服务HttpClientCrudService,我们将在本文中修改它,一个单独的接口IHttpClientServiceImplementation,所有HttpClient服务都将从它继承,数据传输类和一个修改过的Program类:

让我们来看看Program类的当前代码
class Program
{
static async Task Main(string[] args)
{
var services = new ServiceCollection();
ConfigureServices(services);
var provider = services.BuildServiceProvider();
try
{
await provider.GetRequiredService<IHttpClientServiceImplementation>()
.Execute();
}
catch (Exception ex)
{
Console.WriteLine($"Something went wrong: {ex}");
}
}
private static void ConfigureServices(IServiceCollection services)
{
services.AddScoped<IHttpClientServiceImplementation, HttpClientCrudService>();
}
}
这没有什么特别的。我们准备服务集合,将服务添加到IOC中,并从服务中执行默认方法。当我们在整个教程中添加不同的服务时,我们将扩展ConfigureServices方法。
关于HttpClient
我们不会深入研究关于HttpClient的理论,因为我们将从示例中学到很多东西,但是让我们看看一些基础知识。
HttpClient是一个类,它允许我们发送HTTP请求,并从指定URI接收HTTP响应。我们可以使用这个类发送各种HTTP请求,如GET、POST、PUT、DELETE、PATCH并来自服务器的响应。
HttpClient使用HTTP消息处理程序发送请求并获取响应。这是默认消息处理程序的主要工作。如果我们阅读微软的文档,我们会读到.NET Framework和.NET Core 2.0以及更早版本的默认是HttpClientHander。但是在.NET Core 2.1和更高版本中,默认的是SocketsHttpHandler。
但是,HttpClient不只是使用一个消息处理程序。我们可以附加多个消息处理程序并创建管道。有些处理程序只能操作请求的头,有些可以处理超时,等等。
在ASP.NET Core中使用HttpClient发送GET请求
现在,让我们从修改HttpClientCrudService 类开始:
public class HttpClientCrudService : IHttpClientServiceImplementation
{
private static readonly HttpClient _httpClient = new HttpClient();
public HttpClientCrudService()
{
_httpClient.BaseAddress = new Uri("https://localhost:5001/api/");
_httpClient.Timeout = new TimeSpan(0, 0, 30);
}
public async Task Execute()
{
}
}
这里,我们创建了一个HttpClient属性,初始化它,并在构造函数中进行配置,我们设置了API的URI及超时时间。当然,我们可以在这个配置中找到更多的属性来使用,但是现在,这就足够了。稍后,当我们开始学习HttpClientFactory时,我们将把这些配置移到别的地方。
现在,我们可以在这个类中添加一个新方法:
public async Task GetCompanies()
{
var response = await _httpClient.GetAsync("companies");
response.EnsureSuccessStatusCode();
var content = await response.Content.ReadAsStringAsync();
var companies = JsonSerializer.Deserialize<List<CompanyDto>>(content, _options);
}
在这个方法中,我们使用HttpClient中的GetAsync方法,并传入地址。为了确保响应是成功的,我们调用EnsureSuccessStatusCode方法。一旦确定有成功状态码的响应,就可以将响应的内容作为字符串读取。最后,我们对数据列表进行反序列化。如你所见,我们使用了一个JsonSerializerOptions类型的参数,所以让我们将它添加到我们的类中,并在Execute方法中调用这个方法:
private readonly JsonSerializerOptions _options; public HttpClientCrudService()
{
_httpClient.BaseAddress = new Uri("https://localhost:5001/api/");
_httpClient.Timeout = new TimeSpan(0, 0, 30); _options = new JsonSerializerOptions { PropertyNameCaseInsensitive = true };
} public async Task Execute()
{
await GetCompanies();
}
我们为JsonSerializer设置不区分大小写的反序列化选项。没有它,我们的响应就不会被正确地反序列化。
现在,我们可以在GetCompanies方法中添加一个断点,启动Web API项目,然后启动客户端:

正如我们所看到的,我们在companies变量中得到了结果。
支持不同的响应格式
在本例中,我们接收到一个JSON作为默认的响应格式。我们的API默认支持这种类型。但有些API不默认为JSON,它们可能支持XML作为默认的响应格式或任何其他格式。在这种情况下,我们的逻辑就行不通了。
除了JSON之外,我们的API还支持XML响应格式。让我们看看如何在客户端应用程序中明确地要求格式。
首先,Http请求和响应都包含一组头,我们可以使用这些头在客户端和服务器应用程序之间传递附加信息。HTTP请求的通用头是Accept。我们使用这个头告诉服务器客户端将接受哪种媒体类型:accept: application/json, text/xml。
所以,让我们看看如何在我们的请求中设置头:
public HttpClientCrudService()
{
_httpClient.BaseAddress = new Uri("https://localhost:5001/api/");
_httpClient.Timeout = new TimeSpan(0, 0, 30);
_httpClient.DefaultRequestHeaders.Clear();
_httpClient.DefaultRequestHeaders.Accept.Add(
new MediaTypeWithQualityHeaderValue("application/json"));
_httpClient.DefaultRequestHeaders.Accept.Add(
new MediaTypeWithQualityHeaderValue("text/xml")); _options = new JsonSerializerOptions { PropertyNameCaseInsensitive = true };
}
这里,我们使用DefaultRequestHeaders属性并将其清除。然后,我们使用Accept属性,由于它是一个集合,所以我们添加了两个MediaTypeWithQualityHeaderValue对象。对于第一个对象,我们支持JSON格式,对于第二个对象,我们支持XML格式。为此,我们需要添加一个新的using语句:
using System.Net.Http.Headers;
现在,如果有一个像这样的配置,必须在方法中添加一些额外的代码,以决定如何反序列化响应:
public async Task GetCompanies()
{
var response = await _httpClient.GetAsync("companies");
response.EnsureSuccessStatusCode(); var content = await response.Content.ReadAsStringAsync(); var companies = new List<CompanyDto>(); if(response.Content.Headers.ContentType.MediaType == "application/json")
{
companies = JsonSerializer.Deserialize<List<CompanyDto>>(content, _options);
}
else if(response.Content.Headers.ContentType.MediaType == "text/xml")
{
var doc = XDocument.Parse(content);
foreach (var element in doc.Descendants())
{
element.Attributes().Where(a => a.IsNamespaceDeclaration).Remove();
element.Name = element.Name.LocalName;
} var serializer = new XmlSerializer(typeof(List<CompanyDto>));
companies = (List<CompanyDto>)serializer.Deserialize(new StringReader(doc.ToString()));
}
}
由于我们同时支持JSON和XML格式,我们必须检查哪个ContentType被应用到响应。如果是JSON,我们只需执行标准的反序列化。但如果是XML,我们将内容解析为XDocument。最后,我们创建一个新的XmlSerializer并反序列化XDocument。
此时,如果我们启动两个应用程序,并在方法中放置一个断点,我们将看到我们的默认格式是JSON:

HttpClient中的优先级
在我们的Accept头设置中,我们支持两种具有相同优先级的格式。优先级的最大值为1。但是,我们可以为这两个头文件中的一个设置较低的首选项——值必须在0到1之间。有较高值的请求将有更高优先级。
所以,让我们在构造函数中降低JSON Accept头的优先级:
_httpClient.DefaultRequestHeaders.Clear();
_httpClient.DefaultRequestHeaders.Accept.Add(
new MediaTypeWithQualityHeaderValue("application/json", 0.9));
_httpClient.DefaultRequestHeaders.Accept.Add(
new MediaTypeWithQualityHeaderValue("text/xml"));
正如我们所看到的,MediaTypeWithQualityHeaderValue构造函数接受另一个参数。我们将它的值设置为0.9。因为我们没有为XML Accept头添加任何值,所以默认值是1。现在,如果我们开始我们的应用程序,我们会发现XML是优先级更高的格式:

因此,执行将跳过这一部分并执行我们的XML反序列化。让我们在XDocument解析之前检查响应体:

然后,让我们在解析操作之后检查doc变量:

我们可以看出区别。在解析操作之后,反序列化就成功完成了:

我们已经看到了如何在请求中向HTTP Accept头添加首选项。但现在问题出现了。如果我们使用部分请求头,该怎么办?
使用HttpRequestMessage类发送HTTP请求
在这个实现中,我们对每个请求使用相同的头配置。因此,如果想发送默认为JSON格式的HTTP请求,我们不能使用这个类中的HTTP配置来实现。这是因为我们将XML格式设置为默认格式。这意味着我们必须提供一个不同的解决方案。
如果我们仔细考虑这一点,我们可以得出结论:BaseAddress和Timeout属性与HttpClient相关,但Accept头的属性连接到请求本身。同样,当我们使用GetAsync方法时,它在内部使用GET HTTP方法创建了一个新的HttpRequestMessage。也就是说,我们可以创建自己的HttpRequestMessage并为该请求提供报头。
最佳实践是在HttpClient实例上设置默认配置,在HTTP请求本身上设置请求配置。当然,如果我们总是希望使用JSON格式作为Accept报头,我们可以在HttpClient设置它。
实现
现在,让我们看看如何使用HttpRequestMessage类来实现HTTP请求。
首先,让我们从构造函数中移除accept头文件的配置:
public HttpClientCrudService()
{
_httpClient.BaseAddress = new Uri("https://localhost:5001/api/");
_httpClient.Timeout = new TimeSpan(0, 0, 30);
_httpClient.DefaultRequestHeaders.Clear();
_options = new JsonSerializerOptions { PropertyNameCaseInsensitive = true };
}
然后,我们可以将GetCompanies方法恢复到之前:
public async Task GetCompanies()
{
var response = await _httpClient.GetAsync("companies");
response.EnsureSuccessStatusCode();
var content = await response.Content.ReadAsStringAsync();
var companies = JsonSerializer.Deserialize<List<CompanyDto>>(content, _options);
}
最后,我们可以添加新方法:
public async Task GetCompaniesWithXMLHeader()
{
var request = new HttpRequestMessage(HttpMethod.Get, "companies");
request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("text/xml"));
var response = await _httpClient.SendAsync(request);
response.EnsureSuccessStatusCode();
var content = await response.Content.ReadAsStringAsync();
var doc = XDocument.Parse(content);
foreach (var element in doc.Descendants())
{
element.Attributes().Where(a => a.IsNamespaceDeclaration).Remove();
element.Name = element.Name.LocalName;
}
var serializer = new XmlSerializer(typeof(List<CompanyDto>));
var companies = (List<CompanyDto>)serializer.Deserialize(new StringReader(doc.ToString()));
}
因此,我们使用HttpRequestMessage类创建了一个新请求,该类提供了HTTP Method作为参数和API的地址。然后,向请求添加报头,并调用SendAsync方法发送请求。提取内容之后,重复前面方法中所做的步骤。我们还要做最后一件事。
让我们确保一旦客户端应用程序启动,这个方法就会被调用:
public async Task Execute(){ //await GetCompanies(); await GetCompaniesWithXMLHeader();}
正如我们之前所做的,我们将在这个方法中放置一个断点并启动两个应用程序:

如你所见,我们得到的结果与前面相同,但这次我们使用HttpRequestMessage的单独方法发送带有XML Accept头的HTTP请求。
结论
在本文中,我们讨论了HttpClient,以及如何在我们的ASP.NET Core中使用它处理来自Web API的数据。
原文链接:https://code-maze.com/fetching-data-with-httpclient-in-aspnetcore/

在ASP.NET Core中用HttpClient(一)——获取数据和内容的更多相关文章
- 在ASP.NET Core中用HttpClient(二)——发送POST, PUT和DELETE请求
在上一篇文章中,我们已经学习了如何在ASP.NET Core中使用HttpClient从Web API获取数据.此外,我们还学习了如何使用GetAsync方法和HttpRequestMessage类发 ...
- 在ASP.NET Core中用HttpClient(三)——发送HTTP PATCH请求
在前面的两篇文章中,我们讨论了很多关于使用HttpClient进行CRUD操作的基础知识.如果你已经读过它们,你就知道如何使用HttpClient从API中获取数据,并使用HttpClient发送PO ...
- 在ASP.NET Core中用HttpClient(四)——提高性能和优化内存
到目前为止,我们一直在使用字符串创建请求体,并读取响应的内容.但是我们可以通过使用流提高性能和优化内存.因此,在本文中,我们将学习如何在请求和响应中使用HttpClient流. 什么是流 流是以文件. ...
- 在ASP.NET Core中用HttpClient(五)——通过CancellationToken取消HTTP请求
用户向服务器发送HTTP请求应用程序页面是一种非常可能的情况.当我们的应用程序处理请求时,用户可以从该页面离开.在这种情况下,我们希望取消HTTP请求,因为响应对该用户不再重要.当然,这只是实际应用 ...
- 在ASP.NET Core中用HttpClient(六)——ASP.NET Core中使用HttpClientFactory
到目前为止,我们一直直接使用HttpClient.在每个服务中,我们都创建了一个HttpClient实例和所有必需的配置.这会导致了重复代码.在这篇文章中,我们将学习如何通过使用HttpClient ...
- C# ASP.NET Core使用HttpClient的同步和异步请求
引用 Newtonsoft.Json // Post请求 public string PostResponse(string url,string postData,out string status ...
- Asp.Net Core基于JWT认证的数据接口网关Demo
近日,应一位朋友的邀请写了个Asp.Net Core基于JWT认证的数据接口网关Demo.朋友自己开了个公司,接到的一个升级项目,客户要求用Aps.Net Core做数据网关服务且基于JWT认证实现对 ...
- ASP.NET Core 使用 EF 框架查询数据 - ASP.NET Core 基础教程 - 简单教程,简单编程
原文:ASP.NET Core 使用 EF 框架查询数据 - ASP.NET Core 基础教程 - 简单教程,简单编程 ASP.NET Core 使用 EF 框架查询数据 上一章节我们学习了如何设置 ...
- Asp.Net Core中HttpClient的使用方式
在.Net Core应用开发中,调用第三方接口也是常有的事情,HttpClient使用人数.使用频率算是最高的一种了,在.Net Core中,HttpClient的使用方式随着版本的升级也发生了一些变 ...
随机推荐
- 计蒜客-T1271 完美K倍子数组
如果一个数组满足长度至少是 22 ,并且其中任意两个不同的元素 A_iAi 和 A_j (i \not = j)Aj(i=j) 其和 A_i+A_jAi+Aj 都是 KK 的倍数,我们就称 ...
- Proud Merchants HDU - 3466 01背包&&贪心
最近,我去了一个古老的国家.在很长一段时间里,它是世界上最富有.最强大的王国.结果,这个国家的人民仍然非常自豪,即使他们的国家不再那么富有.商人是最典型的,他们每个人只卖一件商品,价格是Pi,但是如果 ...
- 主席树 【权值线段树】 && 例题K-th Number POJ - 2104
一.主席树与权值线段树区别 主席树是由许多权值线段树构成,单独的权值线段树只能解决寻找整个区间第k大/小值问题(什么叫整个区间,比如你对区间[1,8]建立一颗对应权值线段树,那么你不能询问区间[2,5 ...
- spark 一、编程指南
总览 第一.每个spark 应用都有一个驱动程序去运行着主函数和再每个节点上的并行操作. spark提供了一个RDD(弹性分布式数据集)的数据集合,可以通过不同的节点并行操作运算,可以通过hdfs文件 ...
- POJ 2288 Islands and Bridges(状压DP)题解
题意:n个点,m有向边,w[i]表示i的价值,求价值最大的哈密顿图(只经过所有点一次).价值为:所有点的w之和,加上,每条边的价值 = w[i] * w[j],加上,如果连续的三个点相互连接的价值 = ...
- javascript输出数据到文件
function export(name, data) { var urlObject = window.URL || window.webkitURL || window var export_bl ...
- mimikatz+procdump 提取 Windows 明文密码
0x00 原理 获取到内存文件 lsass.exe 进程 (它用于本地安全和登陆策略) 中存储的明文登录密码. 0x01 操作 Windows10/2012 以下的版本:1.上传 procdump 执 ...
- 011.NET5_MVC解读Razor混编
MVC开发 1. 什么是MVC? V-视图,呈现给用户看到的内容---表现层 C-控制器,控制业务逻辑计算,可定义多种返回类型.可以是视图模型.JSON.字符串等等 M-视图模型,用于视图和控制之间传 ...
- js array & unshift === push head
js array & unshift === push head const arr = [1, 2, 3]; console.log(arr.unshift(4, 5)); // 5 con ...
- React render twice bug
React render twice bug React bug constructor render twice bug update render twice bug StrictMode htt ...