ABP中的Filter(下)
接着上面的一个部分来叙述,这一篇我们来重点看ABP中的AbpUowActionFilter、AbpExceptionFilter、AbpResultFilter这三个部分也是按照之前的思路来一个个介绍,当然这里面如果和前面的Interceptor有重复的部分,那么将会对两者进行一个对比并作出相关的说明,那么我们现在来一步步来分析这几个Filter的细节。
四 AbpUowActionFilter
这个我们需要和之前的UnitOfWorkInterceptor中的上篇和下篇来进行对比,工作单元部分是整个ABP中非常重要的一个部分,这里我们也来简要的进行分析,详细的过程可以参考UnitOfWorkInterceptor中的过程来说明,这里主要是说一下两者的不同之处,这里我们先看看这部分的代码,然后再进行分析。
public class AbpUowActionFilter : IAsyncActionFilter, ITransientDependency
{
private readonly IUnitOfWorkManager _unitOfWorkManager;
private readonly IAbpAspNetCoreConfiguration _aspnetCoreConfiguration;
private readonly IUnitOfWorkDefaultOptions _unitOfWorkDefaultOptions; public AbpUowActionFilter(
IUnitOfWorkManager unitOfWorkManager,
IAbpAspNetCoreConfiguration aspnetCoreConfiguration,
IUnitOfWorkDefaultOptions unitOfWorkDefaultOptions)
{
_unitOfWorkManager = unitOfWorkManager;
_aspnetCoreConfiguration = aspnetCoreConfiguration;
_unitOfWorkDefaultOptions = unitOfWorkDefaultOptions;
} public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
{
if (!context.ActionDescriptor.IsControllerAction())
{
await next();
return;
} var unitOfWorkAttr = _unitOfWorkDefaultOptions
.GetUnitOfWorkAttributeOrNull(context.ActionDescriptor.GetMethodInfo()) ??
_aspnetCoreConfiguration.DefaultUnitOfWorkAttribute; if (unitOfWorkAttr.IsDisabled)
{
await next();
return;
} using (var uow = _unitOfWorkManager.Begin(unitOfWorkAttr.CreateOptions()))
{
var result = await next();
if (result.Exception == null || result.ExceptionHandled)
{
await uow.CompleteAsync();
}
}
}
}
这里我们来一步步进行分析,首先第一步也是判断当前执行的方法是否是ControllerAction,确确来说就是当前执行的方法是否位于Controller的内部,如果当前方法不是ControllerAction的话那么就不再拦截当前方法,这个和前面分析的是一样的。然后第二部就是通过下面的GetUnitOfWorkAttributeOrNull这个方法来判断能够进行后续操作。
internal static class UnitOfWorkDefaultOptionsExtensions
{
public static UnitOfWorkAttribute GetUnitOfWorkAttributeOrNull(this IUnitOfWorkDefaultOptions unitOfWorkDefaultOptions, MethodInfo methodInfo)
{
var attrs = methodInfo.GetCustomAttributes(true).OfType<UnitOfWorkAttribute>().ToArray();
if (attrs.Length > 0)
{
return attrs[0];
} attrs = methodInfo.DeclaringType.GetTypeInfo().GetCustomAttributes(true).OfType<UnitOfWorkAttribute>().ToArray();
if (attrs.Length > 0)
{
return attrs[0];
} if (unitOfWorkDefaultOptions.IsConventionalUowClass(methodInfo.DeclaringType))
{
return new UnitOfWorkAttribute(); //Default
} return null;
} public static bool IsConventionalUowClass(this IUnitOfWorkDefaultOptions unitOfWorkDefaultOptions, Type type)
{
return unitOfWorkDefaultOptions.ConventionalUowSelectors.Any(selector => selector(type));
}
}
这是一个扩展方法,主要是获取当前方法的名称为UnitOfWork的自定义属性,当然这个【UnitOfWork】可以定义在当前方法的上面,当然也可以定义在该方法的类的上面,如果定义在类的上面则表示整个类都拥有UnitOfWork特性。另外我们可以定义我们自己的规则,从而确保当前方法能够运用到UnitOfWork的特性,具体的方法就是在UnitOfWorkDefaultOptions中定义自己的ConventionalUowSelectors,我们来看看UnitOfWorkDefaultOptions的实现。
internal class UnitOfWorkDefaultOptions : IUnitOfWorkDefaultOptions
{
public TransactionScopeOption Scope { get; set; } /// <inheritdoc/>
public bool IsTransactional { get; set; } /// <inheritdoc/>
public TimeSpan? Timeout { get; set; } /// <inheritdoc/>
public bool IsTransactionScopeAvailable { get; set; } /// <inheritdoc/>
public IsolationLevel? IsolationLevel { get; set; } public IReadOnlyList<DataFilterConfiguration> Filters => _filters;
private readonly List<DataFilterConfiguration> _filters; public List<Func<Type, bool>> ConventionalUowSelectors { get; } public UnitOfWorkDefaultOptions()
{
_filters = new List<DataFilterConfiguration>();
IsTransactional = true;
Scope = TransactionScopeOption.Required; IsTransactionScopeAvailable = true; ConventionalUowSelectors = new List<Func<Type, bool>>
{
type => typeof(IRepository).IsAssignableFrom(type) ||
typeof(IApplicationService).IsAssignableFrom(type)
};
} public void RegisterFilter(string filterName, bool isEnabledByDefault)
{
if (_filters.Any(f => f.FilterName == filterName))
{
throw new AbpException("There is already a filter with name: " + filterName);
} _filters.Add(new DataFilterConfiguration(filterName, isEnabledByDefault));
} public void OverrideFilter(string filterName, bool isEnabledByDefault)
{
_filters.RemoveAll(f => f.FilterName == filterName);
_filters.Add(new DataFilterConfiguration(filterName, isEnabledByDefault));
}
}
在这个里面有一个公共的名称为ConventionalUowSelectors的Func委托集合,在这个里面我们默认添加了从IRepository或者IApplicationService的类型自动添加UnitOfWork的特性的方式,当然我们也可以在我们的Module里面添加自己的UowSelector,通过上面的代码我们可以了解整个过程,在获取了当前的方法的UnitOfWorkAttribute后我们需要判断当前的IsDisable是否为true,如果为true那么再次跳过UnitOfWork的过程,最后就是通过UnitOfWorkManager来启动工作单元,其内部具体的执行过程请参考UnitOfWorkInterceptor中详细的过程。
五 AbpExceptionFilter
这个应该在ABP中非常常见的一类Filter,在我们的代码中底层抛出异常之后我们到底该怎么处理呢?我们来看看ABP中写了哪些?处理过程又是什么样的?
public class AbpExceptionFilter : IExceptionFilter, ITransientDependency
{
public ILogger Logger { get; set; } public IEventBus EventBus { get; set; } private readonly IErrorInfoBuilder _errorInfoBuilder;
private readonly IAbpAspNetCoreConfiguration _configuration; public AbpExceptionFilter(IErrorInfoBuilder errorInfoBuilder, IAbpAspNetCoreConfiguration configuration)
{
_errorInfoBuilder = errorInfoBuilder;
_configuration = configuration; Logger = NullLogger.Instance;
EventBus = NullEventBus.Instance;
} public void OnException(ExceptionContext context)
{
if (!context.ActionDescriptor.IsControllerAction())
{
return;
} var wrapResultAttribute =
ReflectionHelper.GetSingleAttributeOfMemberOrDeclaringTypeOrDefault(
context.ActionDescriptor.GetMethodInfo(),
_configuration.DefaultWrapResultAttribute
); if (wrapResultAttribute.LogError)
{
LogHelper.LogException(Logger, context.Exception);
} if (wrapResultAttribute.WrapOnError)
{
HandleAndWrapException(context);
}
} private void HandleAndWrapException(ExceptionContext context)
{
if (!ActionResultHelper.IsObjectResult(context.ActionDescriptor.GetMethodInfo().ReturnType))
{
return;
} context.HttpContext.Response.StatusCode = GetStatusCode(context); context.Result = new ObjectResult(
new AjaxResponse(
_errorInfoBuilder.BuildForException(context.Exception),
context.Exception is AbpAuthorizationException
)
); EventBus.Trigger(this, new AbpHandledExceptionData(context.Exception)); context.Exception = null; //Handled!
} protected virtual int GetStatusCode(ExceptionContext context)
{
if (context.Exception is AbpAuthorizationException)
{
return context.HttpContext.User.Identity.IsAuthenticated
? (int)HttpStatusCode.Forbidden
: (int)HttpStatusCode.Unauthorized;
} if (context.Exception is AbpValidationException)
{
return (int)HttpStatusCode.BadRequest;
} if (context.Exception is EntityNotFoundException)
{
return (int)HttpStatusCode.NotFound;
} return (int)HttpStatusCode.InternalServerError;
}
}
在这个方法中,首先也是过滤ControllerAction,然后就会获取当前执行方法或者其所属的类上面是否定义了WrapResultAttribute,如果找不到自定义的WrapResultAttribute,那么会为其添加一个默认的WrapResultAttribute,默认的WrapResultAttribute中默认定义LogError=true,所以默认会通过LogHelper.LogException(Logger, context.Exception)来记录当前系统中异常信息作为日志文件。当然这里我们也可以看看HandleAndWrapException中到底做了些什么?
首先是判断当前的方法的返回值是否是一个ObjectResult,那么到底什么是ObjectResult,我们一起来看看。
public static bool IsObjectResult(Type returnType)
{
//Get the actual return type (unwrap Task)
if (returnType == typeof(Task))
{
returnType = typeof(void);
}
else if (returnType.GetTypeInfo().IsGenericType && returnType.GetGenericTypeDefinition() == typeof(Task<>))
{
returnType = returnType.GenericTypeArguments[0];
} if (typeof(IActionResult).GetTypeInfo().IsAssignableFrom(returnType))
{
if (typeof(JsonResult).GetTypeInfo().IsAssignableFrom(returnType) || typeof(ObjectResult).GetTypeInfo().IsAssignableFrom(returnType))
{
return true;
} return false;
} return true;
}
首先来判断当前方法的返回值是否继承自IActionResult,在满足这个条件以后再来看当前方法的返回值是否继承自JsonResult或者是ObjectResult,如果是那么就返回true。返回true后我们会获取当前Response的状态码并且以AjaxRespone的形式返回。这里我们看看在实际的业务中我们的处理方式,看下面的代码。
/// <summary>
/// API 的未捕捉异常处理
/// </summary>
public class ApiExceptionFilter : IExceptionFilter { /// <summary>
/// 仅针对 /api/ 开头的 HTTP API 处理异常
/// </summary>
/// <param name="context">异常的上下文</param>
public void OnException(ExceptionContext context) {
var route = context.ActionDescriptor.AttributeRouteInfo.Template;
if (route.StartsWith("api/")) {
HandleException(context);
}
} /// <summary>
/// 针对不同的异常,HTTP Response 使用不同的 Status Code, Body 均定义为 { "message": "exception message" }
/// </summary>
/// <param name="context"></param>
private void HandleException(ExceptionContext context) {
context.HttpContext.Response.StatusCode = GetStatusCode(context); if (context.Exception is AbpValidationException exception) {
context.Result = new ObjectResult(
new {
// Message = "你的请求无效",
ValidationErrors = GetValidationErrorInfos(exception),
Message = GetValidationErrorNarrative(exception)
}
);
} else if (context.Exception is FileValidationException fileValidationException) {
context.Result = new ObjectResult(
new {
payload = fileValidationException.FileName,
fileValidationException.Message
}
);
} else {
var message = context.Exception.Message;
if (context.Exception.InnerException is ValidationException)
message = context.Exception.InnerException.Message;
context.Result = new ObjectResult(new {
Message = message
});
} context.ExceptionHandled = true;
} private int GetStatusCode(ExceptionContext context) {
if (context.Exception is AbpAuthorizationException) {
return context.HttpContext.User.Identity.IsAuthenticated
? StatusCodes.Status403Forbidden
: StatusCodes.Status401Unauthorized;
} if (context.Exception is AbpValidationException
|| context.Exception is UserFriendlyException
|| context.Exception is ValidationException
|| context.Exception is FileValidationException
|| context.Exception.InnerException is ValidationException) {
return StatusCodes.Status400BadRequest;
} if (context.Exception is EntityNotFoundException) {
return StatusCodes.Status404NotFound;
}
if (context.Exception is PreconditionRequiredException) {
return StatusCodes.Status428PreconditionRequired;
} if (context.Exception is PreconditionFailedException) {
return StatusCodes.Status412PreconditionFailed;
} return StatusCodes.Status500InternalServerError;
} private ValidationErrorInfo[] GetValidationErrorInfos(AbpValidationException validationException) {
var validationErrorInfos = new List<ValidationErrorInfo>(); foreach (var validationResult in validationException.ValidationErrors) {
var validationError = new ValidationErrorInfo(validationResult.ErrorMessage); if (validationResult.MemberNames != null && validationResult.MemberNames.Any()) {
validationError.Members = validationResult.MemberNames.Select(m => m.ToCamelCase()).ToArray();
} validationErrorInfos.Add(validationError);
} return validationErrorInfos.ToArray();
} private string GetValidationErrorNarrative(AbpValidationException validationException) {
var detailBuilder = new StringBuilder();
detailBuilder.AppendLine("验证过程中检测到以下错误"); foreach (var validationResult in validationException.ValidationErrors) {
detailBuilder.AppendFormat(" - {0}", validationResult.ErrorMessage);
detailBuilder.AppendLine();
} return detailBuilder.ToString();
}
}
在实际的业务过程中我们会将当前的报错信息已一定的结构返回给调用的前端,让前端去处理具体的异常信息,通常会将错误信息显示在界面上方几秒中,然后退出的方式。
六 AbpResultFilter
这个在实际过程中用的不是很多我们也来看看到底会做些什么吧?
public class AbpResultFilter : IResultFilter, ITransientDependency
{
private readonly IAbpAspNetCoreConfiguration _configuration;
private readonly IAbpActionResultWrapperFactory _actionResultWrapperFactory; public AbpResultFilter(IAbpAspNetCoreConfiguration configuration,
IAbpActionResultWrapperFactory actionResultWrapper)
{
_configuration = configuration;
_actionResultWrapperFactory = actionResultWrapper;
} public virtual void OnResultExecuting(ResultExecutingContext context)
{
if (!context.ActionDescriptor.IsControllerAction())
{
return;
} var methodInfo = context.ActionDescriptor.GetMethodInfo(); //var clientCacheAttribute = ReflectionHelper.GetSingleAttributeOfMemberOrDeclaringTypeOrDefault(
// methodInfo,
// _configuration.DefaultClientCacheAttribute
//); //clientCacheAttribute?.Apply(context); var wrapResultAttribute =
ReflectionHelper.GetSingleAttributeOfMemberOrDeclaringTypeOrDefault(
methodInfo,
_configuration.DefaultWrapResultAttribute
); if (!wrapResultAttribute.WrapOnSuccess)
{
return;
} _actionResultWrapperFactory.CreateFor(context).Wrap(context);
} public virtual void OnResultExecuted(ResultExecutedContext context)
{
//no action
}
}
这个里面最重要就是最后一个Wrap方法,这个方法会根据返回的结果是JsonResult还是ObjectResult来做不同的处理,这里我以ObjectResult为例来进行说明。
public class AbpObjectActionResultWrapper : IAbpActionResultWrapper
{
private readonly IServiceProvider _serviceProvider; public AbpObjectActionResultWrapper(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
} public void Wrap(ResultExecutingContext actionResult)
{
var objectResult = actionResult.Result as ObjectResult;
if (objectResult == null)
{
throw new ArgumentException($"{nameof(actionResult)} should be ObjectResult!");
} if (!(objectResult.Value is AjaxResponseBase))
{
objectResult.Value = new AjaxResponse(objectResult.Value);
if (!objectResult.Formatters.Any(f => f is JsonOutputFormatter))
{
objectResult.Formatters.Add(
new JsonOutputFormatter(
_serviceProvider.GetRequiredService<IOptions<MvcJsonOptions>>().Value.SerializerSettings,
_serviceProvider.GetRequiredService<ArrayPool<char>>()
)
);
}
}
}
}
这里面也比较简单就是将最终的结果以Json的格式进行输出。
最后,点击这里返回整个ABP系列的主目录。
ABP中的Filter(下)的更多相关文章
- ABP中的Filter(上)
这个部分我打算用上下两个部分来将整个结构来讲完,在我们读ABP中的代码之后我们一直有一个疑问?在ABP中为什么要定义Interceptor和Filter,甚至这两者之间我们都能找到一些对应关系,比如: ...
- ABP中的拦截器之ValidationInterceptor(上)
从今天这一节起就要深入到ABP中的每一个重要的知识点来一步步进行分析,在进行介绍ABP中的拦截器之前我们先要有个概念,到底什么是拦截器,在介绍这些之前,我们必须要了解AOP编程思想,这个一般翻译是面向 ...
- ABP中的拦截器之ValidationInterceptor(下)
在上篇我分析了整个ABP中ValitationInterceptor的整个过程,就其中涉及到的Validator过程没有详细的论述,这篇文章就这个过程进行详细的论述,另外任何一个重要的特性如何应用是最 ...
- ABP中的本地化处理(下)
在上篇文章中我们的重点是讲述怎样通过在Domain层通过PreInitialize()配置ILocalizationConfiguration中的Sources(IList<ILocalizat ...
- 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中的数据过滤器
本文首先介绍了ABP内置的软删除过滤器(ISoftDelete)和多租户过滤器(IMultiTenant),然后介绍了如何实现一个自定义过滤器,最后介绍了在软件开发过程中遇到的实际问题,同时给出了 ...
- ABP源码分析二:ABP中配置的注册和初始化
一般来说,ASP.NET Web应用程序的第一个执行的方法是Global.asax下定义的Start方法.执行这个方法前HttpApplication 实例必须存在,也就是说其构造函数的执行必然是完成 ...
随机推荐
- Kubernetes的污点和容忍(上篇)
背景 搭建了一个k8s(Kubernetes)的事件监听服务,监听事件之后对数据做处理.有天报了一个问题经调查是新版本的k8s集群添加会把unschedule等信息通过污点的方式反映.而这些污点是只有 ...
- 【微信小程序项目实践总结】30分钟从陌生到熟悉
前言 我们之前对小程序做了基本学习: 1. 微信小程序开发07-列表页面怎么做 2. 微信小程序开发06-一个业务页面的完成 3. 微信小程序开发05-日历组件的实现 4. 微信小程序开发04-打造自 ...
- SpringBoot + Spring Security 学习笔记(四)记住我功能实现
记住我功能的基本原理 当用户登录发起认证请求时,会通过UsernamePasswordAuthenticationFilter进行用户认证,认证成功之后,SpringSecurity 调用前期配置好的 ...
- Python实现Singleton模式的几种方式
使用python实现设计模式中的单例模式.单例模式是一种比较常用的设计模式,其实现和使用场景判定都是相对容易的.本文将简要介绍一下python中实现单例模式的几种常见方式和原理.一方面可以加深对pyt ...
- hosts文件的作用
hosts文件的作用 hosts文件是一个没有扩展名的系统文件,可以用记事本等工具打开,作用就是将一些常用的网址域名与其对应的IP地址建立一个关联"数据库",当用户在浏览器中输入一 ...
- .NET CAD二次开发学习第一天
基于浩辰CAD2019 需求: 开发线转圆简单命令.命令过程:1) 请选择图中直线(要求支持一次选多个):2) 弹出对话框,输入圆的图层名和半径3) 点对话框中确定按钮,结束命令.命令执行效果:所选每 ...
- .net 笔试面试总结(3)
什么是Sql注入?如何避免Sql注入? 用户根据系统的程序构造非法的参数从而导致程序执行不是程序期望的恶意Sql语句. 使用参数化的Sql就可以避免Sql注入. 数据库三范式是什么? 第一范式:字段不 ...
- java或Jmeter实现两个日期相加减(2003-06-01-2003-05-01)
在beanshell中写入如下代码, import java.io.FileInputStream; SimpleDateFormat myFormatter = new SimpleDateForm ...
- Oracl 一条sql语句 批量添加、修改数据
最近一直在用,也一直在学oralc,项目上也用到了批量的添加(读取上传CSV文件信息,把符合条件的信息写入到数据库中),在写的时候想到了可能是数据量大就想该怎么快,(由于本人在.NET开发期间没有做过 ...
- 做优化的数据库工程师请参考!CynosDB的计算层设计优化揭秘
本文由云+社区发表 本文作者:孙旭,腾讯数据库开发工程师,9年数据库内核开发经验:熟悉数据库查询处理,并发控制,日志以及存储系统:熟悉PostgreSQL(Greenplum,PGXC等).Terad ...