给HttpClient添加请求头(HttpClientFactory)
前言
在微服务的大环境下,会出现这个服务调用这个接口,那个接口的情况。假设出了问题,需要排查的时候,我们要怎么关联不同服务之间的调用情况呢?换句话就是说,这个请求的结果不对,看看是那里出了问题。
最简单的思路应该就是请求头加一个标识,从头贯穿到尾,这样我们就可以知道,对于这一个请求,在不同的服务都经历了什么样的过程。
在.NET Core时代,相信大部分都是在用HttpClientFactory来创建HttpClient,然后在发起请求。
这篇短文就简单介绍一下如何实现。
示例
我们先定义一个自己的DelegatingHandler,这里取名为HeadersPropagationDelegatingHandler
代码如下:
public class HeadersPropagationDelegatingHandler : DelegatingHandler
{
private readonly IHttpContextAccessor _accessor;
public HeadersPropagationDelegatingHandler(IHttpContextAccessor accessor)
{
_accessor = accessor;
}
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
var traceId = string.Empty;
if (_accessor.HttpContext.Request.Headers.TryGetValue("traceId", out var tId))
{
traceId = tId.ToString();
Console.WriteLine($"{traceId} from request {DateTimeOffset.UtcNow.ToUnixTimeMilliseconds()}.");
}
else
{
traceId = System.Guid.NewGuid().ToString("N");
_accessor.HttpContext.Request.Headers.Add("traceId", new Microsoft.Extensions.Primitives.StringValues(traceId));
Console.WriteLine($"{traceId} from generated {DateTimeOffset.UtcNow.ToUnixTimeMilliseconds()}.");
}
if (!request.Headers.Contains("trace-id"))
{
request.Headers.TryAddWithoutValidation("traceId", traceId);
}
return await base.SendAsync(request, cancellationToken);
}
}
应该不用太多解释,就是在HttpClient发起请求之前,给它加多一个请求头,这个请求头的值要么是从上一个请求的请求头中取,要么就是重新生成一个。
下面就是主角IHttpMessageHandlerBuilderFilter出场了,它只是一个接口,我们需要自己去实现里面的Configure。
简单的示例如下:
public class HeadersPropagationMessageHandlerBuilderFilter : IHttpMessageHandlerBuilderFilter
{
private readonly IHttpContextAccessor httpContextAccessor;
public HeadersPropagationMessageHandlerBuilderFilter(IHttpContextAccessor httpContextAccessor)
{
this.httpContextAccessor = httpContextAccessor;
}
public Action<HttpMessageHandlerBuilder> Configure(Action<HttpMessageHandlerBuilder> next)
{
if (next == null)
{
throw new ArgumentNullException(nameof(next));
}
return (builder) =>
{
next(builder);
builder.AdditionalHandlers.Add(new HeadersPropagationDelegatingHandler(httpContextAccessor));
};
}
}
万事具备,下面我们只需要在Startup中进行注入即可。
public void ConfigureServices(IServiceCollection services)
{
services.AddHttpContextAccessor();
services.AddTransient<Ext.HeadersPropagationDelegatingHandler>();
services.AddSingleton<IHttpMessageHandlerBuilderFilter, Ext.HeadersPropagationMessageHandlerBuilderFilter>();
services.AddHttpClient();
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
}
最后就是调用看看效果,这里为了简单,选择创建多个路由,用路由间发起HTTP请求来模拟。当然,最好的还是多个项目模拟。
示例如下:
[Route("api/[controller]")]
[ApiController]
public class ValuesController : ControllerBase
{
private readonly IHttpClientFactory _clientFactory;
public ValuesController(IHttpClientFactory clientFactory)
{
this._clientFactory = clientFactory;
}
// GET api/values
[HttpGet]
public async Task<string> GetAsync()
{
var traceId = string.Empty;
if (Request.Headers.TryGetValue("traceId", out var tId))
{
traceId = tId.ToString();
Console.WriteLine($"{traceId} from request {DateTimeOffset.UtcNow.ToUnixTimeMilliseconds()}.");
}
else
{
traceId = System.Guid.NewGuid().ToString("N");
Request.Headers.Add("traceId", new Microsoft.Extensions.Primitives.StringValues(traceId));
Console.WriteLine($"{traceId} from generated {DateTimeOffset.UtcNow.ToUnixTimeMilliseconds()}.");
}
using (HttpClient client = new HttpClient())
{
client.DefaultRequestHeaders.Clear();
client.DefaultRequestHeaders.TryAddWithoutValidation("traceId", traceId);
var res = await client.GetAsync("http://localhost:9898/api/values/demo1");
var str = await res.Content.ReadAsStringAsync();
Console.WriteLine($"{traceId} demo1 return {str} at {DateTimeOffset.UtcNow.ToUnixTimeMilliseconds()}");
return str;
}
}
// GET api/values/demo1
[HttpGet("demo1")]
public async Task<string> GetDemo1()
{
var client = _clientFactory.CreateClient("demo2");
var res = await client.GetAsync("http://localhost:9898/api/values/demo2");
var str = await res.Content.ReadAsStringAsync();
return str;
}
// GET api/values/demo2
[HttpGet("demo2")]
public async Task<string> GetDemo2()
{
var client = _clientFactory.CreateClient("demo3");
var res = await client.GetAsync("http://localhost:9898/api/values/demo3");
var str = await res.Content.ReadAsStringAsync();
return str;
}
// GET api/values/demo3
[HttpGet("demo3")]
public ActionResult<string> GetDemo3()
{
return "demo3";
}
// GET api/values/demo4
[HttpGet("demo4")]
public async Task<string> GetDemo4()
{
var client = _clientFactory.CreateClient("demo1");
var res = await client.GetAsync("http://localhost:9898/api/values/demo3");
var str = await res.Content.ReadAsStringAsync();
var traceId = string.Empty;
if (Request.Headers.TryGetValue("traceId", out var tId)) traceId = tId.ToString();
Console.WriteLine($"{traceId} demo3 return {str} at {DateTimeOffset.UtcNow.ToUnixTimeMilliseconds()}");
return str;
}
}
先访问 api/values 再访问 api/values/demo4 可以看到下面的结果。

