5.18预检与自定义提取器
在某些情况下,您可能希望只获取 HTTP 请求对象本身,而不实际发送请求。例如:在单元测试中验证生成的请求是否符合预期,或拿到请求构建器后进一步手动修改,亦或是将请求消息传递给另一个系统执行。
获取请求构建器或请求消息(预检请求)#
在某些情况下,您可能希望只获取 HTTP 请求对象本身,而不实际发送请求。例如:在单元测试中验证生成的请求是否符合预期,或拿到请求构建器后进一步手动修改,亦或是将请求消息传递给另一个系统执行。
为此,当发送 HTTP 远程请求的方法的返回类型为 HttpRequestBuilder 或 HttpRequestMessage 时,框架将直接构建并返回该对象,跳过实际的网络传输。示例如下:
public interface IHttpService : IHttpDeclarative{ // HttpRequestBuilder 类型,不发送请求(预检请求) [Get("https://furion.net/")] Task<HttpRequestBuilder> GetRequestBuilderAsync(); // HttpRequestMessage 类型,不发送请求(预检请求) [Get("https://furion.net/")] Task<HttpRequestMessage> GetRequestMessageAsync();}调用时直接获取对象:
// 获取构建器,可继续链式配置后手动发送var builder = await httpService.GetRequestBuilderAsync(); // 不发送请求builder.WithHeader("X-Custom", "value");var httpResponseMessage = await httpRemoteService.SendAsync(builder); // 发起网络请求// 获取 HttpRequestMessage,用于断言或外部传递var httpRequestMessage = await httpService.GetRequestMessageAsync(); // 不发送请求Assert.Equal("https://furion.net/", httpRequestMessage.RequestUri?.ToString());应用场景#
- 预检请求(Pre-flight Check):在正式发送前,检查生成的请求对象是否符合预期。确认
URL、请求头、Token注入等都正确无误后,再手动发送或继续处理。 - 单元测试:无需模拟网络环境,直接验证生成的
HttpRequestMessage是否包含了正确的参数、头和认证信息。 - 请求对象传递:将构造好的
HttpRequestMessage传递给其他服务、库或进程执行,实现请求构造与请求执行的分离。 - 混合编程:先通过构建器或声明式完成大部分配置(参数映射、
Token注入等),再拿到构建器进行少量动态修改后手动发送,兼顾声明式的简洁与命令式的灵活。
自定义 HTTP 声明提取器#
在 19.5 声明式请求 章节中,我们了解到每种特性或参数类型都对应着一种 HTTP 声明式提取器。以下系统预置的特性提取器及其对应的实现:
BaseAddressAttribute特性提取器:BaseAddressDeclarativeExtractorSuppressExceptionsAttribute特性提取器:ValidationDeclarativeExtractorAutoSetHostHeaderAttribute特性提取器:AutoSetHostHeaderDeclarativeExtractorStandardRequestHeadersAttribute特性提取器:StandardRequestHeadersDeclarativeExtractorHttpClientNameAttribute特性提取器:HttpClientNameDeclarativeExtractorTraceIdentifierAttribute特性提取器:TraceIdentifierDeclarativeExtractorProfilerAttribute特性提取器:ProfilerDeclarativeExtractorSimulateBrowserAttribute特性提取器:SimulateBrowserDeclarativeExtractorAcceptLanguageAttribute特性提取器:AcceptLanguageDeclarativeExtractorDisableCacheAttribute特性提取器:DisableDeclarativeExtractorEnsureSuccessStatusCodeAttribute特性提取器:EnsureSuccessStatusCodeDeclarativeExtractorRetryAttribute特性提取器:RetryDeclarativeExtractorTimeoutAttribute特性提取器:TimeoutDeclarativeExtractorPathSegmentAttribute特性提取器:PathSegmentDeclarativeExtractorQueryParamAttribute特性提取器:QueryParamDeclarativeExtractorQuotaKeyAttribute特性提取器:QuotaKeyDeclarativeExtractorPathAttribute特性提取器:PathDeclarativeExtractorCookieAttribute特性提取器:CookieDeclarativeExtractorRefererAttribute特性提取器:RefererDeclarativeExtractorHeaderAttribute特性提取器:HeaderDeclarativeExtractorPropertyAttribute特性提取器:PropertyDeclarativeExtractorHttpVersionAttribute特性提取器:HttpVersionDeclarativeExtractorSuppressExceptionsAttribute特性提取器:SuppressExceptionsDeclarativeExtractorRemoveTrailingSlashAttribute特性提取器:RemoveTrailingSlashDeclarativeExtractorRequestEventHandlerAttribute特性提取器:RequestEventHandlerDeclarativeExtractorJsonResponseWrapperAttribute特性提取器:JsonResponseWrapperDeclarativeExtractorJsonResponseStringUnwrapAttribute特性提取器:JsonResponseStringUnwrapDeclarativeExtractorSuppressTokenManagementAttribute特性提取器:SuppressTokenManagementDeclarativeExtractorUseETagAttribute特性提取器:UseETagDeclarativeExtractorBodyAttribute特性提取器:BodyDeclarativeExtractorMultipartAttribute和MultipartFormAttribute特性提取器:MultipartDeclarativeExtractorAction<HttpRequestMessage>参数提取器:HttpRequestMessageDeclarativeExtractorAction<HttpMultipartFormDataBuilder>参数提取器:HttpMultipartFormDataBuilderDeclarativeExtractorAction<HttpRequestBuilder>参数提取器:HttpRequestBuilderDeclarativeExtractor
通过自定义 HTTP 声明式提取器,您可以为 HTTP 声明式接口提供额外的功能。以下是一个自定义 AcceptAttribute 特性及其提取器的示例:
1. 定义 AcceptAttribute 特性
设置 AcceptAttribute 特性作用范围为方法或接口上。
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Interface)]public sealed class AcceptAttribute : Attribute{ public AcceptAttribute(string accept) { ArgumentException.ThrowIfNullOrWhiteSpace(accept); Accept = accept; } public string Accept { get; set; }}2. 实现 AcceptDeclarativeExtractor 提取器
解析 AcceptAttribute 特性并设置给 HttpRequestBuilder 实例。
public sealed class AcceptDeclarativeExtractor : IHttpDeclarativeExtractor{ // 实现 Extract 方法 public void Extract(HttpRequestBuilder httpRequestBuilder, HttpDeclarativeParsingContext context) { // 获取方法或接口定义的 AcceptAttribute 特性 if (!context.IsMethodDefined<AcceptAttribute>(out var acceptAttribute, true)) { return; } // 设置 Accept 头 httpRequestBuilder.WithHeader("Accept", acceptAttribute.Accept, replace: true); }}提取器 Extract 方法的 context 参数的类型为 HttpDeclarativeParsingContext,包含以下属性和方法:
-
属性:
Method:被调用方法(MethodInfo类型)。Args:被调用方法的参数值数组(object[]类型)。MethodMetadata:被调用方法的元数据(HttpDeclarativeMethodMetadata类型)。Parameters:被调用方法的参数键值字典(IReadOnlyDictionary<ParameterInfo, object?>类型)。UnFrozenParameters:被调用方法的非冻结类型参数键值字典(IReadOnlyDictionary<ParameterInfo, object?>类型)。
-
方法:
IsFrozenParameter(parameter):判断参数是否是冻结参数类型。IsMethodDefined<TAttribute>(out var attribute, inherit):检查被调用方法是否定义了指定特性。GetMethodDefinedCustomAttributes(inherit, methodScanFirst):获取被调用方法指定特性的所有实例。
3. 在配置中注册自定义提取器
在 Startup.cs 或 Program.cs 文件中,配置并注册 HttpRemote 服务,以启用自定义 HTTP 声明式提取器功能。
services.AddHttpRemote(builder =>{ builder.AddHttpDeclarativeExtractors(() => [ new AcceptDeclarativeExtractor() ]);});4. 在 HTTP 声明式接口中使用自定义特性
[Accept("text/html")]public interface IHttpService : IHttpDeclarative{ // 在方法上应用 [Accept("text/xml")] [Get("https://furion.net/")] Task<string> GetStringAsync();}通过上述步骤,您已经成功创建了一个自定义的 HTTP 声明式提取器。这不仅可以增强 HTTP 声明式接口的功能,还可以使代码更加简洁和易于维护。您可以根据自己的需求继续扩展和自定义其他 HTTP 声明式提取器。
如需更多自定义的 HTTP 声明式提取器,请参考框架内置的 HTTP 声明式提取器代码实现。
自定义 HTTP 声明提取器(授权)#
以下是一个示例,展示了如何通过自定义 AuthenticationAttribute 和 AllowAnonymousAttribute 特性,并添加相应的提取器,以实现自动授权和匿名访问功能。
1. 定义 AuthenticationAttribute 特性
将 AuthenticationAttribute 特性应用于方法或接口上。
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Interface)]public class AuthenticationAttribute : Attribute;2. 实现 AuthenticationDeclarativeExtractor 和 AllowAnonymousDeclarativeExtractor 提取器
/// <summary>/// [Authentication] 特性提取器/// </summary>public class AuthenticationDeclarativeExtractor : IHttpDeclarativeExtractor{ /// <inheritdoc /> public void Extract(HttpRequestBuilder httpRequestBuilder, HttpDeclarativeParsingContext context) { // 如果贴了 [AllowAnonymous] 特性则跳过 if (context.IsMethodDefined<AllowAnonymousAttribute>(out _, true)) return; // 检查是否已经设置了授权信息 if (httpRequestBuilder.AuthenticationHeader is not null) return; // 添加授权标头(这里可以实现任何授权的逻辑,比如从参数获取 token 等等) httpRequestBuilder.AddBearerAuthentication( "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c"); }}/// <summary>/// [AllowAnonymous] 特性提取器/// </summary>public class AllowAnonymousDeclarativeExtractor : IHttpDeclarativeExtractor{ /// <inheritdoc /> public void Extract(HttpRequestBuilder httpRequestBuilder, HttpDeclarativeParsingContext context) { // 如果没有贴 [AllowAnonymous] 特性则跳过 if (!context.IsMethodDefined<AllowAnonymousAttribute>(out _, true)) return; // 移除授权标头 httpRequestBuilder.RemoveHeaders("Authorization"); }}3. 在配置中注册自定义提取器
在 Startup.cs 或 Program.cs 文件中,配置并注册 HttpRemote 服务,以启用自定义 HTTP 声明式提取器功能。
services.AddHttpRemote(builder =>{ // 添加自定义 HTTP 声明式提取器 builder.AddHttpDeclarativeExtractors(() => [ new AuthenticationDeclarativeExtractor(), new AllowAnonymousDeclarativeExtractor() ]); // 扫描程序集批量添加 HTTP 声明式提取器(推荐) // builder.AddHttpDeclarativeExtractorsFromAssemblies([ assembly1, assembly2, ... ]); // 若使用 Furion 框架可直接设置 App.Assemblies});4. 在 HTTP 声明式接口中使用自定义特性
[Authentication] // 添加全局授权public interface IAuthService : IHttpDeclarative{ [Get("https://furion.net/")] Task<string> GetDataAsync(); // 访问这个接口需要授权 [AllowAnonymous] // 匿名访问 [Get("https://furion.net/")] Task<string> LoginAsync(string username, string password);}当调用 GetDataAsync 方法时,将自动添加授权标头(实现授权)。调用 LoginAsync 方法时,将自动移除授权请求标头(实现匿名访问)。
通过这个示例可以看出,自定义 HTTP 声明提取器为实现复杂的授权逻辑提供了极大的灵活性。