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