可以看到用传统的方法和用HttpClientFactory都达到了一样的效果。
给HttpClient添加请求头(HttpClientFactory)的更多相关文章
- urllib2 post请求方式,带cookie,添加请求头
#encoding = utf-8 import urllib2import urllib url = 'http://httpbin.org/post'data={"name": ...
- springcloud- FeginClient 调用统一拦截添加请求头 RequestInterceptor ,被调用服务获取请求头
使用场景: 在springcloud中通过Fegin调用远端RestApi的时候,经常需要传递一些参数信息到被调用服务中去,比如从A服务调用B服务的时候, 需要将当前用户信息传递到B调用的服务中去,我 ...
- iOS UIWebview添加请求头的两种方式
1.在UIWebviewDelegate的方法中拦截request,设置request的请求头,废话不多说看代码: - (BOOL)webView:(UIWebView *)webView shoul ...
- WKWebView单个界面添加请求头
https://www.jianshu.com/p/14b9ea4bf1d4 https://github.com/Yeatse/NSURLProtocol-WebKitSupport/blob/ma ...
- LoadRunner11脚本小技能之添加请求头+定义变量+响应内容乱码转换打印+事务拆分
一.添加请求头 存在一些接口,发送请求时需要进行权限验证.登录验证(不加请求头时运行脚本,接口可能会报401等等),所以需要在脚本中给对应请求添加请求头.注意:请求头需在请求前添加,包含url类.su ...
- Retrofit2 动态(静态)添加请求头Header
Retrofit提供了两个两种定义HTTP请求头字段的方法即静态和动态.静态头不能改变为不同的请求,头的键和值是固定的且不可改变的,随着程序的打开便已固定. 动态添加 @GET("/&quo ...
- python爬虫添加请求头和请求主体
添加头部信息有两种方法 1.通过添加urllib.request.Request中的headers参数 #先把要用到的信息放到一个字典中 headers = {} headers['User-Agen ...
- 给requests模块添加请求头列表和代理ip列表
Requests 是使用 Apache2 Licensed 许可证的 基于Python开发的HTTP 库,其在Python内置模块的基础上进行了高度的封装,符合了Python语言的思想,通俗的说去繁存 ...
- ajax添加请求头(添加Authorization字段)
我们在发AJAX请求的时候可能会需要自定义请求头,在jQuery的$.ajax()方法中提供了beforeSend属性方便我们进行此操作. beforeSend: function(request) ...
随机推荐
- IDEA快捷键用法
1.Ctrl+滑动滚轮调节窗口显示大小(需要设置之后方可) File->Settings->Editor->General->Change fontsize with Ctrl ...
- maxwell实时同步mysql中binlog
概述 Maxwell是一个能实时读取MySQL二进制日志binlog,并生成 JSON 格式的消息,作为生产者发送给 Kafka,Kinesis.RabbitMQ.Redis.Google Cloud ...
- Python深拷贝与浅拷贝区别
可变类型 如list.dict等类型,改变容器内的值,容器地址不变. 不可变类型 如元组.字符串,原则上不可改变值.如果要改变对象的值,是将对象指向的地址改变了 浅拷贝 对于可变对象来说,开辟新的内存 ...
- (三十六)c#Winform自定义控件-步骤控件-HZHControls
官网 http://www.hzhcontrols.com 前提 入行已经7,8年了,一直想做一套漂亮点的自定义控件,于是就有了本系列文章. GitHub:https://github.com/kww ...
- url编码本质
url编码本质 其实url本质就是将中文字符串进行utf8编码,然后得到编码后的对象转换字符串去掉开头的b'以及末尾的',然后再将\x转换成%,再将里面内容x变成e最后将字符串小写变成大写 举例 #拿 ...
- JMeter内存溢出:java.lang.OutOfMemoryError: Java heap space解决方法
一.问题原因 用JMeter压测,有时候当模拟并发请求较大或者脚本运行时间较长时,JMeter会停止,报OOM(内存溢出)错误. 原因是JMeter是一个纯Java开发的工具,内存由java虚拟机JV ...
- .netcore2.1 JS-SDK 从后台获取微信签名,实现自定义分享标题、描述、图片
最近项目移动端需要实现微信自定义分享功能,包含分享自定义标题.描述等. 首先到公众号的后台,功能设置里面,添加将要被分享的域名,如图 后端签名算法实现 ,参考腾讯开发者文档https://mp.wei ...
- windows 应急流程及实战演练
前言 本文摘自信安之路公众号的文章. 当企业发生黑客入侵.系统崩溃或其它影响业务正常运行的安全事件时,急需第一时间进行处理,使企业的网络信息系统在最短时间内恢复正常工作,进一步查找入侵来源,还原入侵事 ...
- Android 上下文菜单 PopupMenu
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); s ...
- linux中OTG识别到一个U盘后产生一个sg节点的全过程
注:本篇文章暂时不做流程图,如果有需求后续补做. 1. 需要准备的源码文件列表: base部分: kernel\base\core.c kernel\base\bus.c kernel\base\dd ...