In my applications based on ordinary MVC and WebApi I had two different error handling routes.
If an error occurred during WebApi call, I would intercept it (using standard web api options) and return json message with corresponding HTTP Status Code so that client app can handle it.
If the error happened in MVC, then I would use some standard handlers that would redirect user to some default error screen possibly based on status code.
Now in ASP.NET Core both are joined in the same framework, so if I just intercept and return JSON, then I risk showing json to a user, since it can be an action that returns a view. On the other hand if I use app.UseExceptionHandler
then my API calls would get HTML from the error page that is unusable inside js.
What is the good way to provide separate error handling for this two cases? Or perhaps there is a better way to handle it altogether?
P.S. I would rather reuse the MVC exception handler that comes out of the box and only add the web api part.
Using HttpError in ASP.NET Web API You can use the CreateErrorResponse extension method in your Web API controller method to return meaningful error codes and error messages. Note that the CreateErrorResponse method creates an HttpError object and then wraps it inside an HttpResponseMessage object.
You can customize how Web API handles exceptions by writing an exception filter. An exception filter is executed when a controller method throws any unhandled exception that is not an HttpResponseException exception.
You can handle default errors at the application level either by modifying your application's configuration or by adding an Application_Error handler in the Global. asax file of your application. You can handle default errors and HTTP errors by adding a customErrors section to the Web. config file.
There are many ways to achive your goal:
1- Using two different exception filter(i would go with this approach because your question is about mvc pipline)
Implementation:
// For api
public class ApiExceptionFilterAttribute : ExceptionFilterAttribute
{
public override void OnException(ExceptionContext context)
{
// send error as json
}
}
[ApiExceptionFilter]
public class ApiController : Controller{...}
// For mvc
public class MvcExceptionFilterAttribute : ExceptionFilterAttribute
{
public override void OnException(ExceptionContext context)
{
// send view result
}
}
[MvcExceptionFilter]
public class HomeController : Controller{...}
If you want to add filter globally, see Register filter for an area
2- Using UseWhen
and UseExceptionHandler
app.UseWhen(x => x.Request.Path.Value.StartsWith("/api"), builder =>
{
builder.UseExceptionHandler(new ExceptionHandlerOptions()
{
ExceptionHandler = async (ctx) =>
{
var feature = ctx.Features.Get<IExceptionHandlerFeature>();
var error = feature?.Error;
// send json
}
});
});
app.UseWhen(x => !x.Request.Path.Value.StartsWith("/api"), builder =>
{
builder.UseExceptionHandler("/Error");
});`
3- Using UseExceptionHandler
conditionally:
app.UseExceptionHandler(new ExceptionHandlerOptions()
{
ExceptionHandler = async (ctx) =>
{
if (ctx.Request.Path.Value.StartsWith("/api"))
{
var feature = ctx.Features.Get<IExceptionHandlerFeature>();
var error = feature?.Error;
// send json
}
else
{
// redirect error page
}
}
});
I would recommend to write your custom middleware to handle exceptions as want. An example here:
public class ErrorMiddleware
{
private readonly RequestDelegate next;
public ErrorHandlingMiddleware(RequestDelegate next)
{
this.next = next;
}
public async Task Invoke(HttpContext context)
{
try
{
await next(context);
}
catch (Exception ex)
{
await HandleExceptionAsync(context, ex);
}
}
private static async Task HandleExceptionAsync(HttpContext context, Exception exception)
{
if (exception == null) return;
var code = HttpStatusCode.InternalServerError;
if (exception is MyNotFoundException) code = HttpStatusCode.NotFound;
//here you can check what kind of exception it is
//wite is proper for Web API, but here you can do what you want
await WriteExceptionAsync(context, exception, code).ConfigureAwait(false);
}
private static async Task WriteExceptionAsync(HttpContext context, Exception exception, HttpStatusCode code)
{
var response = context.Response;
response.ContentType = "application/json";
response.StatusCode = (int)code;
await response.WriteAsync(JsonConvert.SerializeObject(new
{
error = new
{
message = exception.Message,
exception = exception.GetType().Name
}
})).ConfigureAwait(false);
}
}
And this is how you can register your middleware:
app.UseMiddleware(typeof(ErrorMiddleware));
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With