我已经看到了在ASP.NET MVC Core中捕获异常的多种方法,但我需要理解为什么我所做的并不像我预期的那样工作。
我添加了以下内容:
public abstract class GenericActionFilter : ActionFilterAttribute
{
protected readonly ILog Logger = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
public override async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
{
if (await OnBeforeActionExecutionAsync(context))
{
var executed = await next();
if (executed.Exception != null && !executed.ExceptionHandled)
{
await OnExceptionAsync(context, executed.Exception);
}
else
{
await OnAfterActionExecutionAsync(context);
}
}
}
public virtual Task<bool> OnBeforeActionExecutionAsync(ActionExecutingContext context)
{
return Task.FromResult(true);
}
public virtual Task OnAfterActionExecutionAsync(ActionExecutingContext context)
{
return Task.CompletedTask;
}
public virtual Task OnExceptionAsync(ActionExecutingContext context, Exception ex)
{
return Task.CompletedTask;
}
}
并像这样使用它:
public class ExceptionFilter : GenericActionFilter
{
public IntegrationScenarioSettings Settings { get; set; }
public override Task OnExceptionAsync(ActionExecutingContext context, Exception ex)
{
context.Result = new ContentResult
{
Content = Settings.ApiExecutionExceptionMessage,
StatusCode = (int)HttpStatusCode.ServiceUnavailable
};
//outputs to endpoint.log
Logger.Error(ex);
return Task.CompletedTask;
}
}
操作中的底层代码抛出一个异常而不是看到503,我仍然是500。
我在这里缺少什么?
为了通知ASP.NET Core MVC管道您已处理异常,您需要将ActionExecutedContext.ExceptionHandled
设置为true
。因为您在显示的代码中没有这个,所以ASP.NET Core MVC管道使用自己的错误处理逻辑将您的(它认为的)未处理的异常转换为500响应。
现在 - 这个属性存在于ActionExecutedContext
而不是ActionExecutingContext
(您在代码中使用)。这是有道理的,因为ActionExecutingContext
表示动作运行前的状态,ActionExecutedContext
表示动作运行后的状态。这意味着您将需要以下一组更改:
OnExceptionAsync
函数以取ActionExecutedContext
而不是ActionExecutingContext
。OnExceptionAsync
的调用,提供executed
而不是context
。虽然你在这里,你也可以将方法参数折叠到executed
(我将在下面的代码中显示)。context.ExceptionHandled
设置为true
。 :)我从您的问题中获取了代码,删除了与该问题无关的一些代码,并应用了这些更改,我已使用上面的相应数字调出:
public abstract class GenericActionFilter : ActionFilterAttribute
{
public override async Task OnActionExecutionAsync(ActionExecutingContext context,
ActionExecutionDelegate next)
{
if (await OnBeforeActionExecutionAsync(context))
{
var executed = await next();
if (executed.Exception != null && !executed.ExceptionHandled)
{
await OnExceptionAsync(executed); // #2.
}
else
{
// NOTE: You might want to use executed here too.
await OnAfterActionExecutionAsync(context);
}
}
}
// ...
public virtual Task OnExceptionAsync(ActionExecutedContext context) // #1.
{
return Task.CompletedTask;
}
}
public class ExceptionFilter : GenericActionFilter
{
public override Task OnExceptionAsync(ActionExecutedContext context) // #1, #2.
{
Logger.Error(context.Exception); // #2 (single parameter).
context.Result = new ContentResult { ... };
context.ExceptionHandled = true; // #3.
return Task.CompletedTask;
}
}