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?
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()));