ABP 异常处理 第四篇
1、ABP异常处理机制是通过过滤器实现的,我们查看的webAPI的异常处理,我们来看看他的源码,AbpApiExceptionFilterAttribute 继承ExceptionFilterAttribute, ITransientDependency
public class AbpApiExceptionFilterAttribute : ExceptionFilterAttribute, ITransientDependency
{
/// <summary>
/// Reference to the <see cref="ILogger"/>.
/// </summary>
public ILogger Logger { get; set; } /// <summary>
/// Reference to the <see cref="IEventBus"/>.
/// </summary>
public IEventBus EventBus { get; set; } public IAbpSession AbpSession { get; set; } protected IAbpWebApiConfiguration Configuration { get; } /// <summary>
/// Initializes a new instance of the <see cref="AbpApiExceptionFilterAttribute"/> class.
/// </summary>
public AbpApiExceptionFilterAttribute(IAbpWebApiConfiguration configuration)
{
Configuration = configuration;
Logger = NullLogger.Instance;
EventBus = NullEventBus.Instance;
AbpSession = NullAbpSession.Instance;
} /// <summary>
/// Raises the exception event.
/// </summary>
/// <param name="context">The context for the action.</param>
public override void OnException(HttpActionExecutedContext context)
{
var wrapResultAttribute = HttpActionDescriptorHelper
.GetWrapResultAttributeOrNull(context.ActionContext.ActionDescriptor) ??
Configuration.DefaultWrapResultAttribute; if (wrapResultAttribute.LogError)
{
LogHelper.LogException(Logger, context.Exception);
} if (!wrapResultAttribute.WrapOnError)
{
return;
} if (IsIgnoredUrl(context.Request.RequestUri))
{
return;
} if (context.Exception is HttpException)
{
var httpException = context.Exception as HttpException;
var httpStatusCode = (HttpStatusCode) httpException.GetHttpCode(); context.Response = context.Request.CreateResponse(
httpStatusCode,
new AjaxResponse(
new ErrorInfo(httpException.Message),
httpStatusCode == HttpStatusCode.Unauthorized || httpStatusCode == HttpStatusCode.Forbidden
)
);
}
else
{
context.Response = context.Request.CreateResponse(
GetStatusCode(context),
new AjaxResponse(
SingletonDependency<IErrorInfoBuilder>.Instance.BuildForException(context.Exception),
context.Exception is Abp.Authorization.AbpAuthorizationException)
);
} EventBus.Trigger(this, new AbpHandledExceptionData(context.Exception));
} protected virtual HttpStatusCode GetStatusCode(HttpActionExecutedContext context)
{
if (context.Exception is Abp.Authorization.AbpAuthorizationException)
{
return AbpSession.UserId.HasValue
? HttpStatusCode.Forbidden
: HttpStatusCode.Unauthorized;
} if (context.Exception is AbpValidationException)
{
return HttpStatusCode.BadRequest;
} if (context.Exception is EntityNotFoundException)
{
return HttpStatusCode.NotFound;
} return HttpStatusCode.InternalServerError;
} protected virtual bool IsIgnoredUrl(Uri uri)
{
if (uri == null || uri.AbsolutePath.IsNullOrEmpty())
{
return false;
} return Configuration.ResultWrappingIgnoreUrls.Any(url => uri.AbsolutePath.StartsWith(url));
}
}
我们看看他怎么处理异常的,他主要通过这个方法 SingletonDependency<IErrorInfoBuilder>.Instance.BuildForException(context.Exception)
首先我们先了解他返回的数据结构,AjaxResponse,他的基类是AjaxResponseBase,ABP所有接口的返回值的都是封装到这个类里
public abstract class AjaxResponseBase
{
/// <summary>
/// This property can be used to redirect user to a specified URL.
/// </summary>
public string TargetUrl { get; set; } /// <summary>
/// Indicates success status of the result.
/// Set <see cref="Error"/> if this value is false.
/// </summary>
public bool Success { get; set; } /// <summary>
/// Error details (Must and only set if <see cref="Success"/> is false).
/// </summary>
public ErrorInfo Error { get; set; } /// <summary>
/// This property can be used to indicate that the current user has no privilege to perform this request.
/// </summary>
public bool UnAuthorizedRequest { get; set; } /// <summary>
/// A special signature for AJAX responses. It's used in the client to detect if this is a response wrapped by ABP.
/// </summary>
public bool __abp { get; } = true;
}
属性error主要用来封装错误信息的,他的属性包含如下
/// <summary>
/// Error code.
/// </summary>
public int Code { get; set; } /// <summary>
/// Error message.
/// </summary>
public string Message { get; set; } /// <summary>
/// Error details.
/// </summary>
public string Details { get; set; } /// <summary>
/// Validation errors if exists.
/// </summary>
public ValidationErrorInfo[] ValidationErrors { get; set; }
ValidationErrors 这个属性用来保存验证信息的,他的主要属性如下
/// <summary>
/// Validation error message.
/// </summary>
public string Message { get; set; } /// <summary>
/// Relate invalid members (fields/properties).
/// </summary>
public string[] Members { get; set; }
以上是异常信息返回的数据结构,我们来看看他的核心代码,主要是ErrorInfoBuilder
public class ErrorInfoBuilder : IErrorInfoBuilder, ISingletonDependency
{
private IExceptionToErrorInfoConverter Converter { get; set; } /// <inheritdoc/>
public ErrorInfoBuilder(IAbpWebCommonModuleConfiguration configuration, ILocalizationManager localizationManager)
{
Converter = new DefaultErrorInfoConverter(configuration, localizationManager);
} /// <inheritdoc/>
public ErrorInfo BuildForException(Exception exception)
{
return Converter.Convert(exception);
} /// <summary>
/// Adds an exception converter that is used by <see cref="BuildForException"/> method.
/// </summary>
/// <param name="converter">Converter object</param>
public void AddExceptionConverter(IExceptionToErrorInfoConverter converter)
{
converter.Next = Converter;
Converter = converter;
}
}
Converter.Convert(exception); 这个主要封装不同的异常信息,并且把它返回,返回类型是ErrorInfo,他的实现类是DefaultErrorInfoConvert,代码如下:
internal class DefaultErrorInfoConverter : IExceptionToErrorInfoConverter
{
private readonly IAbpWebCommonModuleConfiguration _configuration;
private readonly ILocalizationManager _localizationManager; public IExceptionToErrorInfoConverter Next { set; private get; } private bool SendAllExceptionsToClients
{
get
{
return _configuration.SendAllExceptionsToClients;
}
} public DefaultErrorInfoConverter(
IAbpWebCommonModuleConfiguration configuration,
ILocalizationManager localizationManager)
{
_configuration = configuration;
_localizationManager = localizationManager;
} public ErrorInfo Convert(Exception exception)
{
var errorInfo = CreateErrorInfoWithoutCode(exception); if (exception is IHasErrorCode)
{
errorInfo.Code = (exception as IHasErrorCode).Code;
} return errorInfo;
} private ErrorInfo CreateErrorInfoWithoutCode(Exception exception)
{
if (SendAllExceptionsToClients)
{
return CreateDetailedErrorInfoFromException(exception);
} if (exception is AggregateException && exception.InnerException != null)
{
var aggException = exception as AggregateException;
if (aggException.InnerException is UserFriendlyException ||
aggException.InnerException is AbpValidationException)
{
exception = aggException.InnerException;
}
} if (exception is UserFriendlyException)
{
var userFriendlyException = exception as UserFriendlyException;
return new ErrorInfo(userFriendlyException.Message, userFriendlyException.Details);
} if (exception is AbpValidationException)
{
return new ErrorInfo(L("ValidationError"))
{
ValidationErrors = GetValidationErrorInfos(exception as AbpValidationException),
Details = GetValidationErrorNarrative(exception as AbpValidationException)
};
} if (exception is EntityNotFoundException)
{
var entityNotFoundException = exception as EntityNotFoundException; if (entityNotFoundException.EntityType != null)
{
return new ErrorInfo(
string.Format(
L("EntityNotFound"),
entityNotFoundException.EntityType.Name,
entityNotFoundException.Id
)
);
} return new ErrorInfo(
entityNotFoundException.Message
);
} if (exception is Abp.Authorization.AbpAuthorizationException)
{
var authorizationException = exception as Abp.Authorization.AbpAuthorizationException;
return new ErrorInfo(authorizationException.Message);
} return new ErrorInfo(L("InternalServerError"));
} private ErrorInfo CreateDetailedErrorInfoFromException(Exception exception)
{
var detailBuilder = new StringBuilder(); AddExceptionToDetails(exception, detailBuilder); var errorInfo = new ErrorInfo(exception.Message, detailBuilder.ToString()); if (exception is AbpValidationException)
{
errorInfo.ValidationErrors = GetValidationErrorInfos(exception as AbpValidationException);
} return errorInfo;
} private void AddExceptionToDetails(Exception exception, StringBuilder detailBuilder)
{
//Exception Message
detailBuilder.AppendLine(exception.GetType().Name + ": " + exception.Message); //Additional info for UserFriendlyException
if (exception is UserFriendlyException)
{
var userFriendlyException = exception as UserFriendlyException;
if (!string.IsNullOrEmpty(userFriendlyException.Details))
{
detailBuilder.AppendLine(userFriendlyException.Details);
}
} //Additional info for AbpValidationException
if (exception is AbpValidationException)
{
var validationException = exception as AbpValidationException;
if (validationException.ValidationErrors.Count > )
{
detailBuilder.AppendLine(GetValidationErrorNarrative(validationException));
}
} //Exception StackTrace
if (!string.IsNullOrEmpty(exception.StackTrace))
{
detailBuilder.AppendLine("STACK TRACE: " + exception.StackTrace);
} //Inner exception
if (exception.InnerException != null)
{
AddExceptionToDetails(exception.InnerException, detailBuilder);
} //Inner exceptions for AggregateException
if (exception is AggregateException)
{
var aggException = exception as AggregateException;
if (aggException.InnerExceptions.IsNullOrEmpty())
{
return;
} foreach (var innerException in aggException.InnerExceptions)
{
AddExceptionToDetails(innerException, detailBuilder);
}
}
} 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(L("ValidationNarrativeTitle")); foreach (var validationResult in validationException.ValidationErrors)
{
detailBuilder.AppendFormat(" - {0}", validationResult.ErrorMessage);
detailBuilder.AppendLine();
} return detailBuilder.ToString();
} private string L(string name)
{
try
{
return _localizationManager.GetString(AbpWebConsts.LocalizaionSourceName, name);
}
catch (Exception)
{
return name;
}
}
}
以上ABP异常处理的源码分析,代码中我们实际使用如下:
异常ABP异常分类
ABP的异常基类源代码 ABPException的源代码如下
/// <summary>
/// Base exception type for those are thrown by Abp system for Abp specific exceptions.
/// </summary>
[Serializable]
public class AbpException : Exception
{
/// <summary>
/// Creates a new <see cref="AbpException"/> object.
/// </summary>
public AbpException()
{ } /// <summary>
/// Creates a new <see cref="AbpException"/> object.
/// </summary>
public AbpException(SerializationInfo serializationInfo, StreamingContext context)
: base(serializationInfo, context)
{ } /// <summary>
/// Creates a new <see cref="AbpException"/> object.
/// </summary>
/// <param name="message">Exception message</param>
public AbpException(string message)
: base(message)
{ } /// <summary>
/// Creates a new <see cref="AbpException"/> object.
/// </summary>
/// <param name="message">Exception message</param>
/// <param name="innerException">Inner exception</param>
public AbpException(string message, Exception innerException)
: base(message, innerException)
{ }
}
ABP通过ABPExceptionFilter拦截异常的源代码
public class AbpExceptionFilter : IExceptionFilter, ITransientDependency
{
//日志记录
public ILogger Logger { get; set; } //事件总线
public IEventBus EventBus { get; set; } // 错误信息构建器
private readonly IErrorInfoBuilder _errorInfoBuilder; // AspNetCore 相关的配置信息
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);
}
} /// <summary>
/// 包装异常
/// </summary>
/// <param name="context"></param>
protected virtual void HandleAndWrapException(ExceptionContext context)
{
//判断返回类型是否是objectresult,不是直接返回
if (!ActionResultHelper.IsObjectResult(context.ActionDescriptor.GetMethodInfo().ReturnType))
{
return;
} // 设置 HTTP 上下文响应所返回的错误代码,由具体异常决定。
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!
} //根据不同的异常返回不同的status
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; //
}
}
ABP异常处理包装返回的结果结构,如果不想包装,请使用特性 DontWarpResult
{
"result": {
"totalCount": ,
"items": []
},
"targetUrl": null,
"success": true,
"error": null,
"unAuthorizedRequest": false,
"__abp": true
}
ABP正常接口返回的数据结构如下
public abstract class AjaxResponseBase
{
// 目标 Url 地址
public string TargetUrl { get; set; } // 接口调用是否成功
public bool Success { get; set; } // 当接口调用失败时,错误信息存放在此处
public ErrorInfo Error { get; set; } // 是否是未授权的请求
public bool UnAuthorizedRequest { get; set; } // 用于标识接口是否基于 Abp 框架开发
public bool __abp { get; } = true;
}
显示额外的异常处理,需要在模块预处理的代码中加入:
Configuration.Modules.AbpWebCommon().SendAllExceptionsToClients = true;
实时监听异常处理,如果需要发生报警,重写异常处理事件
public class ExceptionEventHandler : IEventHandler, ITransientDependency
{
/// /// Handler handles the event by implementing this method.
/// /// Event data
public void HandleEvent(AbpHandledExceptionData eventData)
{
Console.WriteLine($"当前异常信息为:{eventData.Exception.Message}");
}
}
ABP 异常处理 第四篇的更多相关文章
- c++异常处理第四篇---不使用try catch语句,使用Loki::ScopeGuard
转载:神奇的Loki::ScopeGuard 2011-07-05 12:52:05 分类: C/C++ 转载:http://blog.csdn.net/fangqu/article/details/ ...
- abp学习(四)——根据入门教程(aspnetMVC Web API进一步学习)
Introduction With AspNet MVC Web API EntityFramework and AngularJS 地址:https://aspnetboilerplate.com/ ...
- 从0开始搭建SQL Server AlwaysOn 第四篇(配置异地机房节点)
从0开始搭建SQL Server AlwaysOn 第四篇(配置异地机房节点) 第一篇http://www.cnblogs.com/lyhabc/p/4678330.html第二篇http://www ...
- ABP框架实践基础篇之开发UI层
返回总目录<一步一步使用ABP框架搭建正式项目系列教程> 说明 其实最开始写的,就是这个ABP框架实践基础篇.在写这篇博客之前,又回头复习了一下ABP框架的理论,如果你还没学习,请查看AB ...
- 第四篇 Entity Framework Plus 之 Batch Operations
用 Entity Framework 进行 增,删,改.都是基于Model进行的,且Model都是有状态追踪的.这样Entity Framework才能正常增,删,改. 有时候,要根据某个字段,批量 ...
- 【第四篇】ASP.NET MVC快速入门之完整示例(MVC5+EF6)
目录 [第一篇]ASP.NET MVC快速入门之数据库操作(MVC5+EF6) [第二篇]ASP.NET MVC快速入门之数据注解(MVC5+EF6) [第三篇]ASP.NET MVC快速入门之安全策 ...
- 解剖SQLSERVER 第十四篇 Vardecimals 存储格式揭秘(译)
解剖SQLSERVER 第十四篇 Vardecimals 存储格式揭秘(译) http://improve.dk/how-are-vardecimals-stored/ 在这篇文章,我将深入研究 ...
- 解剖SQLSERVER 第四篇 OrcaMDF里对dates类型数据的解析(译)
解剖SQLSERVER 第四篇 OrcaMDF里对dates类型数据的解析(译) http://improve.dk/parsing-dates-in-orcamdf/ 在SQLSERVER里面有几 ...
- 深入理解javascript作用域系列第四篇——块作用域
× 目录 [1]let [2]const [3]try 前面的话 尽管函数作用域是最常见的作用域单元,也是现行大多数javascript最普遍的设计方法,但其他类型的作用域单元也是存在的,并且通过使用 ...
随机推荐
- 黄聪:用 CSS 实现元素垂直居中,有哪些好的方案?
1.不知道自己高度和父容器高度的情况下, 利用绝对定位只需要以下三行: parentElement{ position:relative; } childElement{ position: abso ...
- Android Dialog.dismiss()与Activity.finish()顺序
activity.finish() 和dialog.show() 同时调用的时候, 需要先调用dialog.dismiss() 后activity.finish() 如果先直接finish()后,再触 ...
- solr6.4.1搜索引擎(5)使用zookeeper3.4.9分布式集群
本文讲的是如何使用zookeeper将solr分布式部署,也可以理解为tomcat分布式部署. 为什么要使用zookeeper,请参考文章<Solr的SolrCloud与Master-slave ...
- ORACLE procedure 一个参数分解成多个字符一点建议
测试时给什么变量就会生成什么变量, 但是在PROCEDURE时,你给的变量就会变成去掉包含字符q'/ /' 使用procedure splice添加字符串结果,是不包含q'/.删除时用的riqi赋值语 ...
- [UE4]Native Widget Host
一.Native Widget Host是一个容器,它可以包含一个Slate UI 二.Native Widget Host应该用在当你需要把一个Slate UI 放到UMG中的时候,只有这个时候才需 ...
- ScrollView滑动到底部或顶部监听,ScrollView滑动到底部或顶部再继续滑动监听;
ScrollView滑动到底部或顶部后,再继续滑动达到一定距离的监听: ScrollView滑动到底部或顶部的监听: /** * 监听ScrollView滚动到顶部或者底部做相关事件拦截 */ pub ...
- 拼接html
var html='<tbody>\ <tr class="print-tr-top">\ <td width="50%" col ...
- oracle用户的管理
用户登陆 sql>conn 用户名/密码 给用户修改密码 如果给自己修改密码可以直接使用 sql>password 用户名; 如果给别人修改密码则需要具有dba的权限,或是拥有alter ...
- Django之model联表:一对多、跨表操作,联表查询
表结构概述 model.py : class Something(models.Model): name = models.CharField(max_length=32) class UserTyp ...
- jsonArray返回
dao <select id="selectShopInfo" resultType="java.util.HashMap"> SELECT * F ...