.net core HttpClient 使用之消息管道解析(二)
2021-02-17 13:20
标签:des col https connect next demo invoke 动态加载 art
前面分享了 .net core HttpClient 使用之掉坑解析(一),今天来分享自定义消息处理 先贴上一张核心MessageHandler 管道模型的流程图,图如下: 再阅读源代码的时候我们先来看下下面注入 上面代码中有两个核心扩展方法,分别是 我们来看看 代码中把自定义的一、前言
HttpMessageHandler
和PrimaryHttpMessageHandler
的使用场景和区别二、源代码阅读
2.1 核心消息管道模型图
HttpClient 中的HttpMessageHandler
负责主要核心的业务,HttpMessageHandler
是由MessageHandler 链表结构组成,形成一个消息管道模式;具体我们一起来看看源代码2.2 Demo代码演示
HttpClient
的Demo 代码,代码如下:services.AddHttpClient("test")
.ConfigurePrimaryHttpMessageHandler(provider =>
{
return new PrimaryHttpMessageHandler(provider);
})
.AddHttpMessageHandler(provider =>
{
return new LogHttpMessageHandler(provider);
})
.AddHttpMessageHandler(provider =>
{
return new Log2HttpMessageHandler(provider);
});
ConfigurePrimaryHttpMessageHandler
和AddHttpMessageHandler
,这两个方法大家可能会有疑问是做什么的呢?
不错,这两个方法就是扩展注册自定义的HttpMessageHandler
如果不注册,会有默认的HttpMessageHandler
,接下来我们分别来看下提供的扩展方法,如下图:
图中提供了一系列的AddHttpMessageHandler
扩展方法和ConfigurePrimaryHttpMessageHandler
的扩展方法。2.3
AddHttpMessageHandler
HttpClientBuilderExtensions
中的其中一个AddHttpMessageHandler
扩展方法,代码如下: ///
DelegatingHandler
方法添加到HttpMessageHandlerBuilderActions
中,我们再来看看HttpClientFactoryOptions
对象源代码,如下:
new ArgumentException(Resources.HandlerLifetime_InvalidValue, nameof(value));
}
_handlerLifetime = value;
}
}
/// ///
源代码中有如下核心List:
public IList> HttpMessageHandlerBuilderActions { get; } = new List>();
提供了HttpMessageHandlerBuilder
HttpMessageHandler 的构造器列表对象,故,通过AddHttpMessageHandler
可以添加一系列的消息构造器方法对象
我们再来看看这个消息构造器类,核心部分,代码如下:
public abstract class HttpMessageHandlerBuilder
{
///
/// Gets or sets the name of the being created.
///
///
/// The is set by the infrastructure
/// and is public for unit testing purposes only. Setting the outside of
/// testing scenarios may have unpredictable results.
///
public abstract string Name { get; set; }
///
/// Gets or sets the primary .
///
public abstract HttpMessageHandler PrimaryHandler { get; set; }
///
/// Gets a list of additional instances used to configure an
/// pipeline.
///
public abstract IList AdditionalHandlers { get; }
///
/// Gets an which can be used to resolve services
/// from the dependency injection container.
///
///
/// This property is sensitive to the value of
/// . If true this
/// property will be a reference to the application‘s root service provider. If false
/// (default) this will be a reference to a scoped service provider that has the same
/// lifetime as the handler being created.
///
public virtual IServiceProvider Services { get; }
///
/// Creates an .
///
///
/// An built from the and
/// .
///
public abstract HttpMessageHandler Build();
protected internal static HttpMessageHandler CreateHandlerPipeline(HttpMessageHandler primaryHandler, IEnumerable additionalHandlers )
{
// This is similar to https://github.com/aspnet/AspNetWebStack/blob/master/src/System.Net.Http.Formatting/HttpClientFactory.cs#L58
// but we don‘t want to take that package as a dependency.
if (primaryHandler == null)
{
throw new ArgumentNullException(nameof(primaryHandler));
}
if (additionalHandlers == null)
{
throw new ArgumentNullException(nameof(additionalHandlers));
}
var additionalHandlersList = additionalHandlers as IReadOnlyList ?? additionalHandlers.ToArray();
var next = primaryHandler;
for (var i = additionalHandlersList.Count - 1; i >= 0; i--)
{
var handler = additionalHandlersList[i];
if (handler == null)
{
var message = Resources.FormatHttpMessageHandlerBuilder_AdditionalHandlerIsNull(nameof(additionalHandlers));
throw new InvalidOperationException(message);
}
// Checking for this allows us to catch cases where someone has tried to re-use a handler. That really won‘t
// work the way you want and it can be tricky for callers to figure out.
if (handler.InnerHandler != null)
{
var message = Resources.FormatHttpMessageHandlerBuilder_AdditionHandlerIsInvalid(
nameof(DelegatingHandler.InnerHandler),
nameof(DelegatingHandler),
nameof(HttpMessageHandlerBuilder),
Environment.NewLine,
handler);
throw new InvalidOperationException(message);
}
handler.InnerHandler = next;
next = handler;
}
return next;
}
}
HttpMessageHandlerBuilder
构造器中有两个核心属性PrimaryHandler
和AdditionalHandlers
,细心的同学可以发现AdditionalHandlers
是一个IList
列表,也就是说可以HttpClient 可以添加多个DelegatingHandler
即多个HttpMessageHandler
消息处理Handler 但是只能有一个PrimaryHandler
Handler
同时HttpMessageHandlerBuilder
提供了一个抽象的Build
方法,还有一个CreateHandlerPipeline
方法,这个方法主要是把IList
和PrimaryHandler
构造成一个MessageHandler 链表结构(通过DelegatingHandler
的InnerHandler
属性进行连接起来)
2.4 ConfigurePrimaryHttpMessageHandler
public static IHttpClientBuilder ConfigurePrimaryHttpMessageHandler(this IHttpClientBuilder builder, Func configureHandler )
{
if (builder == null)
{
throw new ArgumentNullException(nameof(builder));
}
if (configureHandler == null)
{
throw new ArgumentNullException(nameof(configureHandler));
}
builder.Services.Configure(builder.Name, options =>
{
options.HttpMessageHandlerBuilderActions.Add(b => b.PrimaryHandler = configureHandler());
});
return builder;
}
通过上面的HttpMessageHandlerBuilder
源代码分析ConfigurePrimaryHttpMessageHandler
方法主要是给Builder 中添加PrimaryHandler
消息Handler
2.5 DefaultHttpMessageHandlerBuilder
我们知道在services.AddHttpClient()
方法中会注册默认的DefaultHttpMessageHandlerBuilder
消息构造器方法,它继承DefaultHttpMessageHandlerBuilder
,那我们来看看它的源代码
internal class DefaultHttpMessageHandlerBuilder : HttpMessageHandlerBuilder
{
public DefaultHttpMessageHandlerBuilder(IServiceProvider services)
{
Services = services;
}
private string _name;
public override string Name
{
get => _name;
set
{
if (value == null)
{
throw new ArgumentNullException(nameof(value));
}
_name = value;
}
}
public override HttpMessageHandler PrimaryHandler { get; set; } = new HttpClientHandler();
public override IList AdditionalHandlers { get; } = new List();
public override IServiceProvider Services { get; }
public override HttpMessageHandler Build()
{
if (PrimaryHandler == null)
{
var message = Resources.FormatHttpMessageHandlerBuilder_PrimaryHandlerIsNull(nameof(PrimaryHandler));
throw new InvalidOperationException(message);
}
return CreateHandlerPipeline(PrimaryHandler, AdditionalHandlers);
}
代码中Build
会去调用HttpMessageHandlerBuilder 的CreateHandlerPipeline
方法把HttpMessageHandler 构建成一个类似于链表的结构。
到这里源代码已经分析完了,接下来我们来演示一个Demo,来证明上面的核心HttpMessageHandler 流程走向图
三、Demo演示证明
我们继续来看上面我的Demo代码:
services.AddHttpClient("test")
.ConfigurePrimaryHttpMessageHandler(provider =>
{
return new PrimaryHttpMessageHandler(provider);
})
.AddHttpMessageHandler(provider =>
{
return new LogHttpMessageHandler(provider);
})
.AddHttpMessageHandler(provider =>
{
return new Log2HttpMessageHandler(provider);
});
代码中自定义了两个HttpMessageHandler
和一个PrimaryHttpMessageHandler
我们再来分别看看Log2HttpMessageHandler
、LogHttpMessageHandler
和PrimaryHttpMessageHandler
代码,代码很简单就是SendAsync
前后输出了Log信息,代码如下:
自定义的PrimaryHttpMessageHandler
代码如下:
public class PrimaryHttpMessageHandler: DelegatingHandler
{
private IServiceProvider _provider;
public PrimaryHttpMessageHandler(IServiceProvider provider)
{
_provider = provider;
InnerHandler = new HttpClientHandler();
}
protected async override TaskSendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
System.Console.WriteLine("PrimaryHttpMessageHandler Start Log");
var response= await base.SendAsync(request, cancellationToken);
System.Console.WriteLine("PrimaryHttpMessageHandler End Log");
return response;
}
}
Log2HttpMessageHandler
代码如下:
public class Log2HttpMessageHandler : DelegatingHandler
{
private IServiceProvider _provider;
public Log2HttpMessageHandler(IServiceProvider provider)
{
_provider = provider;
//InnerHandler = new HttpClientHandler();
}
protected async override TaskSendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
System.Console.WriteLine("LogHttpMessageHandler2 Start Log");
var response=await base.SendAsync(request, cancellationToken);
System.Console.WriteLine("LogHttpMessageHandler2 End Log");
return response;
}
}
LogHttpMessageHandler
代码如下:
public class LogHttpMessageHandler : DelegatingHandler
{
private IServiceProvider _provider;
public LogHttpMessageHandler(IServiceProvider provider)
{
_provider = provider;
//InnerHandler = new HttpClientHandler();
}
protected async override TaskSendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
System.Console.WriteLine("LogHttpMessageHandler Start Log");
var response=await base.SendAsync(request, cancellationToken);
System.Console.WriteLine("LogHttpMessageHandler End Log");
return response;
}
}
三个自定义Handler 代码已经完成,我们继续添加调用代码,如下:
///
///
///
///
///
public async Taskstring> GetBaiduAsync(string url)
{
var client = _clientFactory.CreateClient("test");
var result = await client.GetStringAsync(url);
return result;
}
现在我们运行访问接口,运行后的控制台Log 如下图:
看到输出结果,大家有没有发现跟Asp.net core 中的中间件管道的运行图一样。
四、总结
HttpClient
中HttpMessageHandler
可以自定义多个,但是只能有一个PrimaryHttpMessageHandler
如果添加多个只会被最后面添加的给覆盖;添加的一系列Handler 构成一个链式管道模型,并且PrimaryHttpMessageHandler
主的消息Handler 是在管道的最外层,也就是管道模型中的最后一道Handler。
使用场景:我们可以通过自定义的MessageHandler 来动态加载请求证书,通过数据库的一些信息,在自定义的Handler 中加载注入对应的证书,这样可以起到动态加载支付证书作用,同时可以SendAsync 之前或者之后做一些自己的验证等相关业务,大家只需要理解它们的用途,自然知道它的强大作用,今天就分享到这里
.net core HttpClient 使用之消息管道解析(二)
标签:des col https connect next demo invoke 动态加载 art
原文地址:https://www.cnblogs.com/lonelyxmas/p/12954791.html
文章标题:.net core HttpClient 使用之消息管道解析(二)
文章链接:http://soscw.com/index.php/essay/56594.html