您的位置:首页 > 其它

NET Core写了一个轻量级的Interception框架[开源]

2017-01-12 07:51 651 查看

NET Core写了一个轻量级的Interception框架[开源]

[code]























[/code]
[/code]
考虑到依赖注入的使用,我们并没有为具体的Interceptor类型定义一个接口,用户仅仅需要按照如下的约定来定义这个Interceptor类型就可以了。对ASP.NET Core的管道设计比较熟悉的人应该可以看出这与中间件的设计是一致的。

Interceptor具有一个这样一个公共构造函数:它的第一个参数是一个InterceptDelegate 类型的委托,我们通过它调用后续的Interceptor或者目标对象。我们并不对后续的参数做任何约束,它们可以采用DI的方式进行注入(比如上面的loggerFactory参数)。如果不能以DI的形式提供的参数(比如参数category),在后面注册的时候需要显式指定。

拦截注入的功能虚线实现在一个名为InvokeAsync的方法中,该方法的需要返回一个Task对象,并且要求方法中包含一个类型为InvocationContext 的对象,该对象表示执行代理方法的执行上下文。如下面的代码片段所示,我们不仅仅可以得到与当前方法调用相关的上下文信息,还可以直接利用它设置参数的值和最终返回的值。InvokeAsync方法需要自行决定是否继续调用后续的Interceptor和目标对象,这可以直接通过在构造函数中指定的这个InterceptDelegate 来完成。

[code][code]



















[/code]
[/code]
由于构造函数和InvokeAsync方法都支持依赖注入,所以ErrorLogger也可以定义成如下的形式(ILoggerFactory 在InvokeAsync方法中注入)。

[code][code]










)













[/code]
[/code]

四、定义InterceptorAttribute

由于我们采用标注Attribute的方式,我们为这样的Attribute定义了一个名为InterceptorAttribute的基类。针对ErrorLogger的ErrorLoggerAttribute定义如下,它的核心在与需要实现抽象方法Use并利用作为参数的IInterceptorChainBuilder 注册对应的ErrorLogger。IInterceptorChainBuilder 中定义了一个泛型的方法使我们很容易地实现针对某个Interceptor类型的注册。该方法的第一个参数是整数,它决定注册的Interceptor在整个Interceptor有序列表中的位置。InterceptorAttribute中定义了对应的Order属性。如果注册Interceptor类型的构造还是具有不能通过依赖注入的参数,我们需要在调用Use方法的时候显式指定(比如category)。

[code][code]














[/code]
[/code]
InterceptorAttribute可以应用在类和方法上(我不赞成将它应用到接口上),在默认情况下它的AllowMultiple 属性为False。如果我们希望Interceptor链中可以包含多个相同类型的Interceptor,我们可以将AllowMultiple 属性设置为True。值得一提的是,在AllowMultiple 属性为False的情况下,如果类型和方法上都应用了同一个InterceptorAttribute,那么只会选择应用在方法上的那一个。在如下的代码中,我们将ErrorLoggerAttribute应用到总是会抛出异常的Invoke方法中,并且将日志类型设置为“App”。

[code][code]













[/code]
[/code]

五、以DI的方式注入代理

我们依然会以DI的方式来使用上面定义的服务IFoobarService,但是毫无疑问,注入的对象必须是目标对象(FoobarService)的代理,我们注册的Interceptor才能生效,为了达到这个目的,我们需要使用如下这个IInterceptable<T>接口,它的Proxy属性为我们返回需要的代理对象。

[code][code]







[/code]
[/code]
比如我们选在在MVC应用中将IFoobarService注入到Controller中,我们可以采用如下的定义方式。

[code][code]



interceptable)


;









[/code]
[/code]
接下来我们来完成这个应用余下的部分。如下面的代码片段所示,我们在作为启动类Startup的ConfigureServicves方法中调用IServiceCollection的扩展方法AddInterception注册于Interception相关的服务。为了确定ErrorLogger是否将异常信息写入日志,我们在Main方法中添加了针对ConsoleLoggerProvider的注册,并选择只写入类型为“App”的日志。

[code][code]





























[/code]
[/code]
运行该应用后,如果我们利用浏览器访问该应用,由于我们注册了DeveloperExceptionPageMiddleware中间件,所以会出入如下图所示的错误页面。而服务端的控制台会显示记录下的错误日志。





六、如果你不喜欢IInterceptable<T>接口

Interception自身的特质决定我们只有注入目标对象的代理才能让注册的Interceptor被执行,这个问题我们是利用IInterceptable<T>接口来实现的,可能有人觉得这种方法不是很爽的话,我们还有更好的解决方案。我们先将HomeController写成正常的形式。

[code][code]



service)











[/code]
[/code]
接下来我们需要在Startup的ConfigureServices方法调用ServiceCollection的ToInterceptable方法即可。

[code][code]








();









[/code]
[/code]
目前来说,如果采用这种方法,我们需要让注入的服务实现一个空的IInterceptable接口,因为我会利用它来确定某个对象是否需要封装成代理,将来我会将这个限制移除。

[code][code]








[/code]
[/code]
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: