[Abp vNext 源码分析] - 9. 接口参数的验证

ABP vNext 当中的审计模块早在 依赖注入与拦截器一文中有所提及,但没有详细的对其进行分析。

审计模块是 ABP vNext 框架的一个基本组件,它能够提供一些实用日志记录。不过这里的日志不是说系统日志,而是说接口每次调用之后的执行情况(执行时间、传入参数、异常信息、请求 IP)。

除了常规的日志功能以外,关于 实体聚合 的审计字段接口也是存放在审计模块当中的。(创建人创建时间修改人修改时间删除人删除时间

二、源码分析 2.1. 审计日志拦截器 2.1.1 审计日志拦截器的注册

Volo.Abp.Auditing 的模块定义十分简单,主要是提供了 审计日志拦截器 的注册功能。下面代码即在组件注册的时候,会调用 AuditingInterceptorRegistrar.RegisterIfNeeded 方法来判定是否为实现类型(ImplementationType) 注入审计日志拦截器。

public class AbpAuditingModule : AbpModule { public override void PreConfigureServices(ServiceConfigurationContext context) { context.Services.OnRegistred(AuditingInterceptorRegistrar.RegisterIfNeeded); } }

跳转到具体的实现,可以看到内部会结合三种类型进行判断。分别是 AuditedAttribute 、IAuditingEnabled、DisableAuditingAttribute 。

前两个作用是,只要类型标注了 AuditedAttribute 特性,或者是实现了 IAuditingEnable 接口,都会为该类型注入审计日志拦截器。

而 DisableAuditingAttribute 类型则相反,只要类型上标注了该特性,就不会启用审计日志拦截器。某些接口需要 提升性能 的话,可以尝试使用该特性禁用掉审计日志功能。

public static class AuditingInterceptorRegistrar { public static void RegisterIfNeeded(IOnServiceRegistredContext context) { // 满足条件时,将会为该类型注入审计日志拦截器。 if (ShouldIntercept(context.ImplementationType)) { context.Interceptors.TryAdd<AuditingInterceptor>(); } } private static bool ShouldIntercept(Type type) { // 首先判断类型上面是否使用了辅助类型。 if (ShouldAuditTypeByDefault(type)) { return true; } // 如果任意方法上面标注了 AuditedAttribute 特性,则仍然为该类型注入拦截器。 if (type.GetMethods().Any(m => m.IsDefined(typeof(AuditedAttribute), true))) { return true; } return false; } //TODO: Move to a better place public static bool ShouldAuditTypeByDefault(Type type) { // 下面就是根据三种辅助类型进行判断,是否为当前 type 注入审计日志拦截器。 if (type.IsDefined(typeof(AuditedAttribute), true)) { return true; } if (type.IsDefined(typeof(DisableAuditingAttribute), true)) { return false; } if (typeof(IAuditingEnabled).IsAssignableFrom(type)) { return true; } return false; } } 2.1.2 审计日志拦截器的实现

审计日志拦截器的内部实现,主要使用了三个类型进行协同工作。它们分别是负责管理审计日志信息的 IAuditingManager,负责创建审计日志信息的 IAuditingHelper,还有统计接口执行时常的 Stopwatch 。

整个审计日志拦截器的大体流程如下:

首先是判定 MVC 审计日志过滤器是否进行处理。

再次根据特性,和类型进行二次验证是否应该创建审计日志信息。

根据调用信息,创建 AuditLogInfo 和 AuditLogActionInfo 审计日志信息。

调用 StopWatch 的计时方法,如果出现了异常则将异常信息添加到刚才构建的 AuditLogInfo 对象中。

无论是否出现异常,都会进入 finally 语句块,这个时候会调用 StopWatch 实例的停止方法,并统计完成执行时间。

public override async Task InterceptAsync(IAbpMethodInvocation invocation) { if (!ShouldIntercept(invocation, out var auditLog, out var auditLogAction)) { await invocation.ProceedAsync(); return; } // 开始进行计时操作。 var stopwatch = Stopwatch.StartNew(); try { await invocation.ProceedAsync(); } catch (Exception ex) { // 如果出现了异常,一样的将异常信息添加到审计日志结果中。 auditLog.Exceptions.Add(ex); throw; } finally { // 统计完成,并将信息加入到审计日志结果中。 stopwatch.Stop(); auditLogAction.ExecutionDuration = Convert.ToInt32(stopwatch.Elapsed.TotalMilliseconds); auditLog.Actions.Add(auditLogAction); } }

可以看到,只有当 ShouldIntercept() 方法返回 true 的时候,下面的统计等操作才会被执行。

protected virtual bool ShouldIntercept( IAbpMethodInvocation invocation, out AuditLogInfo auditLog, out AuditLogActionInfo auditLogAction) { auditLog = null; auditLogAction = null; if (AbpCrossCuttingConcerns.IsApplied(invocation.TargetObject, AbpCrossCuttingConcerns.Auditing)) { return false; } // 如果没有获取到 Scop,则返回 false。 var auditLogScope = _auditingManager.Current; if (auditLogScope == null) { return false; } // 进行二次判断是否需要存储审计日志。 if (!_auditingHelper.ShouldSaveAudit(invocation.Method)) { return false; } // 构建审计日志信息。 auditLog = auditLogScope.Log; auditLogAction = _auditingHelper.CreateAuditLogAction( auditLog, invocation.TargetObject.GetType(), invocation.Method, invocation.Arguments ); return true; } 2.2 审计日志的持久化

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zysgws.html