吐槽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'\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(并有异常抛出)时,则根本捕获不到异常,这应该算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'\n",
"details":xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx......,
"data": null,
"validationErrors": null
}
}

① 输出的信息,从一开始就没有包含TraceId;

② 这个格式的日志过于详细,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'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异常处理的缺陷/改造方案的更多相关文章

  1. [Abp vNext 入坑分享] - 8.Redis与Refit的接入

    前言 本章结束之后,这个abp vnext系列算是初步完结了,基础的组件都已经接入了.如果各位还需要其它的组件的话,可以自己按需要进行接入使用.其实这个只是一个基础的框架,可以自己根据需要进行变通的. ...

  2. 基于 abp vNext 和 .NET Core 开发博客项目 - Blazor 实战系列(一)

    系列文章 基于 abp vNext 和 .NET Core 开发博客项目 - 使用 abp cli 搭建项目 基于 abp vNext 和 .NET Core 开发博客项目 - 给项目瘦身,让它跑起来 ...

  3. 基于 abp vNext 和 .NET Core 开发博客项目 - Blazor 实战系列(二)

    系列文章 基于 abp vNext 和 .NET Core 开发博客项目 - 使用 abp cli 搭建项目 基于 abp vNext 和 .NET Core 开发博客项目 - 给项目瘦身,让它跑起来 ...

  4. 自定义Visual Studio.net Extensions 开发符合ABP vnext框架代码生成插件[附源码]

    介绍 我很早之前一直在做mvc5 scaffolder的开发功能做的已经非常完善,使用代码对mvc5的项目开发效率确实能成倍的提高,就算是刚进团队的新成员也能很快上手,如果你感兴趣 可以参考 http ...

  5. Abp vNext 切换MySql数据库

    Abp vNext是Abp的下一代版本,目前还在经一步完善,代码已经全部重写了,好的东西保留了下来,去除了很多笨重的东西,从官宣来看,Abp vNext主要是为了以后微服务架构而诞生的. 从源码来看, ...

  6. [Abp vNext微服务实践] - 服务通讯

    简介 服务通讯是微服务架构中必不可少的功能,服务通讯的效率决定了微服务架构的优略.常用的微服务通讯策略有两种,分别是rpc.http,其中rpc以gRpc框架为代表使用者最多.abp vNext微服务 ...

  7. [Abp vNext微服务实践] - vue-element-admin管理Identity

    一.简介 abp vNext微服务框架中已经提供通用权限和用户角色管理模块,管理UI使用的是MVC模式,不适用于国内主打的MVVM开发模式.在前端框架选型后笔者决定改造abp vNext微服务框架中原 ...

  8. [Abp vNext微服务实践] - vue-element-admin登录二

    简介: Vue Element Admin是基于vue.element ui开发的后台管理ui,abp vNext是abp新一代微服务框架.本篇将会介绍如何改造Vue Element Admin权限验 ...

  9. [Abp vNext微服务实践] - 业务开发

    前几篇分别介绍了abp vNext微服务框架.开发环境搭建和vue element admin前端框架接入,在vue element admin中实现用户角色管理基本功能后就可以开始进行业务开发了,本 ...

随机推荐

  1. 「CSP-S 2019」格雷码

    [题目描述] 传送门 [题解] 题目中已经清楚地告诉你怎么用n位格雷码推n+1位格雷码, 直接二叉树模拟即可 注意要使用unsigned long long(如果这道题没有95分部分分,不知道有多少人 ...

  2. miniconda安装及使用

    conda环境配置 安装conda [清华源下载地址](https://mirrors.tuna.tsinghua.edu.cn/anaconda/miniconda/) 官网或百度云网盘下载对应版本 ...

  3. 企业安全01-Apache solr XML实体注入漏洞CVE-2017-12629

    Apache solr XML 实体注入漏洞CVE-2017-12629 一.简介 Apache Solr 是一个开源的搜索服务器.Solr 使用 Java 语言开发,主要基于 HTTP 和 Apac ...

  4. SQL优化思路与解决方案

    1.面对问题SQL的思考 这条查询SQL的语句到底有没有问题? 存在什么问题? 什么情况下存在问题? 怎么去优化? 2.SQL优化思路 where查询字段是否建立索引? 是否有建立索引但是查询时候没有 ...

  5. @RequestParam,@RequestBody,@PathVariable注解还分不清吗?

    前言 在使用 SpringMVC 开发时,经常遇到前端传递的各种参数,比如 form 表单,JSON 数据,String[] 数组,再或者是最常见的 String 字符串等等,总之大部分场景都是在标题 ...

  6. macos brew zookeeper,安装后zookeeper启动失败?

    一.Zookeeper安装流程 执行如下安装命令: brew install zookeeper 执行截图如下: 安装后查看 zookeeper 安装信息(默认拉取最新版本) brew info zo ...

  7. IdentityServer4系列 | 常见术语说明

    一.前言 在上一篇中,我们IdentityServer4的说明,认识到是一个基于OpenID Connect协议标准的身份认证和授权程序,并简单的对基础知识的认识以及区别说明,从OAuth.OpenI ...

  8. sqlmap工具的简单使用

    0x00 sqlmap简介:sqlmap是一款针对sql漏洞的自动化注入工具,有一个非常棒的特性,即对检测与利用的自动化处理(数据库指纹.访问底层文件系统.执行命令). 官方网站下载http://sq ...

  9. 第6章 Python中的动态可执行方法目录

    第6.1节 Python代码编译 第6.2节 Python特色的动态可执行方法简介 第6.3节 Python动态执行之动态编译的compile函数 第6.4节 Python动态表达式计算:eval函数 ...

  10. PyQt(Python+Qt)学习随笔:QTableView的sortingEnabled属性

    老猿Python博文目录 老猿Python博客地址 sortingEnabled属性用于控制是企业视图按列排序功能,如果此属性为True,则对tableView视图中的数据启用排序,如果此属性为Fal ...