前言

在微服务的大环境下,会出现这个服务调用这个接口,那个接口的情况。假设出了问题,需要排查的时候,我们要怎么关联不同服务之间的调用情况呢?换句话就是说,这个请求的结果不对,看看是那里出了问题。

最简单的思路应该就是请求头加一个标识,从头贯穿到尾,这样我们就可以知道,对于这一个请求,在不同的服务都经历了什么样的过程。

在.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)的更多相关文章

  1. urllib2 post请求方式,带cookie,添加请求头

    #encoding = utf-8 import urllib2import urllib url = 'http://httpbin.org/post'data={"name": ...

  2. springcloud- FeginClient 调用统一拦截添加请求头 RequestInterceptor ,被调用服务获取请求头

    使用场景: 在springcloud中通过Fegin调用远端RestApi的时候,经常需要传递一些参数信息到被调用服务中去,比如从A服务调用B服务的时候, 需要将当前用户信息传递到B调用的服务中去,我 ...

  3. iOS UIWebview添加请求头的两种方式

    1.在UIWebviewDelegate的方法中拦截request,设置request的请求头,废话不多说看代码: - (BOOL)webView:(UIWebView *)webView shoul ...

  4. WKWebView单个界面添加请求头

    https://www.jianshu.com/p/14b9ea4bf1d4 https://github.com/Yeatse/NSURLProtocol-WebKitSupport/blob/ma ...

  5. LoadRunner11脚本小技能之添加请求头+定义变量+响应内容乱码转换打印+事务拆分

    一.添加请求头 存在一些接口,发送请求时需要进行权限验证.登录验证(不加请求头时运行脚本,接口可能会报401等等),所以需要在脚本中给对应请求添加请求头.注意:请求头需在请求前添加,包含url类.su ...

  6. Retrofit2 动态(静态)添加请求头Header

    Retrofit提供了两个两种定义HTTP请求头字段的方法即静态和动态.静态头不能改变为不同的请求,头的键和值是固定的且不可改变的,随着程序的打开便已固定. 动态添加 @GET("/&quo ...

  7. python爬虫添加请求头和请求主体

    添加头部信息有两种方法 1.通过添加urllib.request.Request中的headers参数 #先把要用到的信息放到一个字典中 headers = {} headers['User-Agen ...

  8. 给requests模块添加请求头列表和代理ip列表

    Requests 是使用 Apache2 Licensed 许可证的 基于Python开发的HTTP 库,其在Python内置模块的基础上进行了高度的封装,符合了Python语言的思想,通俗的说去繁存 ...

  9. ajax添加请求头(添加Authorization字段)

    我们在发AJAX请求的时候可能会需要自定义请求头,在jQuery的$.ajax()方法中提供了beforeSend属性方便我们进行此操作. beforeSend: function(request) ...

随机推荐

  1. java基础 - 形参和实参,值传递和引用传递

    形参和实参 形参:就是形式参数,用于定义方法的时候使用的参数,是用来接收调用者传递的参数的. 形参只有在方法被调用的时候,虚拟机才会分配内存单元,在方法调用结束之后便会释放所分配的内存单元. 因此,形 ...

  2. Java.awt.geom.AffineTransform 的使用

    https://docs.oracle.com/javase/8/docs/api/java/awt/geom/AffineTransform.html http://www.cjsdn.net/Do ...

  3. hibernate opensission.createSQLquery 问题

    在进行分页查询的时候,通常会用到,页码,每页容量等等的参数进行操作,如下图: query.list()的时候会执行得到所需要的集合的值,在这个过程中, 如果使用自定义的返回参数,比如将日期做一些基本处 ...

  4. java之工厂方法设计模式

    工厂方法模式是设计模式中应用最广泛的模式.在面向对象的编程中,对象的创建工作非常简单,对象的创建时机却很重要.工厂方法模式就是解决这个问题,它通过面向对象的手法,将所要创建的具体对象创建工作延迟到了子 ...

  5. 磕磕绊绊中,使用Git工具完成代码上传

    1.安装Git工具 1)下载并安装Git工具:Git下载地址 安装完成之后,在桌面空白处点击右键,会出现以下选项: 2.初始化环境 1) 在一文件夹中,点击右键,选择上图中的Git Bash Here ...

  6. mybatis无效比较:invalid comparison:java.util.data and java.lang.string

    原因: 时间与空字符串比较是无效的,如果拿传入的时间类型参数与空字符串''进行对比则会引发invalid comparison:java.util.data and java.lang.string异 ...

  7. Java入门系列之类继承、抽象类、接口(五)

    前言 C#和Java关于类.抽象类.接口使用方式基本相似,只是对应关键字使用不同罢了,本节呢,我们只是对照C#和Java中关于这三个概念在具体使用时,看看有哪些不一样的地方. 类继承 C#和Java在 ...

  8. 一起学Android之ContentProvider

    本文主要讲解在Android开发中ContentProvider的常规用法,仅供学习分享使用,如有不足之处,还请指正. 访问一个ContentProvider 在Android开发中,应用程序通过Co ...

  9. windows下的go get 显示进度

    我的Go版本是:go1.12.7 1.在你的Go安装目录下找到 D:\Go\src\github.com\tools\godep\vendor\golang.org\x\tools\go\vcs\vs ...

  10. Winform中自定义添加ZedGraph右键实现设置所有Y轴刻度的上下限

    场景 Winforn中实现ZedGraph自定义添加右键菜单项(附源码下载): https://blog.csdn.net/BADAO_LIUMANG_QIZHI/article/details/10 ...