How to handle errors differently for (or distinguish between) API calls and MVC (views) calls in ASP.NET Core
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));