【问题标题】:Exception handler middleware not catching异常处理程序中间件未捕获
【发布时间】:2017-06-20 15:34:43
【问题描述】:

我正在使用 ASP.NET Core 开发一个 Web API,并且我正在尝试实现一个自定义错误处理中间件,以便我可以抛出标准异常,这些异常可以使用适当的 HTTP 状态代码转换为 JSON 响应。

例如,如果我这样做:

throw new NotFoundApiException("The object was not found");

我需要把它转换成:

StatusCode: 404
ContentType: application/json
ResponseBody: {"error": "The object was not found"}

这是我的中间件

public class ErrorHandlingMiddleware
{
    private readonly RequestDelegate next;

    public ErrorHandlingMiddleware(RequestDelegate next)
    {
        this.next = next;
    }

    public async Task Invoke(HttpContext context)
    {
        try {
            await next(context);
        } catch (ApiException ex) {
            await HandleExceptionAsync(context, ex);
        }
    }

    private static Task HandleExceptionAsync(HttpContext context, ApiException exception)
    {
        var result = JsonConvert.SerializeObject(new { error = exception.Message });
        context.Response.ContentType = "application/json";
        context.Response.StatusCode = exception.httpStatusCode;

        return context.Response.WriteAsync(result);
    }
}

例外情况

public class ApiException : System.Exception
{
    private int _httpStatusCode = (int)HttpStatusCode.InternalServerError;
    public ApiException() { }
    public ApiException(string message): base(message) { }

    public int httpStatusCode {
        get { return this._httpStatusCode; }
    }
}
public class NotFoundApiException : ApiException
{
    private int _httpStatusCode = (int)HttpStatusCode.BadRequest;
    public NotFoundApiException() { }
    public NotFoundApiException(string message): base(message) { }
}

启动

public void Configure(/*...*/)
{
    loggerFactory.AddConsole();
    app.UseMiddleware<ErrorHandlingMiddleware>();
    app.UseMvc();
}

控制器动作

[HttpGet("object/{guid}")]
public WebMessage Get(Guid guid)
{
    throw new NotFoundApiException(string.Format("The object {0} was not found", guid));
//...

我可以看到请求进入了我注册的中间件,但异常没有被捕获,只是像往常一样简单地抛出

我怀疑是竞态条件或类似情况,实际上我对它们的异步函数了解不多。

有人知道为什么我的异常没有被捕获吗?


编辑通过使用 VisualStudio 继续执行,我可以看到预期的行为:我终于得到了回应。
似乎异常并没有真正被中间件捕获,而是在之后以某种方式处理。

【问题讨论】:

  • 今天也开始遇到这个问题。使用相同的中间件甚至无法捕获 Exception
  • 祝你好运,如果你发现了什么,请毫不犹豫地告诉你:) 我也会这样做

标签: c# asp.net asp.net-core


【解决方案1】:

我对这个问题的解决方案是删除Startup.cs中的app.UseDeveloperExceptionPage();

【讨论】:

  • 是的,这就是解决方案。
  • @Menahem 使用异常页面不是很有用,然后为 API 调用返回错误。如果您想用错误页面响应您的某些请求,您可以修改 ErrorHandlingMiddleware 以不同方式处理非 API 调用。检查 Invoke 方法并过滤 context.Request.Path 以获得正确的请求。
【解决方案2】:

就我而言,我发现app.UseMiddleware&lt;ExceptionHandlingMiddleware&gt;(); 应该在Configure() 方法的顶部。

【讨论】:

    【解决方案3】:

    你也可以试试Exception filters

    (当然,过滤器不像错误处理中间件那样灵活,这在一般情况下更好,但是 - 至少对我来说 - 过滤器工作正常,没有任何问题)

    这就是我正在使用的:

    public class ExceptionGlobalFilter : ExceptionFilterAttribute
    {
        private readonly ILogger logger;
    
        public ExceptionGlobalFilter(ILoggerFactory lf)
        {
            logger = lf.CreateLogger("ExceptionGlobalFilter");
        }
    
    
        public override void OnException(ExceptionContext context)
        {
            var customObject = new CustomObject(context.Exception);
    
            //TODO: Add logs
            if (context.Exception is BadRequestException)
            {
                context.Result = new BadRequestObjectResult(customObject);
            }
            else if (context.Exception is NotFoundException)
            {
                context.Result = new NotFoundObjectResult(customObject);
            }
            else
            {
                context.Result = new OkObjectResult(customObject);
            }
    
            base.OnException(context);
        }
    
    
        public override async Task OnExceptionAsync(ExceptionContext context)
        {
            await base.OnExceptionAsync(context);
            return;
        }
    }
    


    Startup.cs:

    services.AddMvc(config =>
    {
        config.Filters.Add(typeof(ExceptionGlobalFilter));
    });
    


    更多信息:

    【讨论】:

    • 我也试过了,我得到了完全相同的行为。但我刚刚看到,通过在 VisualStudio 中继续执行,响应是我所期望的。我会更新我的问题
    • 细节很少: 1) 这取决于您何时(以何种顺序)注册中间件。 2) 请注意,一旦发送了响应的标头,就无法更改响应的状态码。必须完成响应或中止连接。 3) 要在 ExceptionFilter 中处理异常,请将 ExceptionContext.ExceptionHandled 属性设置为 true 或写入响应。这会阻止异常的传播。 4) 在 ASP.NET 1.1 中,如果将 ExceptionHandled 设置为 true 并写入响应,则不会发送响应。请参阅 GitHub 存储库中的问题 #5594。
    • 特别是验证您是否不受与提到的问题 #5594 相关的更改的影响:github.com/aspnet/Mvc/issues/5594。问题的简短说明:在 ASP.NET 1.1 中,如果将 ExceptionHandled 设置为 true 并编写响应,则不会发送响应。在这种情况下,ASP.NET Core 1.0 确实会发送响应,而 ASP.NET Core 1.1.2 将返回到 1.0 的行为。
    • 另外,请查看 ExceptionHandlerMiddleware.cs 源代码:github.com/aspnet/Diagnostics/blob/master/src/… - 特别是查看第 50-54 行 if (context.Response.HasStarted) - 也许是您的情况?
    【解决方案4】:

    @Pierre,在使用中间件作为全局异常处理程序时,我在这里遇到了同样的问题。这个问题是由于我错误地编写了一个“async void”方法引起的,我在名为“NewException”的方法中抛出了一个异常:

    [Route("api/[controller]")]
    [ApiController]
    public class ValuesController : ControllerBase
    {
        // GET api/values
        [HttpGet]
        public async Task<IActionResult> Get()
        {
            NewException();
            return Ok("<h1>Hi, Welcome!</h1>");
        }
    
        private async void NewException()
        {
            throw new InvalidOperationException("WTF");
        }
    

    如果我将代码 sn-p 更改为:

    ,中间件将不会捕获异常 [InvalidOperationException("WTF")]
    [Route("api/[controller]")]
    [ApiController]
    public class ValuesController : ControllerBase
    {
        // GET api/values
        [HttpGet]
        public async Task<IActionResult> Get()
        {
            await NewException();
            return Ok("<h1>Hi, Welcome!</h1>");
        }
    
        private async Task NewException()
        {
            throw new InvalidOperationException("WTF");
        }
    

    异常中间件会捕获它。希望对您有所帮助。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2014-09-02
      • 2018-06-14
      • 2011-09-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-05-30
      相关资源
      最近更新 更多