首页 > 其他分享 >自动转发接收的请求报头

自动转发接收的请求报头

时间:2023-08-12 22:46:52浏览次数:34  
标签:调用 请求 using 转发 报头 接收 httpClient HttpClient

如何自动转发接收的请求报头?

 

了解OpenTelemetry的朋友应该知道,为了将率属于同一个请求的多个操作(Span)串起来,上游应用会生成一个唯一的TraceId。在进行跨应用的Web调用时,这个TraceId和代表跟踪操作标识的SpanID一并发给目标应用,W3C还专门指定了一份名为Trace Context的标准,该标准确定了一个名为trace-parent的请求报头来传递TraceId、(Parent)SpanID以及其他两个跟踪属性。其实我们的应用也可能会使用到分布式跟踪这种类似的功能,我们需要在某个应用中添加一些“埋点”,当它调用另一个应用时,这些埋点会自动添加到请求的报头集合中,从而实现在整个调用链中自动传递。为了实现这个功能,我创建了一个名为HeaderForwarder(Github)的框架。本文不会介绍HeaderForwarder的设计,仅仅介绍它的使用方式,有兴趣的朋友可以查看源代码。

一、 请求报头的自动转发
二、 屏蔽自动转发功能
三、 为请求添加请求报头
四、 同名报头的处理
五、 屏蔽“外部”添加的请求报头

一、 请求报头的自动转发

我们创建App1、App2和App3三个应用,ASP.NET Core应用App2和App3以路由的形式提供一个简单的API,App1则是一个简单的控制台应用。App1利用HttpClient调用App2承载的API,后者进一步调用App3。我们让处于中间的App2安装HeaderForwarder。如下所示的是控制台应用App1的定义。我们利用创建的HttpClient调用App2承载的API,发送的请求中人为添加了名为 “foo” 、“bar” 和 “baz” 的三个报头。

复制代码
var request = new HttpRequestMessage(HttpMethod.Get, "http://localhost:5000/test");
request.Headers.Add("foo", "123");
request.Headers.Add("bar", "456");
request.Headers.Add("baz", "789");
using (var httpClient = new HttpClient())
{
    await httpClient.SendAsync(request);
}
复制代码

App2定义如下。HeaderForwarder设计的服务通过调用IServiceCollection接口的AddHeaderForwarder进行注册,该方法中同时指定了需要自动转发的报头名称 “foo” 和 “bar” (不区分大小写)。后面调用AddHttpClient扩展方法是为了使用注入的IHttpClientFactory对象所需的HttpClient对象。

复制代码
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddHeaderForwarder("foo", "bar").AddHttpClient();
var app = builder.Build();
app.MapGet("/test", async (HttpRequest request, IHttpClientFactory httpClientFactory) =>
{
    foreach (var kv in request.Headers)
    {
        Console.WriteLine($"{kv.Key}:{kv.Value}");
    }
    await httpClientFactory.CreateClient().GetAsync("http://localhost:5001/test");
});
app.Run("http://localhost:5000");
复制代码

App1调用的API体现为针对路径 “/test” 注册的路由。路由处理程序会再控制台上输出接收到的所有请求报头,并在此之后利用IHttpClientFactory对象创建的HttpClient完成针对App3的调用。App3提供的API仅仅按照如下的方式将接收到的请求报头输出到控制台上。

复制代码
var app = WebApplication.CreateBuilder(args).Build();
app.MapGet("/test",  (HttpRequest request) =>
{
    foreach (var kv in request.Headers)
    {
        Console.WriteLine($"{kv.Key}:{kv.Value}");
    }
});
app.Run("http://localhost:5001");
复制代码

三个应用先后启动后,App1调用App2添加的三个请求报头(“foo” 、 “bar” 和 “baz”)会出现在App2的控制台上。HeaderForwarder只会自动转发指定的请求报头“foo” 和“bar” ,所有只有这两个报头会出现在App3的控制台上。从图中还可以看到,默认由HttpClientFactory创建的HttpClient的调用添加和转发用于分布式跟踪的traceparent报头。

clip_image002

二、 屏蔽自动转发功能

HeaderForwarder能够获得当前的HttpContext上下文,并提取并转发所需的请求报头。如果App2在调用App3的时候并不希望将报头转发出去,可以按照如下的方式注入IOutgoingHeaderProcessor对象,并调用其SuppressHeaderForwarder方法将报头自动转发功能屏蔽掉。

复制代码
using HeaderForwarder;

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddHeaderForwarder("foo", "bar").AddHttpClient();
var app = builder.Build();
app.MapGet("/test", async (IHttpClientFactory httpClientFactory,IOutgoingHeaderProcessor processor ) =>
{
    using (processor.SuppressHeaderForwarder())
    {
        await httpClientFactory.CreateClient().GetAsync("http://localhost:5001/test");
    }
});
app.Run("http://localhost:5000");
复制代码

