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

news/2024/7/5 23:18:04 标签: nokia, 敏捷开发, ppt, sharepoint, sdl

之前吐槽Abp的用户/租户管理模块!今天我又来了,这次我给Abp官方repo提了一个issue。

目前Website使用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'\n",
   "details": "OdbcException: ERROR [42000] [Cloudera][ImpalaODBC] (360) Syntax error occurred during query execution: [HY000] : AnalysisException: Could not resolve column/field reference: 'ug_fed89221846a42dc8427932b2965a020'\n\nSTACK 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\n 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\n 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\n 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\n 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\n 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\n 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'\n\nSTACK 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\n 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\n 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\n 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\n 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\n 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\n at Castle.DynamicProxy.AsyncInterceptorBase.ProceedAsynchronous[TResult") Syntax error occurred during query execution: [HY000] : AnalysisException: Could not resolve column/field reference: 'ug_fed89221846a42dc8427932b2965a020'\n\nSTACK 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\n 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\n 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\n 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\n 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\n 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\n at Castle.DynamicProxy.AsyncInterceptorBase.ProceedAsynchronous[TResult")\n at Volo.Abp.Castle.DynamicProxy.CastleAbpMethodInvocationAdapterWithReturnValue`1.ProceedAsync()\n at Volo.Abp.Validation.ValidationInterceptor.InterceptAsync(IAbpMethodInvocation invocation)\n at Volo.Abp.Castle.DynamicProxy.CastleAsyncAbpInterceptorAdapter`1.InterceptAsync[TResult](IInvocation invocation, IInvocationProceedInfo proceedInfo, Func`3 proceed "TResult")\n at Castle.DynamicProxy.AsyncInterceptorBase.ProceedAsynchronous[TResult](IInvocation invocation, IInvocationProceedInfo proceedInfo "TResult")\n at Volo.Abp.Castle.DynamicProxy.CastleAbpMethodInvocationAdapterWithReturnValue`1.ProceedAsync()\n at Volo.Abp.Auditing.AuditingInterceptor.InterceptAsync(IAbpMethodInvocation invocation)\n at Volo.Abp.Castle.DynamicProxy.CastleAsyncAbpInterceptorAdapter`1.InterceptAsync[TResult](IInvocation invocation, IInvocationProceedInfo proceedInfo, Func`3 proceed "TResult")\n at Castle.DynamicProxy.AsyncInterceptorBase.ProceedAsynchronous[TResult](IInvocation invocation, IInvocationProceedInfo proceedInfo "TResult")\n at Volo.Abp.Castle.DynamicProxy.CastleAbpMethodInvocationAdapterWithReturnValue`1.ProceedAsync()\n at Volo.Abp.Uow.UnitOfWorkInterceptor.InterceptAsync(IAbpMethodInvocation invocation)\n at Volo.Abp.Castle.DynamicProxy.CastleAsyncAbpInterceptorAdapter`1.InterceptAsync[TResult](IInvocation invocation, IInvocationProceedInfo proceedInfo, Func`3 proceed "TResult")\n 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\n at lambda_method3440(Closure , Object )\n at Microsoft.AspNetCore.Mvc.Infrastructure.ActionMethodExecutor.AwaitableObjectResultExecutor.Execute(IActionResultTypeMapper mapper, ObjectMethodExecutor executor, Object controller, Object[] arguments)\n at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeActionMethodAsync>g__Awaited|12_0(ControllerActionInvoker invoker, ValueTask`1 actionResultValueTask)\n at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeNextActionFilterAsync>g__Awaited|10_0(ControllerActionInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)\n at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Rethrow(ActionExecutedContextSealed context)\n at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)\n at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeInnerFilterAsync>g__Awaited|13_0(ControllerActionInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)\n at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeNextExceptionFilterAsync>g__Awaited|25_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)\n",
   "data": null,
   "validationErrors": null
  }
}

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

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

  1. 并没有如官方所述:自动处理所有异常,实际需要满足官方所说的某个条件:

这就导致当Controller Action方法返回的不是object result时,则根本捕获不到异常(我们暂时不说middleware产生的异常),这应该算Abp的一个Bug

  1. 输出的异常没有TraceId, 不利于日志排查

  2. 发送到客户端的日志字段message,detail过于详细冗长,不适合前端显示

也可以配置SendExceptionsDetailsToClients,不将异常信息发送到客户端,但这样就因噎废食了。

3. 异常处理的目标

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

基于Abp的异常处理现状,考虑做一些改进:

  1. 对所有Controller-Action方法捕获异常, [修复Abp Bug]

  2. 在Abp的异常处理结果中添加 TraceId

  3. 希望将服务端异常分类,简化后给到前端;同时也不妨碍开发者查看详细异常信息。

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

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

这是一个ServiceFilterAttribute, 你可以理解有个特性作用在每一个Controller的Action方法上:

