Search code examples
c#error-handlingmiddleware

ErrorHandling Middleware catch is never caught


I have this ErrorHandlingMiddleware that looks like this:

public class ErrorHandlingMiddleware
{
    private readonly RequestDelegate _next;
    public ErrorHandlingMiddleware(RequestDelegate next)
    {
        this._next = next;
    }

    public async Task Invoke(HttpContext context /* other dependencies */)
    {
        try
        {
            await _next(context);
        }
        catch (Exception ex)
        {
            await HandleExceptionAsync(context, ex);
        }
    }

    private static Task HandleExceptionAsync(HttpContext context, Exception ex)
    {
        var statusCode = (int)HttpStatusCode.InternalServerError;

        if (ex is NotFoundError) statusCode = (int)HttpStatusCode.NotFound;
        //else if (ex is MyUnauthorizedException) code = HttpStatusCode.Unauthorized;
        //else if (ex is MyException) code = HttpStatusCode.BadRequest;

        var error = new AttemptError(statusCode, ex.Message, ex);

        context.Response.ContentType = "application/json";
        context.Response.StatusCode = statusCode;

        return context.Response.WriteAsync(error.ToString());
    }
}

And I have added this to my Startup class:

public void Configure(IApplicationBuilder app)
{
    app.UseMiddleware<ErrorHandlingMiddleware>();
    app.SeedIdentityServerDatabase();
    app.UseDeveloperExceptionPage();

    app.UseIdentityServer();
    app.UseSwagger();
    app.UseSwaggerUI(c =>
    {
        c.SwaggerEndpoint("/swagger/v1/swagger.json", "r3plica Identity Server v1");
        c.OAuthClientId("swagger");
        c.OAuthAppName("Swagger Api UI");
    });
    app.UseMvc();
}

I would expect that if I was anywhere in my application and I throw an exception, it would be caught and it would execute this line:

await HandleExceptionAsync(context, ex);

So, I set up a test:

throw new Exception();

Which is thrown in my controller. When I run my application and then call the endpoint that has that exception thrown, it does indeed get to the Invoke method of my ErrorHandlingMiddleware, but instead of an exception being caught, it just goes to the await _next(context)....

Does anyone know what I am doing wrong?


Solution

  • I fixed this by creating a filter:

    public class HttpResponseExceptionFilter : IActionFilter, IOrderedFilter
    {
        public int Order { get; set; } = int.MaxValue - 10;
    
        public void OnActionExecuting(ActionExecutingContext context) { }
    
        public void OnActionExecuted(ActionExecutedContext context)
        {
            if (context.Exception == null) return;
    
            var attempt = Attempt<string>.Fail(context.Exception);
    
            if (context.Exception is AttemptException exception)
            {
                context.Result = new ObjectResult(attempt)
                {
                    StatusCode = exception.StatusCode,
                };
            }
            else
            {
                context.Result = new ObjectResult(attempt)
                {
                    StatusCode = (int)HttpStatusCode.InternalServerError,
                };
            }
    
            context.ExceptionHandled = true;
        }
    }
    

    And registering it like this:

    services.AddControllers(options => options.Filters.Add(new HttpResponseExceptionFilter()));