前言

以前写过的文章 Asp.net core 学习笔记 ( HttpClient ).

其实 HttpClient 内容是挺多的, 但是我自己用到的很少. 所以这篇记入一下自己用到的就好了.

参考

3 ways to use HTTPClientFactory in ASP.NET Core 2.1

Docs – Make HTTP requests using IHttpClientFactory in ASP.NET Core

3 大用法介绍

其实是 4 种哦, 只是第 4 种我不熟就忽略掉呗.

Basic usage 的方式是注入 HttpClientFactory > 创建 HttpClient > 发 request

Named clients 的方式是先在 program.cs provide 好 config, 比如 Uri, default header 等等. 然后注入 HttpClientFactory > 创建 HttpClient with named config > 发 request.

如果你要发多个不同的 path 但是相同的 origin 的话, 这个管理方式会比 Basic usage 好.

Typed clients 的方式是先做一个 service class, 在 constructor 注入 HttpClient 然后配置 config, 提供各做方法里面使用 HttpClient 发 request.

这个方式主要就是封装了所以对这个 client 的请求, 外部只要 call service 的 method 就好.

对比 3 大用法

可以看出来, 主要就是在管理上的区别. 个人建议, 如果只是 1 个请求, 那么直接用 basic 就好了, 如果多个请求, 或者多个地方用到, 那么就用 Type clients. 至于 Named clients 感觉有点不上不下就不要用了.

Get Request (Basic usage)

要到这里 Github – CountryCodes.json 请求 Country Code JSON

Provide HttpClient

builder.Services.AddHttpClient();

注入 HttpClientFactory

private readonly IHttpClientFactory _httpClientFactory;

public IndexModel(
IHttpClientFactory httpClientFactory
)
{
_httpClientFactory = httpClientFactory;
}

创建 Request Message

var httpRequestMessage = new HttpRequestMessage
{
RequestUri = new Uri("https://gist.githubusercontent.com/Goles/3196253/raw/9ca4e7e62ea5ad935bb3580dc0a07d9df033b451/CountryCodes.json"),
Method = HttpMethod.Get,
Headers = {
{ "Accept", "application/json; charset=utf-8" }
}
};
httpRequestMessage.Headers.Append("Accept", "application/json; charset=utf-8"); // 或者后来添加也可以

调用 HttpClient 发请求

var httpClient = _httpClientFactory.CreateClient();
var response = await httpClient.SendAsync(httpRequestMessage);
if (response.IsSuccessStatusCode)
{
var json = await response.Content.ReadAsStringAsync();
}

以上就是 basic usage 的方式. 类似发 SMTP 请求那样.

解析 JSON response

另外, 也可以直接解析 JSON response

public class Country
{
public string Name { get; set; } = ""; [JsonPropertyName("dial_code")]
public string DialCode { get; set; } = ""; public string Code { get; set; } = "";
} var counties = await response.Content.ReadFromJsonAsync<List<Country>>();

Download Image

下载图片也是可以的

var buffer = await response.Content.ReadAsByteArrayAsync();
using var fileStream = System.IO.File.Create("abc.jpg", buffer.Length);
await fileStream.WriteAsync(buffer);
fileStream.Flush();

Get Request (Typed clients)

创建 Client

public class CountryCodeHttpClient
{
private readonly HttpClient _httpClient;
public CountryCodeHttpClient(
HttpClient httpClient
)
{
_httpClient = httpClient;
_httpClient.BaseAddress = new Uri("https://gist.githubusercontent.com");
_httpClient.DefaultRequestHeaders.Append("Accept", "application/json; charset=utf-8");
} public async Task<List<Country>> GetCountriesAsync()
{
var response = await _httpClient.GetAsync("/Goles/3196253/raw/9ca4e7e62ea5ad935bb3580dc0a07d9df033b451/CountryCodes.json");
if (response.IsSuccessStatusCode)
{
return (await response.Content.ReadFromJsonAsync<List<Country>>())!;
}
else
{
throw new Exception("error");
}
}
}

通常 Client 在 construtor 会配置通用的 URI 和 Header. 在方法内则控制 path 和 extra Header.

再给一个复杂例子

var queryBuilder = new QueryBuilder();
queryBuilder.Add("param1", "value1");
var queryString = queryBuilder.ToQueryString().Value!; var requestMessage = new HttpRequestMessage
{
Method = HttpMethod.Get,
RequestUri = new Uri($"/Goles/3196253/raw/9ca4e7e62ea5ad935bb3580dc0a07d9df033b451/CountryCodes.json{queryString}", UriKind.Relative),
Headers = {
{ "Accept", "application/json; charset=utf-8" }
}
}; var response = await _httpClient.SendAsync(requestMessage);

Provide HttpClient

builder.Services.AddHttpClient<CountryCodeHttpClient>();

调用

var counties = await _countryCodeHttpClient.GetCountriesAsync();

BaseAddress 和 HttpRequestMessage.RequestUri

HttpClient 可以 set BaseAddress。

