ABP中的Filter(上)
这个部分我打算用上下两个部分来将整个结构来讲完,在我们读ABP中的代码之后我们一直有一个疑问?在ABP中为什么要定义Interceptor和Filter,甚至这两者之间我们都能找到一些对应关系,比如:AuthorizationInterceptor和AbpAuthorizationFilter,AuditingInterceptor和AbpAuditActionFilter,甚至这些代码的实现也是调用相同的接口和实现?那么ABP为什么要采用这种方式呢?在前面的章节中我已经充分介绍过了ABP中的各种Interceptor,这一部分我们将重点来理解ABP中的另外一种类型Filter,在理解本篇文章之前,建议先对Asp.Net Core中的Filter有一个清晰的理解,这里建议看微软官方的文档使自己对整个Filter有一个清晰的认识,或者读这篇博客来理解到底什么是Asp.Net Core 中的Filter。
在有了前面的预备知识以后我们就可以来探讨ABP中的Filter到底是怎么一回事了,还是和以前的分析思路一样,我们来看看整个Filter在ABP中是怎样添加并运行的,在我们的项目代码中当我们在Asp.Net Core中使用ABP框架时,我们要做的第一步就是在Startup类的ConfigureServices方法中将我们的ABP添加到DI容器中,ConfigureServices是用来将服务注册到DI容器用的,在Asp.Net Core的Demo程序中我们经常可以看到下面的代码:
// ...
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
}
}
在这里services就是一个DI容器。此例把MVC的服务注册到DI容器,等到需要用到MVC服务时,才从DI容器取得物件实例。在我们的ABP中整个实现也是按照这个思路来进行的,首先是在ConfigureServices方法中我们调用AddAbp方法,在这个方法的主要作用按照ABP中的注释是:Integrates ABP to AspNet Core.主要就是将ABP集成到整个Asp.Net Core中,我们来简要看看这个AddAbp方法到底做了些什么?
/// <summary>
/// Integrates ABP to AspNet Core.
/// </summary>
/// <typeparam name="TStartupModule">Startup module of the application which depends on other used modules. Should be derived from <see cref="AbpModule"/>.</typeparam>
/// <param name="services">Services.</param>
/// <param name="optionsAction">An action to get/modify options</param>
public static IServiceProvider AddAbp<TStartupModule>(this IServiceCollection services, [CanBeNull] Action<AbpBootstrapperOptions> optionsAction = null)
where TStartupModule : AbpModule
{
var abpBootstrapper = AddAbpBootstrapper<TStartupModule>(services, optionsAction); ConfigureAspNetCore(services, abpBootstrapper.IocManager); return WindsorRegistrationHelper.CreateServiceProvider(abpBootstrapper.IocManager.IocContainer, services);
}
主要就是做了三件事:1 初始化整个AbpBootstrapper并创建唯一的实例。2 配置Asp.Net Core中的一些重要的服务及参数。 3 将我们之前注册到DI容器中的所有IServiceCollection都添加到ABP中使用的Windsor Castel容器中来接管了 ASP.NET Core 自带的IOC容器。而我们今天要讨论和比较的两个部分就包含在这几个过程中,为整个ABP框架中添加Interceptor在第一个过程中完成,而我们今天要讲的ABP中的Filter就是包含在第二个过程中,那么我们先来看看第二个过程。
//Configure MVC
services.Configure<MvcOptions>(mvcOptions =>
{
mvcOptions.AddAbp(services);
});
在第二个过程我们只选取和添加Filter相关的代码部分,这部分的重点在于mvcOptions.AddAbp的方法,我们来看看里面的实现。
internal static class AbpMvcOptionsExtensions
{
public static void AddAbp(this MvcOptions options, IServiceCollection services)
{
AddConventions(options, services);
AddFilters(options);
AddModelBinders(options);
} private static void AddConventions(MvcOptions options, IServiceCollection services)
{
options.Conventions.Add(new AbpAppServiceConvention(services));
} private static void AddFilters(MvcOptions options)
{
options.Filters.AddService(typeof(AbpAuthorizationFilter));
options.Filters.AddService(typeof(AbpAuditActionFilter));
options.Filters.AddService(typeof(AbpValidationActionFilter));
options.Filters.AddService(typeof(AbpUowActionFilter));
options.Filters.AddService(typeof(AbpExceptionFilter));
options.Filters.AddService(typeof(AbpResultFilter));
} private static void AddModelBinders(MvcOptions options)
{
options.ModelBinderProviders.Insert(0, new AbpDateTimeModelBinderProvider());
}
}
看到了吧,这里我们向MvcOptions对应的Filter里面添加了6中不同的Filter,看到这个我们就知道了接下来我们就要对这几种Filter进行分析和总结。
一 AbpAuthorizationFilter
在分析这部分之前我们可以和之前讲到的AuthorizationInterceptor来做一个对比,因为我们在分析代码的时候发现这两个方法大部分的代码都是重复的,在AuthorizationInterceptor中能够对某个类型或者这个类型中的Public或者是NonPublic方法是否定义了自定义的AbpAuthorize或者RequiresFeature属性,只有定义了这些属性才会进行拦截操作。两个实现的内部都是调用IAuthorizationHelper中定义的Authorize方法,然后执行CheckFeatures和CheckPermissions这两个过程,这两个过程在之前的部分有介绍这里就不再赘述。这里面我们发现在AbpAuthorizationFilter中只会拦截ControllerAction,所以可以这么理解AbpAuthorizationFilter只要用于拦截Controller中定义的一些方法,如果不是一个控制器方法则直接返回,而AuthorizationInterceptor则通过 Castle Windsor Interceptor 来验证普通类型的方法,来检测当前用户是否有权限进行调用。所以这里我们需要进行区分。
另外在AbpAuthorizationFilter这一部分中,我们可以看到整个过程使用了两个Catch来捕获特定的异常,然后进行特定的处理,这些处理包括记录日志、触发异常事件、以及对ObjectResult的返回类型采用AjaxResponse对象进行封装信息等一系列处理,这里我们可以看一下源代码的处理过程。
public class AbpAuthorizationFilter : IAsyncAuthorizationFilter, ITransientDependency
{
public ILogger Logger { get; set; } // 权限验证类,这个才是真正针对权限进行验证的对象
private readonly IAuthorizationHelper _authorizationHelper;
// 异常包装器主要是用来封装没有授权时返回的错误信息
private readonly IErrorInfoBuilder _errorInfoBuilder;
// 事件总线处理器在这里用于触发一个未授权请求引发的事件,用户可以监听此事件来进行自己的处理
private readonly IEventBus _eventBus; // 构造注入
public AbpAuthorizationFilter(
IAuthorizationHelper authorizationHelper,
IErrorInfoBuilder errorInfoBuilder,
IEventBus eventBus)
{
_authorizationHelper = authorizationHelper;
_errorInfoBuilder = errorInfoBuilder;
_eventBus = eventBus;
Logger = NullLogger.Instance;
} public async Task OnAuthorizationAsync(AuthorizationFilterContext context)
{
// 如果注入了 IAllowAnonymousFilter 过滤器则允许所有匿名请求
if (context.Filters.Any(item => item is IAllowAnonymousFilter))
{
return;
} // 如果不是一个控制器方法则直接返回
if (!context.ActionDescriptor.IsControllerAction())
{
return;
} // 开始使用 IAuthorizationHelper 来进行权限校验
try
{
await _authorizationHelper.AuthorizeAsync(
context.ActionDescriptor.GetMethodInfo(),
context.ActionDescriptor.GetMethodInfo().DeclaringType
);
}
// 如果是未授权异常的处理逻辑
catch (AbpAuthorizationException ex)
{
// 记录日志
Logger.Warn(ex.ToString(), ex); // 触发异常事件
_eventBus.Trigger(this, new AbpHandledExceptionData(ex)); // 如果接口的返回类型为 ObjectResult,则采用 AjaxResponse 对象进行封装信息
if (ActionResultHelper.IsObjectResult(context.ActionDescriptor.GetMethodInfo().ReturnType))
{
context.Result = new ObjectResult(new AjaxResponse(_errorInfoBuilder.BuildForException(ex), true))
{
StatusCode = context.HttpContext.User.Identity.IsAuthenticated
? (int) System.Net.HttpStatusCode.Forbidden
: (int) System.Net.HttpStatusCode.Unauthorized
};
}
else
{
context.Result = new ChallengeResult();
}
}
// 其他异常则显示为内部异常信息
catch (Exception ex)
{
Logger.Error(ex.ToString(), ex); _eventBus.Trigger(this, new AbpHandledExceptionData(ex)); if (ActionResultHelper.IsObjectResult(context.ActionDescriptor.GetMethodInfo().ReturnType))
{
context.Result = new ObjectResult(new AjaxResponse(_errorInfoBuilder.BuildForException(ex)))
{
StatusCode = (int) System.Net.HttpStatusCode.InternalServerError
};
}
else
{
//TODO: How to return Error page?
context.Result = new StatusCodeResult((int)System.Net.HttpStatusCode.InternalServerError);
}
}
}
}
二 AbpAuditActionFilter
在使用审计ActionFilter之前,我们也建议先看看之前介绍AuditingInterceptor的文章,通过对比你发现这两个部分核心也是相同的,其内部都是调用IAuditingHelper中定义的CreateAuditInfo来创建审计日志,那么我们着重看看这两者在实现方式上面有哪些不同的地方,在方法(AuditingInterceptor中的Intercept)调用之前,首先也是通过AbpCrossCuttingConcerns.IsApplied(invocation.InvocationTarget, AbpCrossCuttingConcerns.Auditing)方法来验证当前方法是否已经应用过审计功能,原因也好理解同一个方法不应该被AuditingInterceptor和AbpAuditActionFilter中的方法拦截两次,所以这个判断很好理解就是用于判断当前调用方法是否已经被应用过审计功能了,如果应用过则直接跳过去。我们来看看IsApplied方法中到底做了些什么?
public static bool IsApplied([NotNull] object obj, [NotNull] string concern)
{
if (obj == null)
{
throw new ArgumentNullException(nameof(obj));
} if (concern == null)
{
throw new ArgumentNullException(nameof(concern));
} return (obj as IAvoidDuplicateCrossCuttingConcerns)?.AppliedCrossCuttingConcerns.Contains(concern) ?? false;
}
在这个方法的内部首先将当前当前类型转换为IAvoidDuplicateCrossCuttingConcerns接口形式,如果转换成功则判断接口中定义的AppliedCrossCuttingConcerns字符串List是否已经包含了AbpCrossCuttingConcerns.Auditing=“AbpAuditing”这个常量,那么在ABP框架中哪种类型的对象能够转换为IAvoidDuplicateCrossCuttingConcerns接口形式呢?我们发现在ABP中默认有两种类型继承自IAvoidDuplicateCrossCuttingConcerns接口,一种是ApplicationService另外一种就是DynamicApiController<T>,第二种不太常见,但是第一种在应用层每一个应用服务对象都要继承自ApplicationService,所以说应用层对象都能够转换成IAvoidDuplicateCrossCuttingConcerns形式,那么我们通过阅读源码发现这两个作用的范围是不同的,AbpAuditActionFilter主要用于审计Controller层中添加了Audited属性的公共并且非匿名登录的一些方法的审计工作日志输出工作,而AuditingInterceptor中的Intercept方法主要用于其他一些添加了Audited属性的公共方法并且非匿名登录的一些方法的普通类型的方法或类型的审计工作,通过这里相信你对这两者已经有一个清晰的认识啦,接下来我们就是看一看IAuditingHelper中做了些什么,其实里面主要是将当前执行方法的TeantId、UserID、ServiceName、名称、类型、参数、耗时、Exception、TotalMilliseconds等参数自动保存到数据库中,从而方便我们来查询这些方法的执行情况。
三 AbpValidationActionFilter
这个部分的内容在ValidationInterceptor中的上篇和下篇有过详细的对比,这里不再赘述。
后面的AbpUowActionFilter、AbpExceptionFilter、以及AbpResultFilter将在下一篇中具体讲到,当前篇主要就是包含这些内容。
最后,点击这里返回整个ABP系列的主目录。
ABP中的Filter(上)的更多相关文章
- ABP中的Filter(下)
接着上面的一个部分来叙述,这一篇我们来重点看ABP中的AbpUowActionFilter.AbpExceptionFilter.AbpResultFilter这三个部分也是按照之前的思路来一个个介绍 ...
- ABP中的拦截器之ValidationInterceptor(上)
从今天这一节起就要深入到ABP中的每一个重要的知识点来一步步进行分析,在进行介绍ABP中的拦截器之前我们先要有个概念,到底什么是拦截器,在介绍这些之前,我们必须要了解AOP编程思想,这个一般翻译是面向 ...
- Abp中SwaggerUI的接口文档添加上传文件参数类型
在使用Swashbuckle上传文件的时候,在接口文档中希望看到上传控件,但是C#中,没有FromBodyAttribute这个特性,所以需要在运行时,修改参数的swagger属性. 首先看下,最 ...
- ABP中的本地化处理(上)
今天这篇文章主要来总结一下ABP中的多语言是怎么实现的,在后面我们将结合ABP中的源码和相关的实例来一步步进行说明,在介绍这个之前我们先来看看ABP的官方文档,通过这个文档我们就知道怎样在我们的系统中 ...
- ABP源码分析三十五:ABP中动态WebAPI原理解析
动态WebAPI应该算是ABP中最Magic的功能之一了吧.开发人员无须定义继承自ApiController的类,只须重用Application Service中的类就可以对外提供WebAPI的功能, ...
- ABP中动态WebAPI原理解析
ABP中动态WebAPI原理解析 动态WebAPI应该算是ABP中最Magic的功能之一了吧.开发人员无须定义继承自ApiController的类,只须重用Application Service中的类 ...
- JS组件系列——在ABP中封装BootstrapTable
前言:关于ABP框架,博主关注差不多有两年了吧,一直迟迟没有尝试.一方面博主觉得像这种复杂的开发框架肯定有它的过人之处,系统的稳定性和健壮性比一般的开源框架肯定强很多,可是另一方面每每想到它繁琐的封装 ...
- ABP中的拦截器之AuditingInterceptor
在上面两篇介绍了ABP中的ValidationInterceptor之后,我们今天来看看ABP中定义的另外一种Interceptor即为AuditingInterceptor,顾名思义就是一种审计相关 ...
- ABP中的模块初始化过程(一)
在总结完整个ABP项目的结构之后,我们就来看一看ABP中这些主要的模块是按照怎样的顺序进行加载的,在加载的过程中我们会一步步分析源代码来进行解释,从而使自己对于整个框架有一个清晰的脉络,在整个Asp. ...
随机推荐
- 如何利用HTTP缓存来加快你的网站应用
缓存在web环境各个环节都有实现,有CPU缓存.文件缓存.程序的Opcode缓存(APC,eAccelerator).内存缓存(Memcached,Redis).代理服务器(Nginx,Squid). ...
- Java开发笔记(八十一)如何使用系统自带的注解
之前介绍继承的时候,提到对于子类而言,父类的普通方法可以重写也可以不重写,但是父类的抽象方法是必须重写的,如果不重写,编译器就直接在子类名称那里显示红叉报错.例如,以前演示抽象类用法之时,曾经把Chi ...
- Huffman Tree 简单构造
//函数:构造Huffman树HT[2*n-1] #define MAXVALUE 9999//假设权值不超过9999 #define MAXLEAF 30 #define MAXNODE MAXLE ...
- Python二级-----------程序冲刺2
1. 编写 Python 程序输出一个具有如下风格效果的文本,用作文本进度条样式,部分代码如下,填写空格处. ...
- 非常易于理解‘类'与'对象’ 间 属性 引用关系,暨《Python 中的引用和类属性的初步理解》读后感
关键字:名称,名称空间,引用,指针,指针类型的指针(即指向指针的指针) 我读完后的理解总结: 1. 我们知道,python中的变量的赋值操作,变量其实就是一个名称name,赋值就是将name引用到一个 ...
- javascript 函数后面有多个小括号f( )( )( )...
有时我们看见js函数后面跟着多个小括号是什么意思?f( )( )( )... f()执行f函数,返回子函数 f()()执行子函数,返回孙函数 f()()()执行孙函数,返回重孙函数 ... ... 但 ...
- PHP制作个人博客-广告位添加与调用 推荐文章数据调取
上一节博客的导航我们已经动态调取,这一节我们主讲一下如何根据页面布局,后台添加广告位,及模板上动态调取广告.博客推荐文章的数据调用. 首先我们在云码博客的后台添加10条左右的测试数据,thinkcmf ...
- 使用vue-cli快速搭建vue项目
直接上干货...... 步骤: 1.安装node.js:(下载地址:https://nodejs.org/en/download/)安装完成以后,在命令窗口输入node -v 查看node版本. ...
- Chart.js 與 ASP.NET MVC 整合應用
Chart.js 是一套開放原始碼的「圖表」繪製函式庫,和其他第三方的圖表工具相比,Chart.js 的特色如下: 支援 HTML 5.響應式網頁 (RWD, Responsive Web Desig ...
- oppo7.0系统手机(亲测有效)激活Xposed框架的流程
对于喜欢钻研手机的朋友而言,很多时候会使用到xposed框架及种类繁多功能极强的模块,对于5.0以下的系统版本,只要手机能获得root权限,安装和激活xposed框架是异常简单的,但随着系统版本的升级 ...