接着上面的一个部分来叙述,这一篇我们来重点看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(下)的更多相关文章

  1. ABP中的Filter(上)

    这个部分我打算用上下两个部分来将整个结构来讲完,在我们读ABP中的代码之后我们一直有一个疑问?在ABP中为什么要定义Interceptor和Filter,甚至这两者之间我们都能找到一些对应关系,比如: ...

  2. ABP中的拦截器之ValidationInterceptor(上)

    从今天这一节起就要深入到ABP中的每一个重要的知识点来一步步进行分析,在进行介绍ABP中的拦截器之前我们先要有个概念,到底什么是拦截器,在介绍这些之前,我们必须要了解AOP编程思想,这个一般翻译是面向 ...

  3. ABP中的拦截器之ValidationInterceptor(下)

    在上篇我分析了整个ABP中ValitationInterceptor的整个过程,就其中涉及到的Validator过程没有详细的论述,这篇文章就这个过程进行详细的论述,另外任何一个重要的特性如何应用是最 ...

  4. ABP中的本地化处理(下)

    在上篇文章中我们的重点是讲述怎样通过在Domain层通过PreInitialize()配置ILocalizationConfiguration中的Sources(IList<ILocalizat ...

  5. ABP源码分析三十五:ABP中动态WebAPI原理解析

    动态WebAPI应该算是ABP中最Magic的功能之一了吧.开发人员无须定义继承自ApiController的类,只须重用Application Service中的类就可以对外提供WebAPI的功能, ...

  6. ABP中动态WebAPI原理解析

    ABP中动态WebAPI原理解析 动态WebAPI应该算是ABP中最Magic的功能之一了吧.开发人员无须定义继承自ApiController的类,只须重用Application Service中的类 ...

  7. JS组件系列——在ABP中封装BootstrapTable

    前言:关于ABP框架,博主关注差不多有两年了吧,一直迟迟没有尝试.一方面博主觉得像这种复杂的开发框架肯定有它的过人之处,系统的稳定性和健壮性比一般的开源框架肯定强很多,可是另一方面每每想到它繁琐的封装 ...

  8. ABP中的数据过滤器

      本文首先介绍了ABP内置的软删除过滤器(ISoftDelete)和多租户过滤器(IMultiTenant),然后介绍了如何实现一个自定义过滤器,最后介绍了在软件开发过程中遇到的实际问题,同时给出了 ...

  9. ABP源码分析二:ABP中配置的注册和初始化

    一般来说,ASP.NET Web应用程序的第一个执行的方法是Global.asax下定义的Start方法.执行这个方法前HttpApplication 实例必须存在,也就是说其构造函数的执行必然是完成 ...

随机推荐

  1. Java 中的几种线程池,你之前用对了吗

    好久不发文章了,难道是因为忙,其实是因为懒.这是一篇关于线程池使用和基本原理的科普水文,如果你经常用到线程池,不知道你的用法标准不标准,是否有隐藏的 OOM 风险.不经常用线程池的同学,还有对几种线程 ...

  2. Asp.Net MVC Https设置

    1.   IIS设置 1.1 创建SSL证书 点击左侧菜单栏顶部,点击“功能视图”里的“服务器证书”: 点击“创建自动签名证书”创建自动签名证书: 1.2 设置SSL证书 点开网站,在“功能视图”里点 ...

  3. mysql触发器new和old

    下面为您介绍mysql触发器new old的相关知识,供您参考学习,如果您在mysql触发器方面遇到过类似的问题,不妨一看,相信对您会有所帮助. mysql触发器new old: "NEW ...

  4. 使用WordPress制作微信小程序

    0 产品由来 微信小程序具有即来即用.轻量化.与微信贴合性好的特点.对于独立产品来说,使用微信小程序能够较好的服务与个人及现在的互联网社群,提升用户体验. 本次设计的微信小程序是面向无人机开发者社区的 ...

  5. 【esri-loader】帮助文档翻译 part2 用法

    esri-loader怎么用?看完不要太清楚. [未完待续]!!! Q1: 在哪里用? 这是我最疑惑的问题之一,我知道要用esri-loader,肯定是某条js导入语句起作用的,但是你得告诉我写在哪里 ...

  6. 关于Android Studio 3.2 运行应用时提示 “Instant Run requires that the platform corresponding to your target device (Android 7.0 (Nougat)) is installed.” 的说明

    点击"Run",运行App后,Android Studio显示如图1-1界面: 图1-1 这是因为你连接的外部设备(比如Android手机或AVD)的SDK版本在你的电脑上没有安装 ...

  7. eclipse 导入gradle引入多模块项目,引入eclipse后变成了好几个工程

    1.eclipse  导入gradle 项目 ,选择项目文件夹. 2.导入完成后,文档结构变成 ,多个子项目并列了,而且互不依赖,没有层级结构了. 3.点击项目目录,右上角这个小箭头,选择projec ...

  8. windows10滑轮bug

    今天我突然发现我一点也忍受不了在UWP应用.wi10窗口.设置等界面无法使用鼠标滑轮了.这个bug已经困扰了我差不多一年了,从买了这台笔记本就开始了.而且这个问题在中间的某一次升级系统后,也修复了,但 ...

  9. 【导航】FPGA相关

    [博客索引] FPGA相关 数字电路实现上,较多的经验是基于Xilinx/Altera的FPGA,使用Verilog语言,实现光传输SDH.OTN通信协议,DDR3控制器应用,以及视频分割.合并.sc ...

  10. Neuroph开发过程

    文章提纲 安装与配置 开发小结 建立项目 配置项目 理解感知机的代码 安装与配置 JDK的安装:建议JRE 1.8以上: Neuroph安装:建议2.94的版本.下载地址 neuroph-core-2 ...