天天看点

给HttpClient添加请求头(HttpClientFactory)

给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)

如果您认为这篇文章还不错或者有所收获,可以点击右下角的【推荐】按钮,因为你的支持是我继续写作,分享的最大动力!

作者:Catcher Wong ( 黄文清 )

来源:http://catcher1994.cnblogs.com/

声明:

本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。如果您发现博客中出现了错误,或者有更好的建议、想法,请及时与我联系!!如果想找我私下交流,可以私信或者加我微信。

继续阅读