mirror of https://github.com/abpframework/abp.git
6 changed files with 406 additions and 2 deletions
@ -0,0 +1,103 @@ |
|||||
|
using System; |
||||
|
using System.Diagnostics; |
||||
|
using System.Threading.Tasks; |
||||
|
using Microsoft.AspNetCore.Mvc.Abstractions; |
||||
|
using Microsoft.AspNetCore.Mvc.Filters; |
||||
|
using Microsoft.Extensions.Options; |
||||
|
using Volo.Abp.Aspects; |
||||
|
using Volo.Abp.Auditing; |
||||
|
using Volo.Abp.DependencyInjection; |
||||
|
|
||||
|
namespace Volo.Abp.AspNetCore.Mvc.Auditing |
||||
|
{ |
||||
|
public class AbpAuditPageFilter : IAsyncPageFilter, ITransientDependency |
||||
|
{ |
||||
|
protected AbpAuditingOptions Options { get; } |
||||
|
private readonly IAuditingHelper _auditingHelper; |
||||
|
private readonly IAuditingManager _auditingManager; |
||||
|
|
||||
|
public AbpAuditPageFilter(IOptions<AbpAuditingOptions> options, IAuditingHelper auditingHelper, IAuditingManager auditingManager) |
||||
|
{ |
||||
|
Options = options.Value; |
||||
|
_auditingHelper = auditingHelper; |
||||
|
_auditingManager = auditingManager; |
||||
|
} |
||||
|
|
||||
|
public Task OnPageHandlerSelectionAsync(PageHandlerSelectedContext context) |
||||
|
{ |
||||
|
return Task.CompletedTask; |
||||
|
} |
||||
|
|
||||
|
public async Task OnPageHandlerExecutionAsync(PageHandlerExecutingContext context, PageHandlerExecutionDelegate next) |
||||
|
{ |
||||
|
if (context.HandlerMethod == null || !ShouldSaveAudit(context, out var auditLog, out var auditLogAction)) |
||||
|
{ |
||||
|
await next(); |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
using (AbpCrossCuttingConcerns.Applying(context.HandlerInstance, AbpCrossCuttingConcerns.Auditing)) |
||||
|
{ |
||||
|
var stopwatch = Stopwatch.StartNew(); |
||||
|
|
||||
|
try |
||||
|
{ |
||||
|
var result = await next(); |
||||
|
|
||||
|
if (result.Exception != null && !result.ExceptionHandled) |
||||
|
{ |
||||
|
auditLog.Exceptions.Add(result.Exception); |
||||
|
} |
||||
|
} |
||||
|
catch (Exception ex) |
||||
|
{ |
||||
|
auditLog.Exceptions.Add(ex); |
||||
|
throw; |
||||
|
} |
||||
|
finally |
||||
|
{ |
||||
|
stopwatch.Stop(); |
||||
|
auditLogAction.ExecutionDuration = Convert.ToInt32(stopwatch.Elapsed.TotalMilliseconds); |
||||
|
auditLog.Actions.Add(auditLogAction); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private bool ShouldSaveAudit(PageHandlerExecutingContext context, out AuditLogInfo auditLog, out AuditLogActionInfo auditLogAction) |
||||
|
{ |
||||
|
auditLog = null; |
||||
|
auditLogAction = null; |
||||
|
|
||||
|
if (!Options.IsEnabled) |
||||
|
{ |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
if (!context.ActionDescriptor.IsPageAction()) |
||||
|
{ |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
var auditLogScope = _auditingManager.Current; |
||||
|
if (auditLogScope == null) |
||||
|
{ |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
if (!_auditingHelper.ShouldSaveAudit(context.ActionDescriptor.GetMethodInfo(), true)) |
||||
|
{ |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
auditLog = auditLogScope.Log; |
||||
|
auditLogAction = _auditingHelper.CreateAuditLogAction( |
||||
|
auditLog, |
||||
|
context.ActionDescriptor.AsControllerActionDescriptor().ControllerTypeInfo.AsType(), |
||||
|
context.ActionDescriptor.AsControllerActionDescriptor().MethodInfo, |
||||
|
context.HandlerArguments |
||||
|
); |
||||
|
|
||||
|
return true; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,112 @@ |
|||||
|
using System; |
||||
|
using System.Threading.Tasks; |
||||
|
using Microsoft.AspNetCore.Http; |
||||
|
using Microsoft.AspNetCore.Mvc; |
||||
|
using Microsoft.AspNetCore.Mvc.Abstractions; |
||||
|
using Microsoft.AspNetCore.Mvc.Filters; |
||||
|
using Microsoft.Extensions.DependencyInjection; |
||||
|
using Microsoft.Extensions.Logging; |
||||
|
using Microsoft.Extensions.Logging.Abstractions; |
||||
|
using Volo.Abp.AspNetCore.ExceptionHandling; |
||||
|
using Volo.Abp.DependencyInjection; |
||||
|
using Volo.Abp.ExceptionHandling; |
||||
|
using Volo.Abp.Http; |
||||
|
using Volo.Abp.Json; |
||||
|
|
||||
|
namespace Volo.Abp.AspNetCore.Mvc.ExceptionHandling |
||||
|
{ |
||||
|
public class AbpExceptionPageFilter : IAsyncPageFilter, ITransientDependency |
||||
|
{ |
||||
|
public ILogger<AbpExceptionPageFilter> Logger { get; set; } |
||||
|
|
||||
|
private readonly IExceptionToErrorInfoConverter _errorInfoConverter; |
||||
|
private readonly IHttpExceptionStatusCodeFinder _statusCodeFinder; |
||||
|
private readonly IJsonSerializer _jsonSerializer; |
||||
|
|
||||
|
public AbpExceptionPageFilter( |
||||
|
IExceptionToErrorInfoConverter errorInfoConverter, |
||||
|
IHttpExceptionStatusCodeFinder statusCodeFinder, |
||||
|
IJsonSerializer jsonSerializer) |
||||
|
{ |
||||
|
_errorInfoConverter = errorInfoConverter; |
||||
|
_statusCodeFinder = statusCodeFinder; |
||||
|
_jsonSerializer = jsonSerializer; |
||||
|
|
||||
|
Logger = NullLogger<AbpExceptionPageFilter>.Instance; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
public Task OnPageHandlerSelectionAsync(PageHandlerSelectedContext context) |
||||
|
{ |
||||
|
return Task.CompletedTask; |
||||
|
} |
||||
|
|
||||
|
public async Task OnPageHandlerExecutionAsync(PageHandlerExecutingContext context, PageHandlerExecutionDelegate next) |
||||
|
{ |
||||
|
if (context.HandlerMethod == null || !ShouldHandleException(context)) |
||||
|
{ |
||||
|
await next(); |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
var pageHandlerExecutedContext = await next(); |
||||
|
if (pageHandlerExecutedContext.Exception == null) |
||||
|
{ |
||||
|
return;; |
||||
|
} |
||||
|
|
||||
|
await HandleAndWrapException(pageHandlerExecutedContext); |
||||
|
} |
||||
|
|
||||
|
protected virtual bool ShouldHandleException(PageHandlerExecutingContext context) |
||||
|
{ |
||||
|
//TODO: Create DontWrap attribute to control wrapping..?
|
||||
|
|
||||
|
if (context.ActionDescriptor.IsPageAction() && |
||||
|
context.ActionDescriptor.HasObjectResult()) |
||||
|
{ |
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
if (context.HttpContext.Request.CanAccept(MimeTypes.Application.Json)) |
||||
|
{ |
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
if (context.HttpContext.Request.IsAjax()) |
||||
|
{ |
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
protected virtual async Task HandleAndWrapException(PageHandlerExecutedContext context) |
||||
|
{ |
||||
|
//TODO: Trigger an AbpExceptionHandled event or something like that.
|
||||
|
|
||||
|
context.HttpContext.Response.Headers.Add(AbpHttpConsts.AbpErrorFormat, "true"); |
||||
|
context.HttpContext.Response.StatusCode = (int)_statusCodeFinder.GetStatusCode(context.HttpContext, context.Exception); |
||||
|
|
||||
|
var remoteServiceErrorInfo = _errorInfoConverter.Convert(context.Exception); |
||||
|
|
||||
|
context.Result = new ObjectResult(new RemoteServiceErrorResponse(remoteServiceErrorInfo)); |
||||
|
|
||||
|
var logLevel = context.Exception.GetLogLevel(); |
||||
|
|
||||
|
Logger.LogWithLevel(logLevel, $"---------- {nameof(RemoteServiceErrorInfo)} ----------"); |
||||
|
Logger.LogWithLevel(logLevel, _jsonSerializer.Serialize(remoteServiceErrorInfo, indented: true)); |
||||
|
Logger.LogException(context.Exception, logLevel); |
||||
|
|
||||
|
await context.HttpContext |
||||
|
.RequestServices |
||||
|
.GetRequiredService<IExceptionNotifier>() |
||||
|
.NotifyAsync( |
||||
|
new ExceptionNotificationContext(context.Exception) |
||||
|
); |
||||
|
|
||||
|
context.Exception = null; //Handled!
|
||||
|
} |
||||
|
|
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,44 @@ |
|||||
|
using System.Threading.Tasks; |
||||
|
using Microsoft.AspNetCore.Mvc.Abstractions; |
||||
|
using Microsoft.AspNetCore.Mvc.Filters; |
||||
|
using Volo.Abp.Aspects; |
||||
|
using Volo.Abp.DependencyInjection; |
||||
|
using Volo.Abp.Features; |
||||
|
|
||||
|
namespace Volo.Abp.AspNetCore.Mvc.Features |
||||
|
{ |
||||
|
public class AbpFeaturePageFilter : IAsyncPageFilter, ITransientDependency |
||||
|
{ |
||||
|
private readonly IMethodInvocationFeatureCheckerService _methodInvocationAuthorizationService; |
||||
|
|
||||
|
public AbpFeaturePageFilter(IMethodInvocationFeatureCheckerService methodInvocationAuthorizationService) |
||||
|
{ |
||||
|
_methodInvocationAuthorizationService = methodInvocationAuthorizationService; |
||||
|
} |
||||
|
|
||||
|
public Task OnPageHandlerSelectionAsync(PageHandlerSelectedContext context) |
||||
|
{ |
||||
|
return Task.CompletedTask; |
||||
|
} |
||||
|
|
||||
|
public async Task OnPageHandlerExecutionAsync(PageHandlerExecutingContext context, PageHandlerExecutionDelegate next) |
||||
|
{ |
||||
|
if (context.HandlerMethod == null || !context.ActionDescriptor.IsPageAction()) |
||||
|
{ |
||||
|
await next(); |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
var methodInfo = context.ActionDescriptor.GetMethodInfo(); |
||||
|
|
||||
|
using (AbpCrossCuttingConcerns.Applying(context.HandlerInstance, AbpCrossCuttingConcerns.FeatureChecking)) |
||||
|
{ |
||||
|
await _methodInvocationAuthorizationService.CheckAsync( |
||||
|
new MethodInvocationFeatureCheckerContext(methodInfo) |
||||
|
); |
||||
|
|
||||
|
await next(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,105 @@ |
|||||
|
using System; |
||||
|
using System.Net.Http; |
||||
|
using System.Threading.Tasks; |
||||
|
using Microsoft.AspNetCore.Mvc.Abstractions; |
||||
|
using Microsoft.AspNetCore.Mvc.Filters; |
||||
|
using Microsoft.Extensions.Options; |
||||
|
using Volo.Abp.AspNetCore.Uow; |
||||
|
using Volo.Abp.DependencyInjection; |
||||
|
using Volo.Abp.Uow; |
||||
|
|
||||
|
namespace Volo.Abp.AspNetCore.Mvc.Uow |
||||
|
{ |
||||
|
public class AbpUowPageFilter : IAsyncPageFilter, ITransientDependency |
||||
|
{ |
||||
|
private readonly IUnitOfWorkManager _unitOfWorkManager; |
||||
|
private readonly AbpUnitOfWorkDefaultOptions _defaultOptions; |
||||
|
|
||||
|
public AbpUowPageFilter(IUnitOfWorkManager unitOfWorkManager, IOptions<AbpUnitOfWorkDefaultOptions> options) |
||||
|
{ |
||||
|
_unitOfWorkManager = unitOfWorkManager; |
||||
|
_defaultOptions = options.Value; |
||||
|
} |
||||
|
public Task OnPageHandlerSelectionAsync(PageHandlerSelectedContext context) |
||||
|
{ |
||||
|
return Task.CompletedTask; |
||||
|
} |
||||
|
|
||||
|
public async Task OnPageHandlerExecutionAsync(PageHandlerExecutingContext context, PageHandlerExecutionDelegate next) |
||||
|
{ |
||||
|
if (context.HandlerMethod == null || !context.ActionDescriptor.IsPageAction()) |
||||
|
{ |
||||
|
await next(); |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
var methodInfo = context.ActionDescriptor.GetMethodInfo(); |
||||
|
var unitOfWorkAttr = UnitOfWorkHelper.GetUnitOfWorkAttributeOrNull(methodInfo); |
||||
|
|
||||
|
context.HttpContext.Items["_AbpActionInfo"] = new AbpActionInfoInHttpContext |
||||
|
{ |
||||
|
IsObjectResult = context.ActionDescriptor.HasObjectResult() |
||||
|
}; |
||||
|
|
||||
|
if (unitOfWorkAttr?.IsDisabled == true) |
||||
|
{ |
||||
|
await next(); |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
var options = CreateOptions(context, unitOfWorkAttr); |
||||
|
|
||||
|
//Trying to begin a reserved UOW by AbpUnitOfWorkMiddleware
|
||||
|
if (_unitOfWorkManager.TryBeginReserved(AbpUnitOfWorkMiddleware.UnitOfWorkReservationName, options)) |
||||
|
{ |
||||
|
var result = await next(); |
||||
|
if (!Succeed(result)) |
||||
|
{ |
||||
|
await RollbackAsync(context); |
||||
|
} |
||||
|
|
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
//Begin a new, independent unit of work
|
||||
|
using (var uow = _unitOfWorkManager.Begin(options)) |
||||
|
{ |
||||
|
var result = await next(); |
||||
|
if (Succeed(result)) |
||||
|
{ |
||||
|
await uow.CompleteAsync(context.HttpContext.RequestAborted); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private AbpUnitOfWorkOptions CreateOptions(PageHandlerExecutingContext context, UnitOfWorkAttribute unitOfWorkAttribute) |
||||
|
{ |
||||
|
var options = new AbpUnitOfWorkOptions(); |
||||
|
|
||||
|
unitOfWorkAttribute?.SetOptions(options); |
||||
|
|
||||
|
if (unitOfWorkAttribute?.IsTransactional == null) |
||||
|
{ |
||||
|
options.IsTransactional = _defaultOptions.CalculateIsTransactional( |
||||
|
autoValue: !string.Equals(context.HttpContext.Request.Method, HttpMethod.Get.Method, StringComparison.OrdinalIgnoreCase) |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
return options; |
||||
|
} |
||||
|
|
||||
|
private async Task RollbackAsync(PageHandlerExecutingContext context) |
||||
|
{ |
||||
|
var currentUow = _unitOfWorkManager.Current; |
||||
|
if (currentUow != null) |
||||
|
{ |
||||
|
await currentUow.RollbackAsync(context.HttpContext.RequestAborted); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private static bool Succeed(PageHandlerExecutedContext result) |
||||
|
{ |
||||
|
return result.Exception == null || result.ExceptionHandled; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
Loading…
Reference in new issue