我有一个同时具有MVC和ASP.NET Core 2.2中“新” ApiController端点的应用程序。
在添加API端点之前,我一直在使用通过app.UseExceptionHandler((x) => { ... }
注册为中间件的全局异常处理程序,该处理程序将重定向到错误页面。
当然,这不适用于API响应,我想返回一个ObjectResult
(协商)500
结果和一个ProblemDetails
格式结果。
问题是,如果我正在处理MVC或API请求,我不确定如何在“ UseExceptionHandler” lambda中可靠地确定。我可以使用某种请求URL匹配(例如/api/...
前缀),但我希望有一个更强大的解决方案,以后再也不会咬我。
我要实现的伪伪代码版本是:
app.UseExceptionHandler(x =>
{
x.Run(async context =>
{
// extract the exception that was thrown
var ex = context.Features.Get<IExceptionHandlerFeature>()?.Error;
try
{
// generically handle the exception regardless of what our response needs to look like by logging it
// NOTE: ExceptionHandlerMiddleware itself will log the exception
// TODO: need to find a way to see if we have run with negotiation turned on (in which case we are API not MVC!! see below extensions for clues?)
// TODO: ... could just use "/api/" prefix but that seems rubbish
if (true)
{
// return a 500 with object (in RFC 7807 form) negotiated to the right content type (eg. json)
}
else
{
// otherwise, we handle the response as a 500 error page redirect
}
}
catch (Exception exofex)
{
// NOTE: absolutely terrible if we get into here
log.Fatal($"Unhandled exception in global error handler!", exofex);
log.Fatal($"Handling exception: ", ex);
}
});
});
}
有什么想法吗?
干杯!
答案 0 :(得分:0)
这可能与您期望的有所不同,但是您可以仅检查请求是否为AJAX请求。
您可以使用此扩展名:
public static class HttpRequestExtensions
{
public static bool IsAjaxRequest(this HttpRequest request)
{
if (request == null)
throw new ArgumentNullException(nameof(request));
if (request.Headers == null)
return false;
return request.Headers["X-Requested-With"] == "XMLHttpRequest";
}
}
然后是带有如下调用方法的中间件:
public async Task Invoke(HttpContext context)
{
if (context.Request.IsAjaxRequest())
{
try
{
await _next(context);
}
catch (Exception ex)
{
//Handle the exception
await HandleExceptionAsync(context, ex);
}
}
else
{
await _next(context);
}
}
private static Task HandleExceptionAsync(HttpContext context, Exception exception)
{
//you can do more complex logic here, but a basic example would be:
var result = JsonConvert.SerializeObject(new { error = "An unexpected error occurred." });
context.Response.ContentType = "application/json";
context.Response.StatusCode = 500;
return context.Response.WriteAsync(result);
}
有关更详细的版本,请参见this SO answer。
答案 1 :(得分:0)
如果要检查请求是否路由到ApiController
,可以尝试IExceptionFilter
处理异常。
public class CustomExceptionFilter : IExceptionFilter
{
public void OnException(ExceptionContext context)
{
if (IsApi(context))
{
HttpStatusCode status = HttpStatusCode.InternalServerError;
var message = context.Result;
//You can enable logging error
context.ExceptionHandled = true;
HttpResponse response = context.HttpContext.Response;
response.StatusCode = (int)status;
response.ContentType = "application/json";
context.Result = new ObjectResult(new { ErrorMsg = message });
}
else
{
}
}
private bool IsApi(ExceptionContext context)
{
var controllerActionDesc = context.ActionDescriptor as ControllerActionDescriptor;
var attribute = controllerActionDesc
.ControllerTypeInfo
.CustomAttributes
.FirstOrDefault(c => c.AttributeType == typeof(ApiControllerAttribute));
return attribute == null ? false : true;
}
}
答案 2 :(得分:0)
感谢其他人的所有建议,但是从这里进行了更多的思考和想法后,我才意识到我的方法一开始并不正确-我应该在控制器中本地处理大多数异常并做出响应从那里。
我基本上使我的错误处理中间件与处理MVC未处理的异常相同。客户端将获得带有HTML响应的500,但那时客户端无能为力,因此没有危害。
感谢您的帮助!