参考:
https://blog.csdn.net/u010476739/article/details/119782562(HttpClint使用案例)
HttpClient用于发送http请求,默认情况下内部使用HttpWebRequest发送请求,这一行为可以通过构造函数中指定HttpMessageHandler参数来使用不同的通道
HttpClient构造函数可以接受一个HttpMessageHandler类型的参数
HttpClient继承自HttpMessageInvoker,从名字可以看出HttpClient是一个调用器,它是对HttpMessageHandler的一个调用,HttpClient本身不提供Http请求功能
public HttpClient(HttpMessageHandler handler){}
HttpClient相关类
HttpClient这个类本身并不会进行实际的网络请求收发处理,我们应将其理解成一个容器、一个中继者,实际的网络请求核心在HttpClientHanlder中,也就是下面图中对应的Inner Handler。
HttpClientHandler
HttpClientHandler继承自HttpMessageHandler,用于设置cookie、代理、证书、Headlers等属性
- 是否自动处理cookie
默认HttpClient是自动处理cookie的,即:上一个请求返回的cookie,可能会随着下次请求发送出去。
HttpClientHandler httpClientHandler = new HttpClientHandler {
UseCookies=false//是否自动处理cookie
};
HttpClient client = new HttpClient(httpClientHandler);
- 是否自动重定向以及最多重定向几次
默认HttpClient自动处理重定向请求,并且最多重定向50次
HttpClientHandler httpClientHandler = new HttpClientHandler
{
AllowAutoRedirect = true,//是否允许重定向,默认为true
MaxAutomaticRedirections = 50//最多重定向几次,默认50次
};
- 内部TCP链接池的设置
每个url(如:http://www.baidu.com:80)最多有几个链接,默认是int.MaxValue。注意:url是不带路径及参数;
HttpClientHandler httpClientHandler = new HttpClientHandler
{
MaxConnectionsPerServer=int.MaxValue,
};
- 是否压缩
默认是不压缩,如果设置开启压缩的话,http请求头中会自动加上Accept-Encoding: gzip(当然你得设置压缩选项是gzip),如果后台也支持这种压缩的话,就会把消息体压缩并在响应头中添加Content-Encoding: gzip
HttpClientHandler httpClientHandler = new HttpClientHandler
{
AutomaticDecompression=System.Net.DecompressionMethods.GZip
};
- 超时设置
SocketsHttpHandler socketsHttpHandler = new SocketsHttpHandler {
ConnectTimeout= System.Threading.Timeout.InfiniteTimeSpan//默认不限制
};
- 响应头数据大小限制
http响应头最大字节数
HttpClientHandler httpClientHandler = new HttpClientHandler
{
MaxResponseHeadersLength = 64,//单位是KB
};
验证相关:PreAuthenticate、Credentials
代理相关:Proxy、UseProxy、DefaultProxyCredentials
DelegatingHandler
抽象类,DelegatingHandler继承自HttpMessageHandler,DelegatingHandler内部有一个属性InnerHandler,http请求都是通过InnerHandler发送,这个InnerHandler就是HttpClientHandler。通过继承此类可实现类似HttpModule的功能
public abstract class DelegatingHandler : HttpMessageHandler
{}
HttpClient请求过程
从Request发起,经过DelegatingHanlder处理后,进入InnerHandler,数据返回后再从Inner Handler 返回到Delegating Hanlder进行处理,最后返回结果。
从设计角度来讲,HttpClient库提供了强大的扩展性,使用者不需要任何继承即可完成对HttpClient的扩展(如果对设计模式熟悉,可以清楚的看出这里用到了装饰器模式)
HttpResponseMessage
HttpClient请求的返回类型,内部包含Headlers、Content、StatusCode、IsSuccessStatusCode等属性
EnsureSuccessStatusCode()方法:如果不返回200就抛出异常
其中Content属性是HttpContent类型,可转成对应类型,获取Content数据
与Refit结合
https://www.xcode.me/code/refit-the-automatic-type-safe-rest-library
https://docs.microsoft.com/zh-cn/aspnet/core/fundamentals/http-requests?view=aspnetcore-2.1#outgoing-request-middleware
HttpClient案例
案例1:设置cookie、Headers、代理、证书等,通过设置HttpClientHandler属性实现
HttpClientHandler httpClientHandler = new HttpClientHandler();
httpClientHandler.CookieContainer.Add(new Cookie("id", "1"));
//httpClientHandler.Proxy = null;
//httpClientHandler.Credentials = null;
HttpClient httpClient = new HttpClient(httpClientHandler);
案例2:记录HttpClient请求、响应内容日志,通过继承DelegatingHandler抽象类实现
我们自己定义了一个LoggingHandler,这个类对应Delegating Handler 是我们自定义的、装饰在Inner Handler外的Handler。
DelegatingHandler重载了SendAsync,在其内部调用了InnerHandler的SendAsync方法,如此我们便可以在实际请求发出,以及返回后进行各种统一的处理,总结起来仍是上面图中画出的,逐层调用。
必须给LoggingHandler传入HttpClientHandler,因为最终都是通过HttpClientHandler发送请求,如果不传会抛异常
public class LoggingHandler : DelegatingHandler
{
public LoggingHandler(HttpMessageHandler handler) : base(handler) { }
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
if (request.Content!=null)
{
Debug.WriteLine(await request.Content.ReadAsStringAsync());
}
HttpResponseMessage response = await base.SendAsync(request, cancellationToken);
if (response.Content!=null)
{
Debug.WriteLine(await response.Content.ReadAsStringAsync());
}
return response;
}
}
HttpMessageHandler handler = new HttpClientHandler();
HttpClient client = new HttpClient(new LoggingHandler(handler));
案例3:失败重试
public class RetryHandler : DelegatingHandler
{
private const int MAX_COUNT = 3;
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
HttpResponseMessage response = null;
for (int i = 0; i < MAX_COUNT; i++)
{
response = await base.SendAsync(request, cancellationToken);
if (response.IsSuccessStatusCode)
{
return response;
}
}
return response;
}
}
HttpMessageHandler handler = new HttpClientHandler();
handler = new LoggingHandler(handler);
handler = new RetryHandler(handler);
HttpClient client = new HttpClient(handler);
案例:ASP.NET Core中指定DelegatingHandler、HttpClientHandler
public void ConfigureServices(IServiceCollection services)
{
services.AddHttpClient("client1", client => { client.BaseAddress = new System.Uri("https://www.baidu.com"); })
.AddHttpMessageHandler(() => new RetryHandler())//设置DelegatingHandler
.ConfigurePrimaryHttpMessageHandler(() => new System.Net.Http.HttpClientHandler()
{//设置HttpClientHandler,也就是InnerHandler
UseCookies = false
});
}
HttpClientFactory使用案例
展开代码
注册
public void ConfigureServices(IServiceCollection services)
{
//1、
services.AddHttpClient();
//2、命名化客户端,可以添加一些特定配置。您可以注册多个命名化客户端,每个客户端都可以预先配置不同的设置。
services.AddHttpClient("client_1",config=>
{
config.BaseAddress= new Uri("http://client_1.com");
config.DefaultRequestHeaders.Add("header_1","header_1");
});
//3、类型化客户端,此HttpClient可以注入到一个自定义类中
services.AddHttpClient<SSOService>();
}
//类型化客户端类
public class SSOService
{
private readonly HttpClient _httpClient = null;
public SSOService(HttpClient httpClient)
{
_httpClient = httpClient;
}
public string GetContent()
{
return _httpClient.GetAsync("https://www.baidu.com").Result.Content.ReadAsStringAsync().Result;
}
}
使用HttpClient
public async Task<ActionResult> Test([FromServices]IHttpClientFactory httpClientFactory,[FromServices] SSOService ssoService)
{
var client1 = httpClientFactory.CreateClient();
var result1 = await client1.GetStringAsync("http://www.site.com/XXX.html");
var client2 = httpClientFactory.CreateClient("client_1");
var result2 = await client2.GetStringAsync("/page1.html");
var result3= ssoService.GetContent();
return Content();
}
HttpClient+Consule案例
展开代码
public class ConsulDiscoveryDelegatingHandler : DelegatingHandler
{
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
var current = request.RequestUri;
try
{ //调用的服务地址里的域名(主机名)传入发现的服务名称即可
request.RequestUri = new Uri($"{current.Scheme}://{LookupService(current.Host)}/{current.PathAndQuery}");
return await base.SendAsync(request, cancellationToken).ConfigureAwait(false);
}
catch (Exception e)
{
throw;
}
finally
{
request.RequestUri = current;
}
}
private string LookupService(string serviceName)
{
using (ConsulClient consulClient = new ConsulClient(config=>config.Address=new Uri("http://localhost:8500/")))
{
var services = _consulClient.Catalog.Service(serviceName).Result.Response;
if (services != null && services.Any())
{
//模拟负载均衡算法(随机获取一个地址)
int index = r.Next(services.Count());
var service = services.ElementAt(index);
return $"{service.ServiceAddress}:{service.ServicePort}");
}
return null;
}
}}
调用
public async Task<Person> GetPerson(int personId)
{
using (HttpClient client = new HttpClient(new ConsulDiscoveryDelegatingHandler()))
{ //调用时的域名(主机名)传入服务发现的名称即可
var response = await client.GetAsync($"http://PersonService/Person/GetPerson?personId={personId}");
var jsonResult = await response.Content.ReadAsStringAsync();
return jsonResult.FromJson<Person>();
}
}