SuppressHeaderForwarder利用返回的IDisposable对象代表“屏蔽上下文”,意味着该创建的“屏障”会在其Dispose方法后失效,所以App2在此上下文中完成针对App3的调用,它接收的请求报头“foo” 和“bar”并不会被转发出去。

clip_image004

三、 为请求添加请求报头

当我们利用HttpClient进行Web调用时,如果需要认为地添加报头,典型的做法就是按照App1异常创建一个HttpRequestMessage对象,并将需要的报头以键值对的形式添加到它的Headers属性中。HeaderForwarder提供了一种更加快捷易用的编程模式。

var processor = OutgoingHeaderProcessor.Create();
using(var httpClient = new HttpClient())
using (processor.AddHeaders(("foo", "123"), ("bar", "456"), ("baz", "789")))
await httpClient.GetAsync("http://localhost:5000/test");

如上面的代码片段所示,我们调用OutgoingHeaderProcessor类型的静态方法Create创建了一个IOutgoingHeaderProcessor对象,并调用其AddHeaders完成了三个请求报头的添加。这个方法同样返回一个通过IDisposable对象表示的执行上下文,在此上下文中针对HttpClient的调用生成的请求均会自动附加这三个报头。

四、 同名报头的处理

由于IOutgoingHeaderProcessor接口的AddHeaders方法返回的时一个IDisposable对象表示的上下文,意味着上下文之间可能出现嵌套的关系。在默认情况下,如果HttpClient在这样一个嵌套的上下文中被使用,这些上下文携带的请求报头都将被转发。一般来说,这种情况正是我们希望的,但是如果我们在一个具有嵌套关系的多个上下文中添加了多个同名的报头,就有可能出现我们不愿看到的结果。

复制代码
using HeaderForwarder;

var processor = OutgoingHeaderProcessor.Create();
using(var httpClient = new HttpClient())
await FooAsync(httpClient);

async Task FooAsync(HttpClient httpClient)
{
    using (processor.AddHeaders(("foobarbaz", "abc")))
    await BarAsync(httpClient);
}
async Task BarAsync(HttpClient httpClient)
{
    using (processor.AddHeaders(("foobarbaz", "abc")))
    await BazAsync(httpClient);
}
async Task BazAsync(HttpClient httpClient)
{
    using (processor.AddHeaders(("foobarbaz", "abc")))
    await httpClient.GetAsync("http://localhost:5000/test");
}
复制代码

如上面的代码所示,三个嵌套调用的方法FooAsync、BarAsync和BazAsync采用相同的方式调用IOutgoingHeaderProcessor对象的AddHeaders方法添加相同的请求报头“foobarbaz”。意味着在BazAsync方法针对HttpClient的调用会在三个嵌套的上下文中进行,这意味着App2会接收到三个同名的请求报头。

clip_image006

如果不希望出现这种情况下,可以将针对AddHeaders方法的调用按照如下的方式替换成ReplaceHeaders。

复制代码
async Task FooAsync(HttpClient httpClient)
{
    using (processor.ReplaceHeaders(("foobarbaz", "abc")))
    await BarAsync(httpClient);
}
async Task BarAsync(HttpClient httpClient)
{
    using (processor.ReplaceHeaders(("foobarbaz", "abc")))
    await BazAsync(httpClient);
}
async Task BazAsync(HttpClient httpClient)
{
    using (processor.ReplaceHeaders(("foobarbaz", "abc")))
    await httpClient.GetAsync("http://localhost:5000/test");
}
复制代码

五、 屏蔽“外部”添加的请求报头

如果不愿意收到嵌套的“外部”上下文的干扰,我们可以调用IOutgoingHeaderProcessor接口的AddHeadersAfterClear方法。顾名思义,这个方法在添加指定请求报头之前,会先将现有的报头清除。

复制代码
var processor = OutgoingHeaderProcessor.Create();
using(var httpClient = new HttpClient())
await FooAsync(httpClient);

async Task FooAsync(HttpClient httpClient)
{
    using (processor.AddHeadersAfterClear(("foo", "123")))
    await BarAsync(httpClient);
}
async Task BarAsync(HttpClient httpClient)
{
    using (processor.AddHeadersAfterClear(("barbaz", "456")))
    await BazAsync(httpClient);
}
async Task BazAsync(HttpClient httpClient)
{
    using (processor.AddHeadersAfterClear(("barbaz", "789")))
    await httpClient.GetAsync("http://localhost:5000/test");
}
复制代码

如上面的代码片段所示,FooAsync调用AddHeadersAfterClear方法添加了一个名为“foo”的报头,BarAsync和BazAsync则采用相同的方式添加了两个同名的请求报头“Barbaz”。App2只会接收到由BazAsync设置的报头。