HttpRequestMessage 可以 set RequestUri。

这 2 个的搭配玩法是这样的。

每当我们要发送 request 之前

HttpClient 会跑一个 CheckRequestBeforeSend(HttpRequestMessage)

里面有一个 PrepareRequestMessage

它就是处理 BaseAddress 和 RequestUri 的

1. 当 BaseAddress 和 RequestUri 都没有定义时,报错。

2. 当 RequestUri 没有定义时,用 BaseAddress 来发送。

3. 当 RequestUri 是绝对路径时,无视 BaseAddress 只用 RequestUri 来发送。

4. 当 RequestUri 是相对路径时,combine BaseAddress 和 RequestUri 来发送。

这个 combine 的代码是

new Uri(_baseAddress, request.RequestUri);

这个 combine 的逻辑是这样的。

假设 base 是 https://example.com/a/b/c/

它的 path 是 ["a", "b", "c", ""],最后一个是 empty。

假设 relative 开头是 /d 表示 override 之前所有 path,结果就是 .com/d,a,b,c,empty 都不要了。

假设 relative 开头是 d 表示 override 最后一个(最后一个是 empty 而不是 c 哦),结果是 .com/a/b/c/d。

假设 relative 开头是 ../d 表示 override 最后两个, 结果是 .com/a/b/d,c 和 empty 不要了。

假设 relative 开头是 ? 表示不需要 override path,把 query 加进去就可以了,结果是 .com/a/b/c/?name=derrick

我们来看几个常见的例子:

var baseUri = new Uri("https://example.com");
var relativeUri = new Uri("/products", UriKind.Relative);
var combineUri = new Uri(baseUri, relativeUri);
Console.WriteLine(combineUri.ToString());

第一,当 baseUri 只是一个 domain,我们加不加 trailing slash 都可以。下面 4 种组合的结果是一样的。

// https://example.com + products = https://example.com/products
// https://example.com + /products = https://example.com/products
// https://example.com/ + products = https://example.com/products
// https://example.com/ + /products = https://example.com/products // https://example.com/ + ?name=derrick = https://example.com/name=derrick
// https://example.com + ?name=derrick = https://example.com/name=derrick (没有 trailing slash 会自动加)

第二,当 baseUrl 是一个 folder,我们必须加上 trailing slash

// https://example.com/products + mk100  = https://example.com/mk100 (错误)
// https://example.com/products + /mk100 = https://example.com/mk100 (错误)
// https://example.com/products/ + mk100 = https://example.com/products/mk100 (正确)
// https://example.com/products/ + /mk100 = https://example.com/mk100 (错误,除非你想要 override base uri 的 path,那就正确)

第三,当 baseUrl 是一个 file,我们不要加上 trailing slash

// https://example.com/products + ?name=derrick  = https://example.com/products?name=derrick (正确)
// https://example.com/products/ + ?name=derrick = https://example.com/products/?name=derrick (错误)

第四,奇葩,在做 Google – Cloud Translation API 看到一个比较奇葩的 url。

// 这个是最终要的 url
// https://translation.googleapis.com/v3/projects/projectid:translateText // 如果 base 是
// https://translation.googleapis.com/v3/projects/projectid/ // relative 是
// :translateText // 结果
// https://translation.googleapis.com/v3/projects/projectid/:translateText
// 这个不是我们要的 url,这题没有办法用 base + relative 的方式搞,因为 projectid:translateText 是不能被拆开的。

Post Request (Basic usage)

Post 和 Get 差不多, 我们 focus 不一样的地方就好了.

var httpRequestMessage = new HttpRequestMessage
{
RequestUri = new Uri($"https://www.googleapis.com/oauth2/v4/token"),
Method = HttpMethod.Post,
Headers = {
{ "Accept", "application/json" },
// { "Content-Type", "application/json" } // 不要在这里声明 Content-Type, 下面的 Content 会负责
// { "Authorization", $"Bearer {accessToken}" } // 常用的 Bearer Token Header
},
Content = JsonContent.Create(new
{
refresh_token = "",
client_id = "",
client_secret = "",
redirect_uri = "https://developers.google.com/oauthplayground",
grant_type = "refresh_token"
}) // 或者直接传 string 也是可以的
// Content = new StringContent(JsonSerializer.Serialize(new
// {
// refresh_token = "",
// client_id = "",
// client_secret = "",
// redirect_uri = "https://developers.google.com/oauthplayground",
// grant_type = "refresh_token"
// }), Encoding.UTF8, mediaType: "application/json")
};
var httpClient = _httpClientFactory.CreateClient();
var response = await httpClient.SendAsync(httpRequestMessage);
if (response.IsSuccessStatusCode)
{
var json = await response.Content.ReadAsStringAsync();
}

1. Method 换成了 POST

2. Header 不可以放 Content-Type. 参考: Stack Overflow – How do you set the Content-Type header for an HttpClient request?

3. Content 可以用 JsonContent 或者 StringContent 记得提供 mediaType, Content 会负责 set header Content-Type

