Make HTTP Request
分类: Asp.net Core入门 ◆ 标签: #Asp.Net core基础 #基础 #Web ◆ 发布于: 2023-06-04 20:41:42
Asp.net Core
提供将IHttpClientFactory
实例注册为服务,为用户创建和配置HttpClient
, IHttpClientFactory
提供如下的优势:
- 提供一个控制中心,用于创建命名或者类型
HttpClient
。 - 利用
Delegate handler
为HttpClient
创建中间件。并提供基于Polly的扩展方法,为httpclient
提供重试,错误处理等功能。 - 将
HttpClientMessageHandler
进行池化,并管理其生命周期。 - 为所有通过
client
的请求提供可配置的日志功能。
基本的使用形式
IHttpClientFactory
有如下几种使用形式:
- 基本使用方法
- 命名客户端
- 强类型客户端
- 泛型客户端
基本使用方式
如下的步骤来使用:
- 使用扩展方法注册服务
- 在需要使用的地方使用构造器注入
IHttpClientFactory
。
public class Startup { public Startup(IConfiguration configuration) { Configuration = configuration; } public IConfiguration Configuration { get; } public void ConfigureServices(IServiceCollection services) { services.AddHttpClient(); // Remaining code deleted for brevity.
使用Services.AddHttpClient()
来注册服务。
使用类的构造函数注入
public class BasicUsageModel : PageModel { private readonly IHttpClientFactory _clientFactory; public IEnumerable<GitHubBranch> Branches { get; private set; } public bool GetBranchesError { get; private set; } public BasicUsageModel(IHttpClientFactory clientFactory) { _clientFactory = clientFactory; } public async Task OnGet() { var request = new HttpRequestMessage(HttpMethod.Get, "https://api.github.com/repos/dotnet/AspNetCore.Docs/branches"); request.Headers.Add("Accept", "application/vnd.github.v3+json"); request.Headers.Add("User-Agent", "HttpClientFactory-Sample"); var client = _clientFactory.CreateClient(); var response = await client.SendAsync(request); if (response.IsSuccessStatusCode) { using var responseStream = await response.Content.ReadAsStreamAsync(); Branches = await JsonSerializer.DeserializeAsync <IEnumerable<GitHubBranch>>(responseStream); } else { GetBranchesError = true; Branches = Array.Empty<GitHubBranch>(); } } }
注入IHttpClientFactory
之后,使用CreateClient()创建客户端。
命名客户端
命名客户端很容易理解,直接看代码就好了。
services.AddHttpClient("github", c => { c.BaseAddress = new Uri("https://api.github.com/"); // Github API versioning c.DefaultRequestHeaders.Add("Accept", "application/vnd.github.v3+json"); // Github requires a user-agent c.DefaultRequestHeaders.Add("User-Agent", "HttpClientFactory-Sample"); });
使用客户端:
public class NamedClientModel : PageModel { private readonly IHttpClientFactory _clientFactory; public IEnumerable<GitHubPullRequest> PullRequests { get; private set; } public bool GetPullRequestsError { get; private set; } public bool HasPullRequests => PullRequests.Any(); public NamedClientModel(IHttpClientFactory clientFactory) { _clientFactory = clientFactory; } public async Task OnGet() { var request = new HttpRequestMessage(HttpMethod.Get, "repos/dotnet/AspNetCore.Docs/pulls"); var client = _clientFactory.CreateClient("github"); var response = await client.SendAsync(request); if (response.IsSuccessStatusCode) { using var responseStream = await response.Content.ReadAsStreamAsync(); PullRequests = await JsonSerializer.DeserializeAsync <IEnumerable<GitHubPullRequest>>(responseStream); } else { GetPullRequestsError = true; PullRequests = Array.Empty<GitHubPullRequest>(); } } }
可以创建多个命名客户端,每个客户端都有不一样的配置和设定。
强类型客户端
分这样几步:
- 定义一个服务,并使用
HttpClient
定义为一个字段 - 为这个服务定义使用
HttpClient
的方法。 - 在
Startup
类的ConfigureService
通过AddHttpClient方法注册该服务。 - 在应用端直接通过构造函数引入该类。
public class RepoService { // _httpClient isn't exposed publicly private readonly HttpClient _httpClient; public RepoService(HttpClient client) { _httpClient = client; } public async Task<IEnumerable<string>> GetRepos() { var response = await _httpClient.GetAsync("aspnet/repos"); response.EnsureSuccessStatusCode(); using var responseStream = await response.Content.ReadAsStreamAsync(); return await JsonSerializer.DeserializeAsync <IEnumerable<string>>(responseStream); } }
注册服务:
services.AddHttpClient<RepoService>(c => { c.BaseAddress = new Uri("https://api.github.com/"); c.DefaultRequestHeaders.Add("Accept", "application/vnd.github.v3+json"); c.DefaultRequestHeaders.Add("User-Agent", "HttpClientFactory-Sample"); });
在应用类中通过构造函数注入该服务,并使用:
public class TypedClientModel : PageModel { private readonly RepoService _repoService; public IEnumerable<GitHubIssue> LatestIssues { get; private set; } public bool HasIssue => LatestIssues.Any(); public bool GetIssuesError { get; private set; } public TypedClientModel(RepoService repoService) { _repoService = repoService; } public async Task OnGet() { try { LatestIssues = await _rrepoService.getXXXX(); } catch(HttpRequestException) { GetIssuesError = true; LatestIssues = Array.Empty<GitHubIssue>(); } } }
泛型客户端
IHttpClientFactory
可以和第三方的库,例如Refit
联合使用,Refit
会将REST API
转换一个自动实现的接口(by RestService)。
我们先定义一个接口:
public interface IHelloClient { [Get("/helloworld")] Task<Reply> GetMessageAsync(); } public class Reply { public string Message { get; set; } }
这个接口代表了一个外部的REST API
以及它的返回。
在注册服务的时候,添加一个强类型的客户端,并Refit
根据接口动态生成一个实现。
public void ConfigureServices(IServiceCollection services) { services.AddHttpClient("hello", c => { c.BaseAddress = new Uri("http://localhost:5000"); }) .AddTypedClient(c => Refit.RestService.For<IHelloClient>(c)); services.AddControllers(); }
然后在应用中直接使用就好了:
[ApiController] public class ValuesController : ControllerBase { private readonly IHelloClient _client; public ValuesController(IHelloClient client) { _client = client; } [HttpGet("/")] public async Task<ActionResult<Reply>> Index() { return await _client.GetMessageAsync(); } }
发送POST
, PUT
, DELETE
请求
这个没啥好讲的,直接看代码:
POST
public async Task CreateItemAsync(TodoItem todoItem) { var todoItemJson = new StringContent( JsonSerializer.Serialize(todoItem, _jsonSerializerOptions), Encoding.UTF8, "application/json"); using var httpResponse = await _httpClient.PostAsync("/api/TodoItems", todoItemJson); httpResponse.EnsureSuccessStatusCode(); }
PUT
public async Task SaveItemAsync(TodoItem todoItem) { var todoItemJson = new StringContent( JsonSerializer.Serialize(todoItem), Encoding.UTF8, "application/json"); using var httpResponse = await _httpClient.PutAsync($"/api/TodoItems/{todoItem.Id}", todoItemJson); httpResponse.EnsureSuccessStatusCode(); }
DELETE
public async Task DeleteItemAsync(long itemId) { using var httpResponse = await _httpClient.DeleteAsync($"/api/TodoItems/{itemId}"); httpResponse.EnsureSuccessStatusCode(); }
构建outgoing 请求中间件
HttpClient
有一个Delegating Handlers
的概念,使用这个概念可以很容易为outgoing 的请求创建中间件。
使用IHttpClientFactory
:
- 可以很容易的为命名客户端定义和应用
Handlers
- 支持注册和chain多个handler,从而创建一个处理外发请求的pipeline。
- 类似inbound的中间件(ASP.net Core)
- 可以使用这个机制:
- 缓存
- 错误处理
- 系列化
- 记录日志
创建delegate handler
创建Delegate handler
, 必须:
- 继承
DelegatingHandler
- 覆盖
SendAsync
方法,可以在传递请求到下一个Handler之前或者之后运行必要的代码。
public class ValidateHeaderHandler : DelegatingHandler { protected override async Task<HttpResponseMessage> SendAsync( HttpRequestMessage request, CancellationToken cancellationToken) { if (!request.Headers.Contains("X-API-KEY")) { return new HttpResponseMessage(HttpStatusCode.BadRequest) { Content = new StringContent( "You must supply an API key header called X-API-KEY") }; } return await base.SendAsync(request, cancellationToken); } }
在服务注册的时候,添加DelegatingHandler
:
public void ConfigureServices(IServiceCollection services) { services.AddTransient<ValidateHeaderHandler>(); services.AddHttpClient("externalservice", c => { // Assume this is an "external" service which requires an API KEY c.BaseAddress = new Uri("https://localhost:5001/"); }) .AddHttpMessageHandler<ValidateHeaderHandler>(); // Remaining code deleted for brevity.
可以同时注册多个Handler:
services.AddTransient<SecureRequestHandler>(); services.AddTransient<RequestDataHandler>(); services.AddHttpClient("clientwithhandlers") // This handler is on the outside and called first during the // request, last during the response. .AddHttpMessageHandler<SecureRequestHandler>() // This handler is on the inside, closest to the request being // sent. .AddHttpMessageHandler<RequestDataHandler>();
客户端的lifttime管理
可以直接看代码:
public void ConfigureServices(IServiceCollection services) { services.AddHttpClient("extendedhandlerlifetime") .SetHandlerLifetime(TimeSpan.FromMinutes(5)); // Remaining code deleted for brevity.
Cookie
IHttpClientFactory
这种模式不适合每次都需要用cookie的场景,遇到这个场景,直接cookie自动处理。
services.AddHttpClient("configured-disable-automatic-cookies") .ConfigurePrimaryHttpMessageHandler(() => { return new HttpClientHandler() { UseCookies = false, }; });
配置HttpMessageHandler
public void ConfigureServices(IServiceCollection services) { services.AddHttpClient("configured-inner-handler") .ConfigurePrimaryHttpMessageHandler(() => { return new HttpClientHandler() { AllowAutoRedirect = false, UseDefaultCredentials = true }; }); // Remaining code deleted for brevity.
使用header传递中间件:
public void ConfigureServices(IServiceCollection services) { services.AddControllers(); services.AddHttpClient("MyForwardingClient").AddHeaderPropagation(); services.AddHeaderPropagation(options => { options.Headers.Add("X-TraceId"); }); } public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } app.UseHttpsRedirection(); app.UseHeaderPropagation(); app.UseRouting(); app.UseAuthorization(); app.UseEndpoints(endpoints => { endpoints.MapControllers(); }); }
配置好后,开始使用:
var client = clientFactory.CreateClient("MyForwardingClient"); var response = client.GetAsync(...);