Reputation: 3142
I have a custom exception middleware added to the ASP.NET Core 3.1 (WebAPI) project. The middleware is pretty simple, its catching specific types of exceptions and passes the appropriate message to the consumer while it sends generic error message for everything else.
Here is the middleware class:
/// <summary>
/// Middleware which catches controller exceptions and extracts exceptions which are specifically intended for the
/// client and sends them as a regular response (and not 500 server error).
/// </summary>
public class ExceptionMiddleware
{
private readonly RequestDelegate _next;
private ILogger<ExceptionMiddleware> _logger;
public ExceptionMiddleware(RequestDelegate next, ILogger<ExceptionMiddleware> logger)
{
_next = next;
_logger = logger;
}
public async Task Invoke(HttpContext context)
{
try
{
await _next(context);
}
catch (MDEException ex) // we only care about this particular exception
{
// Send exception message as plain message
_logger.Log(LogLevel.Error, ex.Message);
context.Response.ContentType = "text/plain";
context.Response.StatusCode = (int)HttpStatusCode.BadRequest;
await context.Response.WriteAsync(ex.Message);
}
catch (Exception ex)
{
// Send generic error as plain message
_logger.Log(LogLevel.Error, ex, ex.Message);
context.Response.ContentType = "text/plain";
context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
await context.Response.WriteAsync("Es trat ein unvorhergesehenes Problem auf - bitte kontaktieren Sie Ihren Administrator!");
}
}
}
This all works fine except when I send an invalid URL to the application. In this case my middleware is not even touched. For example if I use this URL: api/vm/ziel-standort/stellplatzId=2
I get an error similar to this:
{"errors":{"stellplatzId":["The value 'stellplatzId=2' is not valid."]},"type":"https://tools.ietf.org/html/rfc7231#section-6.5.1","title":"One or more validation errors occurred.","status":400,"traceId":"|d7456c7b-469208468c4ac571."}
Here is how my Configure method looks like:
/// <summary>
/// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
/// </summary>
/// <param name="app">Application builder (injected by framework)</param>
/// <param name="log">Logger (injected by framework)</param>
/// <param name="actionProvider">Action provider for listing routes (injected by framework)</param>
public void Configure(IApplicationBuilder app, ILogger<Startup> log, Microsoft.AspNetCore.Mvc.Infrastructure.IActionDescriptorCollectionProvider actionProvider)
{
log.Log(LogLevel.Information, "Configuring application ...");
log.Log(LogLevel.Debug, "Using CorsPolicy");
app.UseCors("CorsPolicy");
log.Log(LogLevel.Debug, "Using Exception middleware (MDEException message pass-through to client)");
app.UseMiddleware<ExceptionMiddleware>();
log.Log(LogLevel.Debug, "Enabling routing");
app.UseRouting();
log.Log(LogLevel.Debug, "Enabling authentication and authorization");
app.UseAuthentication();
app.UseAuthorization();
log.Log(LogLevel.Debug, "Setting up routing for controllers");
app.UseEndpoints(opt =>
{
opt.MapControllers();
});
// .....
}
How can I make my middleware catch this error?
Upvotes: 3
Views: 1441
Reputation: 3193
You are getting model validation error, not exception. To override that you can use the below code in ConfigureServices
method of Startup
Class:
services.AddControllers()
.ConfigureApiBehaviorOptions(o =>
{
o.InvalidModelStateResponseFactory = context =>
{
var _logger = context.HttpContext.RequestServices.GetRequiredService<ILogger<Startup>>();
var ex = new Exception(context.ModelState.Values.First().Errors.First().ErrorMessage);
_logger.Log(LogLevel.Error, ex, ex.Message);
context.HttpContext.Response.ContentType = "text/plain";
context.HttpContext.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
context.HttpContext.Response.WriteAsync("Es trat ein unvorhergesehenes Problem auf - bitte kontaktieren Sie Ihren Administrator!");
return new EmptyResult();
};
});
And, this is how exception handling should be done based upon Microsoft docs:
app.UseExceptionHandler(errorApp =>
{
errorApp.Run(async context =>
{
var ex = context.Features.Get<IExceptionHandlerPathFeature>()?.Error;
if (ex is MDEException) // we only care about this particular exception
{
// Send exception message as plain message
_logger.Log(LogLevel.Error, ex.Message);
context.Response.ContentType = "text/plain";
context.Response.StatusCode = (int)HttpStatusCode.BadRequest;
await context.Response.WriteAsync(ex.Message);
}
else
{
// Send generic error as plain message
_logger.Log(LogLevel.Error, ex, ex.Message);
context.Response.ContentType = "text/plain";
context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
await context.Response.WriteAsync("Es trat ein unvorhergesehenes Problem auf - bitte kontaktieren Sie Ihren Administrator!");
}
});
});
Upvotes: 3