Abp vNext异常处理的缺陷/改造方案

摘要:
Roast AbpVnext异常处理!唉,当前的喷雾器项目是使用AbpVNext开发的,不可避免地要全局处理异常并向服务器提示异常信息。

吐槽Abp Vnext异常处理! 哎呀,是一个喷子

目前项目使用Abp VNext开发,免不了要全局处理异常、提示服务器异常信息。

1. Abp官方异常处理

Abp项目默认会启动内置的异常处理,默认不将异常信息发送到客户端。
在AppModule文件ConfigureServices方法中使用以下代码:

 Configure<AbpExceptionHandlingOptions>(options =>
{
      options.SendExceptionsDetailsToClients = true;
});

可将异常信息发送到客户端:如下图:

{
"error": {
"code": null,
"message": "ERROR [42000] [Cloudera][ImpalaODBC] (360) Syntax error occurred during query execution: [HY000] : AnalysisException: Could not resolve column/field reference: 'ug_fed89221846a42dc8427932b2965a020'
",
"details": "OdbcException: ERROR [42000] [Cloudera][ImpalaODBC] (360) Syntax error occurred during query execution: [HY000] : AnalysisException: Could not resolve column/field reference: 'ug_fed89221846a42dc8427932b2965a020'

STACK TRACE: at Gridsum.EAP.Olap.ExecuteQueryLayer.HandleQueryAsync(QueryContext queryContext, CancellationToken cancellationToken) in /home/gitlab-runner/builds/ttRjAPVA/0/eap/website/app/src/Gridsum.EAP.DataQuery/Olap/ExecuteQueryLayer.cs:line 81
 at Gridsum.EAP.Olap.DistributedCacheLayer.HandleQueryAsync(QueryContext queryContext, CancellationToken cancellationToken) in /home/gitlab-runner/builds/ttRjAPVA/0/eap/website/app/src/Gridsum.EAP.DataQuery/Olap/DistributedCacheLayer.cs:line 50
 at Gridsum.EAP.DataQuery.AbstractQueryExecutor`1.ExecuteQueryAsync(TQuery query, DistributedCacheEntryOptions options, CancellationToken cancellationToken) in /home/gitlab-runner/builds/ttRjAPVA/0/eap/website/app/src/Gridsum.EAP.DataQuery/AbstractQueryExecutor.cs:line 60
 at Gridsum.EAP.DataQuery.AbstractQueryExecutor`1.ExecuteQueryAsync(TQuery query, CancellationToken token) in /home/gitlab-runner/builds/ttRjAPVA/0/eap/website/app/src/Gridsum.EAP.DataQuery/AbstractQueryExecutor.cs:line 47
 at Gridsum.EAP.Application.UserGroupService.ClearUserGroupUserAsync(UserGroupUpdateUserDto updateDto, String idshort, CancellationToken cancelToken) in /home/gitlab-runner/builds/ttRjAPVA/0/eap/website/app/src/Gridsum.EAP.Application/UserGroup/UserGroupService.cs:line 332
 at Gridsum.EAP.Application.UserGroupService.UpdateUserGroupUserAsync(UserGroupUpdateUserDto updateDto, CancellationToken cancelToken) in /home/gitlab-runner/builds/ttRjAPVA/0/eap/website/app/src/Gridsum.EAP.Application/UserGroup/UserGroupService.cs:line 370
 at Castle.DynamicProxy.AsyncInterceptorBase.ProceedAsynchronous[TResult](IInvocation invocation, IInvocationProceedInfo proceedInfo "42000] [Cloudera][ImpalaODBC] (360 "42000] [Cloudera][ImpalaODBC] (360) Syntax error occurred during query execution: [HY000] : AnalysisException: Could not resolve column/field reference: 'ug_fed89221846a42dc8427932b2965a020'

STACK TRACE: at Gridsum.EAP.Olap.ExecuteQueryLayer.HandleQueryAsync(QueryContext queryContext, CancellationToken cancellationToken) in /home/gitlab-runner/builds/ttRjAPVA/0/eap/website/app/src/Gridsum.EAP.DataQuery/Olap/ExecuteQueryLayer.cs:line 81
 at Gridsum.EAP.Olap.DistributedCacheLayer.HandleQueryAsync(QueryContext queryContext, CancellationToken cancellationToken) in /home/gitlab-runner/builds/ttRjAPVA/0/eap/website/app/src/Gridsum.EAP.DataQuery/Olap/DistributedCacheLayer.cs:line 50
 at Gridsum.EAP.DataQuery.AbstractQueryExecutor`1.ExecuteQueryAsync(TQuery query, DistributedCacheEntryOptions options, CancellationToken cancellationToken) in /home/gitlab-runner/builds/ttRjAPVA/0/eap/website/app/src/Gridsum.EAP.DataQuery/AbstractQueryExecutor.cs:line 60
 at Gridsum.EAP.DataQuery.AbstractQueryExecutor`1.ExecuteQueryAsync(TQuery query, CancellationToken token) in /home/gitlab-runner/builds/ttRjAPVA/0/eap/website/app/src/Gridsum.EAP.DataQuery/AbstractQueryExecutor.cs:line 47
 at Gridsum.EAP.Application.UserGroupService.ClearUserGroupUserAsync(UserGroupUpdateUserDto updateDto, String idshort, CancellationToken cancelToken) in /home/gitlab-runner/builds/ttRjAPVA/0/eap/website/app/src/Gridsum.EAP.Application/UserGroup/UserGroupService.cs:line 332
 at Gridsum.EAP.Application.UserGroupService.UpdateUserGroupUserAsync(UserGroupUpdateUserDto updateDto, CancellationToken cancelToken) in /home/gitlab-runner/builds/ttRjAPVA/0/eap/website/app/src/Gridsum.EAP.Application/UserGroup/UserGroupService.cs:line 370
 at Castle.DynamicProxy.AsyncInterceptorBase.ProceedAsynchronous[TResult") Syntax error occurred during query execution: [HY000] : AnalysisException: Could not resolve column/field reference: 'ug_fed89221846a42dc8427932b2965a020'

STACK TRACE: at Gridsum.EAP.Olap.ExecuteQueryLayer.HandleQueryAsync(QueryContext queryContext, CancellationToken cancellationToken) in /home/gitlab-runner/builds/ttRjAPVA/0/eap/website/app/src/Gridsum.EAP.DataQuery/Olap/ExecuteQueryLayer.cs:line 81
 at Gridsum.EAP.Olap.DistributedCacheLayer.HandleQueryAsync(QueryContext queryContext, CancellationToken cancellationToken) in /home/gitlab-runner/builds/ttRjAPVA/0/eap/website/app/src/Gridsum.EAP.DataQuery/Olap/DistributedCacheLayer.cs:line 50
 at Gridsum.EAP.DataQuery.AbstractQueryExecutor`1.ExecuteQueryAsync(TQuery query, DistributedCacheEntryOptions options, CancellationToken cancellationToken) in /home/gitlab-runner/builds/ttRjAPVA/0/eap/website/app/src/Gridsum.EAP.DataQuery/AbstractQueryExecutor.cs:line 60
 at Gridsum.EAP.DataQuery.AbstractQueryExecutor`1.ExecuteQueryAsync(TQuery query, CancellationToken token) in /home/gitlab-runner/builds/ttRjAPVA/0/eap/website/app/src/Gridsum.EAP.DataQuery/AbstractQueryExecutor.cs:line 47
 at Gridsum.EAP.Application.UserGroupService.ClearUserGroupUserAsync(UserGroupUpdateUserDto updateDto, String idshort, CancellationToken cancelToken) in /home/gitlab-runner/builds/ttRjAPVA/0/eap/website/app/src/Gridsum.EAP.Application/UserGroup/UserGroupService.cs:line 332
 at Gridsum.EAP.Application.UserGroupService.UpdateUserGroupUserAsync(UserGroupUpdateUserDto updateDto, CancellationToken cancelToken) in /home/gitlab-runner/builds/ttRjAPVA/0/eap/website/app/src/Gridsum.EAP.Application/UserGroup/UserGroupService.cs:line 370
 at Castle.DynamicProxy.AsyncInterceptorBase.ProceedAsynchronous[TResult")
 at Volo.Abp.Castle.DynamicProxy.CastleAbpMethodInvocationAdapterWithReturnValue`1.ProceedAsync()
 at Volo.Abp.Validation.ValidationInterceptor.InterceptAsync(IAbpMethodInvocation invocation)
 at Volo.Abp.Castle.DynamicProxy.CastleAsyncAbpInterceptorAdapter`1.InterceptAsync[TResult](IInvocation invocation, IInvocationProceedInfo proceedInfo, Func`3 proceed "TResult")
 at Castle.DynamicProxy.AsyncInterceptorBase.ProceedAsynchronous[TResult](IInvocation invocation, IInvocationProceedInfo proceedInfo "TResult")
 at Volo.Abp.Castle.DynamicProxy.CastleAbpMethodInvocationAdapterWithReturnValue`1.ProceedAsync()
 at Volo.Abp.Auditing.AuditingInterceptor.InterceptAsync(IAbpMethodInvocation invocation)
 at Volo.Abp.Castle.DynamicProxy.CastleAsyncAbpInterceptorAdapter`1.InterceptAsync[TResult](IInvocation invocation, IInvocationProceedInfo proceedInfo, Func`3 proceed "TResult")
 at Castle.DynamicProxy.AsyncInterceptorBase.ProceedAsynchronous[TResult](IInvocation invocation, IInvocationProceedInfo proceedInfo "TResult")
 at Volo.Abp.Castle.DynamicProxy.CastleAbpMethodInvocationAdapterWithReturnValue`1.ProceedAsync()
 at Volo.Abp.Uow.UnitOfWorkInterceptor.InterceptAsync(IAbpMethodInvocation invocation)
 at Volo.Abp.Castle.DynamicProxy.CastleAsyncAbpInterceptorAdapter`1.InterceptAsync[TResult](IInvocation invocation, IInvocationProceedInfo proceedInfo, Func`3 proceed "TResult")
 at Gridsum.EAP.Controllers.UserGroupController.UpdateUserGroupUserAsync(String id, CancellationToken cancelToken) in /home/gitlab-runner/builds/ttRjAPVA/0/eap/website/app/src/Gridsum.EAP.HttpApi/Controllers/UserGroupController.cs:line 320
 at lambda_method3440(Closure , Object )
 at Microsoft.AspNetCore.Mvc.Infrastructure.ActionMethodExecutor.AwaitableObjectResultExecutor.Execute(IActionResultTypeMapper mapper, ObjectMethodExecutor executor, Object controller, Object[] arguments)
 at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeActionMethodAsync>g__Awaited|12_0(ControllerActionInvoker invoker, ValueTask`1 actionResultValueTask)
 at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeNextActionFilterAsync>g__Awaited|10_0(ControllerActionInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
 at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Rethrow(ActionExecutedContextSealed context)
 at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
 at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeInnerFilterAsync>g__Awaited|13_0(ControllerActionInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
 at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeNextExceptionFilterAsync>g__Awaited|25_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
",
"data": null,
"validationErrors": null
}
}

经过几天倒腾,发现Abp VNext的异常处理有几个问题。

2.Abp异常处理存在的缺陷

  1. 并没有如官方所述:自动处理所有异常,需要满足官方所说的任意一个条件
    Abp vNext异常处理的缺陷/改造方案第1张

这就导致当Controller Action方法返回类型不是object result(并有异常抛出)时,则根本捕获不到异常,这应该算Abp的一个Bug

  1. 输出的异常没有TraceId, 不利于日志排查
  2. 发送到客户端的日志字段message,detail字段过于详细冗长,不能用于前端显示

当然你也可以使用上面所说的配置:不将异常信息发送到客户端,但这样就因噎废食了。

3. 异常处理的目标

虽然Abp的异常处理有缺陷, 但只是异常信息应用上的缺陷,
Abp异常处理①对异常的划分、②异常信息的本地化、③出现异常时写日志 支持的还是相当好。

我们考虑基于Abp的异常处理做一些扩展,(若扩展不了做一些替换 ..

  1. 对所有Controller-Action方法捕获异常, [修复Abp Bug]
  2. 在Abp的异常处理结果中添加 TraceId
  3. 希望将服务端异常划分为几大类,简化前端显示;同时也不妨碍开发者查看详细异常信息。

4. 揪出Abp异常处理缺陷的根源

Abp异常处理的核心对象AbpExceptionFilter,实现IAsyncExceptionFilter过滤器, ITransientDependency瞬时注入接口。

这是一个ServiceFilterAttribute, 你可以认为有如下

[ServiceFilter(typeof(AbpExceptionFilter))]

特性,作用在每一个Controller的Action方法上。

一旦某个Controller的Action方法发生异常, 会先后执行如下代码:

public async Task OnExceptionAsync(ExceptionContext context)
{
     if (!ShouldHandleException(context))
     {
          return;
     }
     await HandleAndWrapException(context);
}
  1. ShouldHandleException(context) :监测是否应该处理异常,据查该函数确实存在我上文说的问题:并不能捕获所有的Action方法的异常。
  2. HandleAndWrapException(context): 异常处理步骤:
    • 根据Abp内置的异常类型,自动确定状态码 (这个在Abp官方文档有讲)
    • 序列化异常对象,并向客户端输出如下格式:
    {
"error": {
"code": null,
"message": "ERROR [42000] [Cloudera][ImpalaODBC] (360) Syntax error occurred during query execution: [HY000] : AnalysisException: Could not resolve column/field reference: 'ug_fed89221846a42dc8427932b2965a020'
",
"details":xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx......,
"data": null,
"validationErrors": null
}
}

① 输出的信息,从一开始就没有包含TraceId;
② 这个格式的日志过于详细,messagedetails字段均不能用于前端显示。

  • 写日志,默认异常级别为Error

掌握了以上源码,我们就可以针对性的修改Abp的核心异常处理类AbpExceptionFilter

5. Abp异常处理顽疾的修复方案

光说不练假把式

Abp的AbpExceptionFilter不是抽象类,没法重载,为大道我们设定的3个目标。
考虑使用针对性的ExceptionFilter替换默认的AbpExceptionFilter

①. 新建EapExceptionFilter,内容拷贝自AbpExceptionFilter, 并做出如下针对性修改:

Abp vNext异常处理的缺陷/改造方案第2张

Abp vNext异常处理的缺陷/改造方案第3张

②. 在AppModule替换默认的AbpExceptionFilter为新的EapExceptionFilter过滤器:

 context.Services.AddMvc(options =>
{
      options.Filters.ReplaceOne(x=> (x as ServiceFilterAttribute)?.ServiceType?.Name==nameof(AbpExceptionFilter), new ServiceFilterAttribute(typeof(EapExceptionFilter)));  
})

改造的效果如下:

Abp vNext异常处理的缺陷/改造方案第4张

That's All

如果大家真切使用了Abp VNext最新版,

相信我在第2点提到的Abp异常处理的顽疾,Abp使用者会感同身受;

第3点提出的几个目标也是企业级异常处理 要解决的痛点。

此异常处理的思路也可推及到其他非Abp项目.

改造方案在Abp官方github issue上: https://github.com/abpframework/abp/issues/6761
本人经验之谈,如果问题或看法,请不吝赐教!

  1. https://github.com/abpframework/abp/blob/dev/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ExceptionHandling/AbpExceptionFilter.cs
  2. https://docs.microsoft.com/en-us/aspnet/core/mvc/controllers/filters?view=aspnetcore-5.0

免责声明:文章转载自《Abp vNext异常处理的缺陷/改造方案》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇分享一个Css3效果无比惊艳的全屏图片切换效果(Css浏览器Only)[RoarCTF 2019]Simple Upload下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

gitlab实现webhook触发jenkins 自动,构建,测试,push webhook构子 总结

最新一直在学习 工作 + 学习 去掉 90%   所以blog  一直没更 真是很不好!  exsi ceph gitlab jenkins harbor k8s  docker-compose apollo  springboot  dubbo      太多太多 。。。整合   主题: 如何设置GitLab以及Jenkins,实现每次GitLab上有提...

基于Abp VNext框架设计

abp 通过IDistributedEventBus接口集成自IEventBus实现分布式事件消息的发布订阅。 IEventBus在什么时机触发PublishAsync? 当前UnitOfWork完成时,触发IEventBus的PublishAsync 在没有事务环境下,同步调用IEventBus的PublishAsync abp 默认实现基于Ra...

day14-python异常处理

1.     异常 异常即是一个事件,该事件会在程序执行过程中发生,影响了程序的正常执行。 一般情况下,在Python无法正常处理程序时就会发生一个异常。异常是Python对象,表示一个错误。当Python脚本发生异常时我们需要捕获处理它,否则程序会终止执行。最常用异常的形式  异常的常用形式 我们平时工作中最长用到的异常处理的形式: try:     正...

[ABP教程]第三章 创建、更新和删除图书

Web应用程序开发教程 - 第三章: 创建,更新和删除图书 关于本教程 在本系列教程中, 你将构建一个名为 Acme.BookStore 的用于管理书籍及其作者列表的基于ABP的应用程序. 它是使用以下技术开发的: {{DB_Text}} 做为ORM提供程序. {{UI_Value}} 做为UI框架. 本教程分为以下部分: Part 1: 创建服...

Gitlab 修改ldap认证

1. 备份数据 2. 修改配置 使用自己搭建的openldap 使用用户中心的openldap 说明:base属性执行所有员工,user_filter属性主要用来实现分组功能。上面的配置是只有ldap中的gitlab分组中的用户可以登录GitLab。 3、重新加载GitLab配置; # sudo gitlab-ctl reconfigure 4、重...

关于C#中async/await中的异常处理(上)

关于C#中async/await中的异常处理(上) 2012-04-11 09:15 by 老赵, 17919 visits 在同步编程中,一旦出现错误就会抛出异常,我们可以使用try…catch来捕捉异常,而未被捕获的异常则会不断向上传递,形成一个简单而统一的错误处理机制。不过对于异步编程来说,异常处理一直是件麻烦的事情,这也是C#中async/awa...