Brighter 采用了一种与其他许多框架不同的方法,它在请求处理管道中优先考虑显式性。它不依赖隐藏的约定或复杂的配置,而是让你使用属性显式定义管道行为,从而完全控制执行顺序。
俄罗斯套娃模型
Brighter 管道的架构基于俄罗斯套娃(Matryoshka)模型。想象一套嵌套的娃娃:每个娃娃内部都包含一个更小的娃娃。
在 Brighter 中,每个中间件组件(一个"娃娃")都包装链中的下一个组件。处理方法位于最中心。当处理请求时,请求会穿过每个中间件层到达处理程序,然后再次穿过每个层返回。这种设计完美地实现了管道和过滤器模式。
该模型的一个关键特性是能够短路管道。任何中间件都可以停止处理并立即返回结果,阻止请求到达内部层,包括处理程序本身。
这种架构允许管道中的任何步骤:
1. 在请求到达处理程序之前执行工作。
2. 将请求传递给下一步。
3. 在内部处理程序返回后执行工作。
4. 通过提前返回来短路管道(停止处理)。
注意:虽然本指南使用同步处理程序示例,但相同的概念也适用于异步处理程序。只需使用相应的异步属性和处理程序基类(例如,RequestHandlerAsync<T>)。
为处理程序添加管道
在 Brighter 中,添加中间件是通过 C# 属性完成的。你必须显式地将属性添加到请求处理程序的 Handle 方法上。
你使用 step 参数控制执行顺序。数字越小的步骤先执行(进入时)后执行(返回时)。
public class SampleHandler : RequestHandler<SomeMessage>
{
// step 决定顺序。
// Step 1 在 Step 2 之前运行。
[RequestLogging(step: 0)]
public virtual TRequest Handle(TRequest command)
{
...
}
}
实现"全局"中间件
Brighter 不提供流式 API(例如,services.AddGlobalMiddleware(...))来将逻辑全局注入到每个请求中。这是一个经过深思熟虑的设计选择,目的是在处理程序级别保持管道的显式性。
但是,如果你想要"全局"中间件(适用于所有处理程序的逻辑),可以通过继承来实现。通过创建一个包含所需属性的基础处理程序,所有继承的处理程序都将采用该管道。
示例:创建具有全局策略的基础处理程序
// 1. 创建具有通用中间件的基础处理程序
public abstract class MyGlobalPolicyHandler<T> : RequestHandler<T> where T : class, IRequest
{
[RequestLogging(step: 0)]
[FallbackPolicy(backstop: true, circuitBreaker: false, step: 1)]
public override T Handle(T command)
{
// 此调用将命令沿着管道传播到实际的处理程序。
return base.Handle(command);
}
}
// 2. 在你的实现中继承基础处理程序
public class SampleHandler : MyGlobalPolicyHandler<SomeCommand>
{
[UseResiliencePipeline("MyPolicy", step: 2)]
public override SomeCommand Handle(SomeCommand command)
{
// 你的核心业务逻辑在这里。
Console.WriteLine("处理 SomeCommand");
return base.Handle(command);
}
}
SampleHandler 的执行顺序将是:RequestLogging -> FallbackPolicy -> UseResiliencePipeline -> SampleHandler.Handle。
Brighter 提供的中间件
Brighter 提供了几个开箱即用的有用中间件属性:
- RequestLoggingAttribute / RequestLoggingAsyncAttribute -> 记录处理程序管道的进入和退出,包括时间和请求详情。
- FallbackPolicyAttribute / FallbackPolicyAsyncAttribute -> 在处理程序执行失败时提供回退操作。调用处理程序上的 Fallback 方法。
- TimeoutPolicyAttribute -> (已弃用)为处理程序设置最大超时时间。建议使用 UseResiliencePipeline。
- UsePolicyAttribute / UsePolicyAsyncAttribute -> (已弃用)使用 Polly 策略包装处理程序。建议使用 UseResiliencePipeline。
- UseResiliencePipelineAsyncAttribute / UseResiliencePipelineAttribute -> 使用 Polly 弹性管道包装处理程序,用于高级重试、断路器和超时策略。
- MonitorAttribute / MonitorAsyncAttribute -> 允许你监控你的处理程序
- FeatureSwitchAttribute / FeatureSwitchAsyncAttribute -> 基于功能开关动态启用或禁用处理程序。
实现你自己的中间件
要实现自定义中间件,通常需要为每种执行模式(同步或异步)准备两个类:
1. 属性类:用于修饰处理程序方法。
2. 处理程序类:包含实际的中间件逻辑。
示例实现:
// 1. 属性类
[AttributeUsage(AttributeTargets.Method)]
public class MyFeatureFlagAttribute : RequestHandlerAttribute
{
public string FeatureName { get; }
public MyFeatureFlagAttribute(string featureName, int step) : base(step, HandlerTiming.Before)
{
FeatureName = featureName;
}
public override object[] InitializerParams()
{
return new object[] { FeatureName };
}
public override Type GetHandlerType()
{
// 这告诉 Brighter 为该属性使用哪个处理程序类。
return typeof(MyFeatureFlagHandler<>);
}
}
// 2. 处理程序类
public class MyFeatureFlagHandler<TRequest> : RequestHandler<TRequest> where TRequest : class, IRequest
{
private string _featureName;
private IFeatureManager _featureManager; // 假设的功能管理器
public override void InitializeFromAttributeParams(params object[] initializerList)
{
// 接收来自 InitializerParams() 的参数
_featureName = (string)initializerList[0];
_featureManager = ...; // 通常通过 DI 注入
}
public override TRequest Handle(TRequest command)
{
// 检查功能是否启用
if (!_featureManager.IsEnabled(_featureName))
{
return command;
}
// 如果启用,则继续到管道中的下一个处理程序。
return base.Handle(command);
}
}
// 3. 使用自定义中间件
public class DiscountHandler : RequestHandler<ApplyDiscountCommand>
{
[MyFeatureFlag("AdvancedDiscounts", step: 1)]
public override ApplyDiscountCommand Handle(ApplyDiscountCommand command)
{
// 仅当"AdvancedDiscounts"功能启用时才会执行。
return base.Handle(command);
}
}
结论
Brighter 的显式、基于属性的管道为应用程序的横切关注点提供了无与伦比的清晰度和控制。通过利用俄罗斯套娃模型,你可以构建健壮、有序的处理程序管道,这些管道易于理解和维护。无论是使用内置策略还是创建自己的自定义中间件,该过程都保持一致和透明。
参考
官方文档:构建请求处理程序管道
7793

被折叠的 条评论
为什么被折叠?