[ServiceFilter(typeof(AbpExceptionFilter))]

一旦某个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'\n",
   "details":xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx......,
   "data": null,
   "validationErrors": null
  }
}

① 输出的信息,从一开始就没有包含TraceId;
② Abp标准格式化后的异常信息,过于冗长,messagedetails字段均不适合前端显示。

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

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

5. Abp异常处理: 缺陷修复方案

光说不练假把式

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

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

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

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

改造的效果如下:

That's  All

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

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

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

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

改造方案在Abp官方github issue上:  https://github.com/abpframework/abp/issues/6761

一家之言,如有其他看法,请不吝赐教!

(btw,公众号文章发布之后,限制修改;若有后续,请 [阅读原文]!)

Reference

  1. https://github.com/abpframework/abp/blob/dev/framework/src/Volo.Abp.AspNetCore.Mvc/Volo/Abp/AspNetCore/Mvc/ExceptionHandling/AbpExceptionFilter.cs

  2. https://docs.abp.io/zh-Hans/abp/latest/Exception-Handling

  3. https://docs.microsoft.com/en-us/aspnet/core/mvc/controllers/filters?view=aspnetcore-5.0

  • 吐槽一下Abp的用户和租户管理模块

  • 临近年关,修复ASP.NET Core因浏览器内核版本引发的单点登录故障

  • 一套标准的ASP.NET Core容器化应用日志收集分析方案

  • Oh my God, Swagger API文档竟然可以这样写?

  • ASP.NET Core应用注意这一点,CTO会对你刮目相看

关注并星标我们
更多干货及最佳实践分享


http://www.niftyadmin.cn/n/1864564.html

相关文章

浅议 Task 底层的调度机制 TaskScheduler

相信大家对 Task 已经非常熟悉了&#xff0c;在 Task 底层有一个发动机&#xff0c;决定了它是涡轮增压还是自然吸气&#xff0c;它就是 TaskScheduler 抽象类&#xff0c;在框架下这个发动机有两个默认实现子类&#xff1a;ThreadPoolTaskScheduler 和 SynchronizationContext…

姑苏慕容与软件开发

一、逆向工程 那女子悠悠的道&#xff1a;“丐帮‘打狗棒法’与‘降龙十八掌’两大神技&#xff0c;是丐帮的不传之秘。你们‘还施水阁’和我家‘琅擐玉洞’的藏谱拼凑起来&#xff0c;也只一些残缺不全的棒法、掌法。运功的心法却全然没有。你家公子可怎生练&#xff1f;”阿朱…

Abp小试牛刀之 图片上传

图片上传是很常见的功能&#xff0c;里面有些固定的操作也可以沉淀下来。本文记录使用Abp vNext做图片上传的姿势。目标上传图片----->预览图片----->确定保存支持集群部署实现思路&#xff1a;1. 上传图片要使用WebAPI特定媒体类型&#xff1a;multipart/form-data;2. 因…

最小残余材料长多少米

钢管 长321米 截取17米个25米的钢管N条 N不定 求 最小残余材料长多少米? 谢谢各位了~~~~~~~~~~~~~~~~~~~~~~~~~ 问题点数&#xff1a;80、回复次数&#xff1a;1Top Private Sub Command1_Click() Dim last As…

临近年关,发生两起磁盘占满引发的服务下线故障

一口气说两个因为磁盘空间不足引发的应用故障。作为拿起键盘一把梭的Coder&#xff0c; 开发--->部署-->收工--->心旷神怡&#xff0c;滋一口82年的可乐.过了几个月&#xff0c;服务突然下线了&#xff01;CTO又有杀程序员祭天的理由了!事故1&#xff1a;Azure App Se…

C#常用命令集合

Console.Write()&#xff1a;输出命令&#xff0c;输出数据不换行 Console.WriteLine()&#xff1a;输出命令&#xff0c;输出数据后换行 Console.ReadLine()&#xff1a;等待输入回车后&#xff0c;取值再往下运行。返回值是字符串 转载于:https://www.cnblogs.com/lgxjt168/a…

[AS3] 解决跨域问题

1.图片跨域在被加载图片域放置crossdomain.xmlload(new URLRequest(path), new LoaderContext(true));2.点播流(http视频流)在被加载视频域放置crossdomain.xml设置netStream.checkPolicyFile true;3.实时流跨域这个要在FlashPlayer 9.0.115.0才有效main.asc 中加上applicatio…

对CORS OPTIONS预检请求的一些思考

前后端分离模大势所趋&#xff0c;跨域问题更是老生常谈。《程序员应对浏览器同源策略的姿势》一文提到三种跨域请求方案&#xff0c;重点讲述了w3c和浏览器厂商推出的CORS规范。同源策略 所谓同源是指域名、协议、端口相同。不同源的浏览器脚本(javascript、ActionScript、ca…