StringContent.cs

JsonContent.cs

ASP.NET Core – HttpClient的更多相关文章

  1. asp.net Core HttpClient 出现Cannot access a disposed object. Object name: 'SocketsHttpHandler' 的问题。

    ASP.NET Core 部署在Centos 中 偶尔出现 One or more errors occurred. (Cannot access a disposed object.Object n ...

  2. Asp.Net Core 轻松学-HttpClient的演进和避坑

    前言     在 Asp.Net Core 1.0 时代,由于设计上的问题, HttpClient 给开发者带来了无尽的困扰,用 Asp.Net Core 开发团队的话来说就是:我们注意到,HttpC ...

  3. ASP.NET Core中如何针对一个使用HttpClient对象的类编写单元测试

    原文地址: How to unit test a class that consumes an HttpClient with IHttpClientFactory in ASP.NET Core? ...

  4. ASP.NET Core 2.1 : 十三.httpClient.GetAsync 报SSL错误的问题

    不知什么时候 ,出现了这样的一个奇怪问题,简单的httpClient.GetAsync("xxxx")居然报错了.(ASP.NET Core 系列目录) 一.问题描述 把原来的程序 ...

  5. .net core下用HttpClient和asp.net core实现https的双向认证

    关于https双向认证的知识可先行google,这时矸接代码. 为了双向认证,我们首先得准备两个crt证书,一个是client.crt,一个是server.crt,有时为了验证是否同一个根证书的验证, ...

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

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

  7. ASP.NET CORE 2.* 利用集成测试框架覆盖HttpClient相关代码

    ASP.NET CORE 集成测试官方介绍 我的asp.net core 项目里面大部分功能都是去调用别人的API ,大量使用HttpClient,公司单元测试覆盖率要求95%以上,很难做到不mock ...

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

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

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

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

  10. ASP.NET Core教程:在ASP.NET Core中使用HttPClient调用WebService

    一.前言 在以前的一篇文章中,曾经讲述过如何在ASP.NET Core中调用WebService.但是那种方式是通过静态引用的方式去调用的,如果是在生产环境中,肯定不能使用这种方式去调用,幸运的是微软 ...

随机推荐

  1. C# 语言笔记

    1. C# 初识 因为先前已经学过 C++ 了,所以在C# 的学习中,大多只记录和 C++ 不同的点,在学习的过程中,感谢刘铁猛老师的教程,您是我C# 入门的领路人. 1.1 使用 .net cli ...

  2. 使用PHP实现字符串的上标和下标,比如:M²和Log₂FC

    要在PHP中实现字符串的上标和下标效果,并直接在命令行或网页中正确显示,你可以分别使用Unicode转义序列或HTML实体来表示上标(UPER)和下标(SUB)字符.对于打印到网页的情况,可以使用HT ...

  3. oeasy教您玩转vim - 005 - # 程序本质

    ​ 程序本质 回忆上次内容 py 的程序是按照顺序 一行行挨排解释执行的 我们可以 python3 -m pdb hello.py 来对程序调试 调试的目的是去除 bug 别害怕 bug bug 会有 ...

  4. vue小知识~eventBus

    eventBus是指在向全区暴露这个vue对象,此时在任意一个地方都可以使用vue相关的实例 在main.js配置 Vue.prototype.$bus=new Vue() 此时整个应用都可以使用vu ...

  5. 关于c++出现的易错问题

    比如我一个对象,经常操作用的指针ptr,原生指针比如ClassA* ca =; 但是我要保存ca,在另一个地方操作,比如: cb =ca; 这样子是不行的,因为我要操作的是ca,而不是ca的值,为什么 ...

  6. pycharm中可以运行脚本(只在控制台运行,Debugger不运行,设置的断点没用)但是不能debug脚本

    以前用的时候好好地,但是最近上班突然脚本就不能debug了,debug直接报错,如下所示 上网查过该有的原因: 1.在pycharm中两个地方设置成utf-8,页面右下角和File>settin ...

  7. CCStheia添加include路径

    一.在系统内找到该路径 二.复制该路径,并更改写法 C:\Users\c1519\workspace_ccstheia\OLED\user_lib 改为: C:/Users/c1519/workspa ...

  8. iOS开发基础144-逐字打印效果

    在AIGC类的APP中,实现那种一个字一个字.一行一行地打印出文字的效果,可以通过多种方法来实现.下面是一些实现方法,使用Swift和OC来举例说明. OC版 1. 基于定时器的逐字打印效果 可以使用 ...

  9. 【Java】Reflection 反射机制 03调用

    调用属性,方法,构造器 属性调用 @Test public void fieldCall() throws NoSuchFieldException, IllegalAccessException, ...

  10. 【Scala】07 集合

    分三大类: 序列 Seq 集 Set 映射 Map 所有集合类型都扩展自Iterable特质(可迭代的) 所有集合类型都提供[可变]和[不可变]的版本 归纳在下面两个包中 scala.collecti ...