clip_image008

AddHeadersAfterClear针对现有报头的清除只会体现在它创建的上下文中,当前上下文并不会收到影响。因为该方法根本没有做任何清除工作,而是创建一个全新的上下文。AddHeaders和ReplaceHeaders方法其实重用了外部的上下文。

标签:调用,请求,using,转发,报头,接收,httpClient,HttpClient
From: https://www.cnblogs.com/Leo_wl/p/17625709.html

相关文章

  • oracle归档日志暴增原因分析,Oracle归档日志满导致数据库性能异常慢 转发 https://b
    ============= oracle数据库archivelog暴增分析====================前言归档量突然增长到981G/天,导致归档目录使用率告警归档日志量异常暴增会导致磁盘空间爆满,数据库异常1、归档日志量统计SELECTTRUNC(FIRST_TIME)"TIME",SUM(BLOCK_SIZE*BLOCKS)/1024/1024/102......
  • 设置 X11 转发以在 Linux 中访问 GUI
    一、概述X11转发是一种在客户端和服务器之间传输图形界面的协议。它允许远程客户端在本地显示远程服务器上的图形应用程序,使用户可以在本地操作远程服务器上的图形界面。使用场景:远程服务器管理:管理员可以通过X11转发在本地管理远程服务器上的图形化工具和应用程序,而无需直接......
  • 华大 HC32F460 CAN 同时接收到两帧数据,导致后面的数据错位问题
    我在调试项目的时候,分为1个主机与2个从机,通过CAN进行通信,起初调的时候好好地,等将功能全部调完之后,整体断电,之后在上电,发现主机CAN通讯错乱,也就是接收的数据不对,比较混乱,之后我寻找问题发现2个从机上电之后会给主机发送CAN数据,我就怀疑是不是这个原因造成的CAN通讯混乱,之后我给屏......
  • 微信 H5 页面兼容性——转发给朋友
    微信公众号页面提供了转发给朋友的功能,如果没有设置隐藏“发送给朋友”,就可以转发给朋友。wx.hideMenuItems({menuList:['menuItem:share:appMessage'//发送给朋友],//要隐藏的菜单项});转发给朋友后,朋友可能会看到三种不同的分享内容。1.分享内容1.1.微......
  • 路由表(RIB)与转发表(FIB)
    路由表被称为路由信息库(RoutingInformationBase,RIB),转发表也叫转发信息库(ForwardingInformationbase,FIB)具备路由功能的华为数通设备(路由器、三层交换机等),都维护着两种重要的数据表:路由表(RIB)、转发表(FIB)。RIB路由表路由表通常存储在设备的动态内存中,如RAM随机存储器中,每台路由......
  • go kratos protobuf 接收动态JSON数据
    前言google.protobuf.Struct是GoogleProtocolBuffers中的一种特殊类型,用于表示动态的键值对数据。它可以存储任意类型的数据,并提供了方便的方法来访问和操作这些数据。Struct类型通常用于在不事先知道数据结构的情况下传递和处理配置、参数或其他动态数据。https://pkg.g......
  • 2023-2029全球FTA接收器行业调研及趋势分析报告
     2022年全球FTA接收器市场规模约亿元,2018-2022年年复合增长率CAGR约为%,预计未来将持续保持平稳增长的态势,到2029年市场规模将接近亿元,未来六年CAGR为%。从核心市场看,中国FTA接收器市场占据全球约%的市场份额,为全球最主要的消费市场之一,且增速高于全球。2022年市场规模约亿元......
  • Nginx 转发请求的时候,丢失请求头
    请求连接:curl-H"Content-Type:application/json;charset=utf-8"-H"channel_id:21520160723000110027"-H"sequenceid:1122"http://127.0.0.1:1095/app/api/gqzr/swxxcx-XPOST-d'{"shxydm":"911500002115000009D&quo......
  • 国标GB28181视频平台LntonGBS(源码版)国标平台级联时,通道上传上级宇视平台无法接收的问
    LntonGBS是基于公安部推出的GB/T28181协议开发的视频平台,在安防监控领域应用广泛。下面是一些关于LntonGBS平台的主要特点:GB/T28181协议兼容性、视频直播和转码、云端录像和存储、语音对讲和警告功能、平台级联功能。通过以上的功能和特点,LntonGBS平台能够满足安防监控领域各类场景......
  • k8s---使用ingress配置域名转发时的traefik路径规则详解
    ingress中traefik的使用方式如下:apiVersion:extensions/v1beta1kind:Ingressmetadata:name:spark-client-testnamespace:defaultannotations:kubernetes.io/ingress.class:traefiktraefik.frontend.rule.type:PathPrefixspec:rules:-host:......