问题
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.
回答1:
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
}
}
});
回答2:
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));
来源:https://stackoverflow.com/questions/40149061/how-to-handle-errors-differently-for-or-distinguish-between-api-calls-and-mvc