在使用常规ASP.NET Web API多年之后,我正在为我的新REST API项目使用ASP.NET核心。我在ASP.NET Core Web API中看不到任何处理异常的好方法。我尝试实现了一个异常处理过滤器/属性:
public class ErrorHandlingFilter : ExceptionFilterAttribute
{
public override void OnException(ExceptionContext context)
{
HandleExceptionAsync(context);
context.ExceptionHandled = true;
}
private static void HandleExceptionAsync(ExceptionContext context)
{
var exception = context.Exception;
if (exception is MyNotFoundException)
SetExceptionResult(context, exception, HttpStatusCode.NotFound);
else if (exception is MyUnauthorizedException)
SetExceptionResult(context, exception, HttpStatusCode.Unauthorized);
else if (exception is MyException)
SetExceptionResult(context, exception, HttpStatusCode.BadRequest);
else
SetExceptionResult(context, exception, HttpStatusCode.InternalServerError);
}
private static void SetExceptionResult(
ExceptionContext context,
Exception exception,
HttpStatusCode code)
{
context.Result = new JsonResult(new ApiResponse(exception))
{
StatusCode = (int)code
};
}
}
下面是我的启动过滤器注册:
services.AddMvc(options =>
{
options.Filters.Add(new AuthorizationFilter());
options.Filters.Add(new ErrorHandlingFilter());
});
我遇到的问题是,当我的AuthorizationFilter
中发生异常时,ErrorHandlingFilter
不会处理它。我希望它能像使用旧的ASP.NET Web API一样在那里被捕获。
那么,如何从Action Filters中捕获所有应用程序异常以及任何异常呢?
发布于 2016-08-12 20:53:51
您最好的选择是使用中间件来实现您想要的日志记录。您希望将异常日志记录放在一个中间件中,然后在另一个中间件中处理显示给用户的错误页面。这允许逻辑分离,并遵循微软在两个中间件组件中布局的设计。这里有一个很好的微软文档链接:Error Handling in ASP.Net Core
对于您的特定示例,您可能希望使用StatusCodePage middleware中的一个扩展,或者像this一样使用您自己的扩展。
您可以在这里找到一个记录异常的示例:ExceptionHandlerMiddleware.cs
public void Configure(IApplicationBuilder app)
{
// app.UseErrorPage(ErrorPageOptions.ShowAll);
// app.UseStatusCodePages();
// app.UseStatusCodePages(context => context.HttpContext.Response.SendAsync("Handler, status code: " + context.HttpContext.Response.StatusCode, "text/plain"));
// app.UseStatusCodePages("text/plain", "Response, status code: {0}");
// app.UseStatusCodePagesWithRedirects("~/errors/{0}");
// app.UseStatusCodePagesWithRedirects("/base/errors/{0}");
// app.UseStatusCodePages(builder => builder.UseWelcomePage());
app.UseStatusCodePagesWithReExecute("/Errors/{0}"); // I use this version
// Exception handling logging below
app.UseExceptionHandler();
}
如果您不喜欢这个特定实现,那么也可以使用ELM Middleware,下面是一些示例:Elm Exception Middleware
public void Configure(IApplicationBuilder app)
{
app.UseStatusCodePagesWithReExecute("/Errors/{0}");
// Exception handling logging below
app.UseElmCapture();
app.UseElmPage();
}
如果这不能满足您的需求,您可以通过查看中间件组件的ExceptionHandlerMiddleware和ElmMiddleware实现来掌握构建您自己的概念。
将异常处理中间件添加到StatusCodePages中间件之下是很重要的,但最重要的是添加其他中间件组件。这样,您的异常中间件将捕获异常,记录它,然后允许请求进入StatusCodePage中间件,该中间件将向用户显示友好的错误页面。
发布于 2018-08-15 02:30:20
首先,感谢Andrei,因为我的解决方案基于他的示例。
我包括我的,因为它是一个更完整的样本,可能会为读者节省一些时间。
Andrei方法的局限性是不能处理日志记录、捕获可能有用的请求变量和内容协商(无论客户端请求了什么- XML /纯文本等,它都将始终返回JSON )。
我的方法是使用ObjectResult,它允许我们使用MVC中内置的功能。
此代码还会阻止缓存响应。
错误响应的修饰方式使其可以由XML序列化程序序列化。
public class ExceptionHandlerMiddleware
{
private readonly RequestDelegate next;
private readonly IActionResultExecutor<ObjectResult> executor;
private readonly ILogger logger;
private static readonly ActionDescriptor EmptyActionDescriptor = new ActionDescriptor();
public ExceptionHandlerMiddleware(RequestDelegate next, IActionResultExecutor<ObjectResult> executor, ILoggerFactory loggerFactory)
{
this.next = next;
this.executor = executor;
logger = loggerFactory.CreateLogger<ExceptionHandlerMiddleware>();
}
public async Task Invoke(HttpContext context)
{
try
{
await next(context);
}
catch (Exception ex)
{
logger.LogError(ex, $"An unhandled exception has occurred while executing the request. Url: {context.Request.GetDisplayUrl()}. Request Data: " + GetRequestData(context));
if (context.Response.HasStarted)
{
throw;
}
var routeData = context.GetRouteData() ?? new RouteData();
ClearCacheHeaders(context.Response);
var actionContext = new ActionContext(context, routeData, EmptyActionDescriptor);
var result = new ObjectResult(new ErrorResponse("Error processing request. Server error."))
{
StatusCode = (int) HttpStatusCode.InternalServerError,
};
await executor.ExecuteAsync(actionContext, result);
}
}
private static string GetRequestData(HttpContext context)
{
var sb = new StringBuilder();
if (context.Request.HasFormContentType && context.Request.Form.Any())
{
sb.Append("Form variables:");
foreach (var x in context.Request.Form)
{
sb.AppendFormat("Key={0}, Value={1}<br/>", x.Key, x.Value);
}
}
sb.AppendLine("Method: " + context.Request.Method);
return sb.ToString();
}
private static void ClearCacheHeaders(HttpResponse response)
{
response.Headers[HeaderNames.CacheControl] = "no-cache";
response.Headers[HeaderNames.Pragma] = "no-cache";
response.Headers[HeaderNames.Expires] = "-1";
response.Headers.Remove(HeaderNames.ETag);
}
[DataContract(Name= "ErrorResponse")]
public class ErrorResponse
{
[DataMember(Name = "Message")]
public string Message { get; set; }
public ErrorResponse(string message)
{
Message = message;
}
}
}
发布于 2017-12-23 23:46:27
首先,将ASP.NET Core2 Startup
配置为重新执行到错误页面,以查找来自web服务器的任何错误和任何未处理的异常。
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment()) {
// Debug config here...
} else {
app.UseStatusCodePagesWithReExecute("/Error");
app.UseExceptionHandler("/Error");
}
// More config...
}
接下来,定义一个异常类型,它允许您抛出带有HTTP状态代码的错误。
public class HttpException : Exception
{
public HttpException(HttpStatusCode statusCode) { StatusCode = statusCode; }
public HttpStatusCode StatusCode { get; private set; }
}
最后,在错误页面的控制器中,根据错误原因以及最终用户是否会直接看到响应来自定义响应。此代码假定所有API都以/api/
开头。
[AllowAnonymous]
public IActionResult Error()
{
// Gets the status code from the exception or web server.
var statusCode = HttpContext.Features.Get<IExceptionHandlerFeature>()?.Error is HttpException httpEx ?
httpEx.StatusCode : (HttpStatusCode)Response.StatusCode;
// For API errors, responds with just the status code (no page).
if (HttpContext.Features.Get<IHttpRequestFeature>().RawTarget.StartsWith("/api/", StringComparison.Ordinal))
return StatusCode((int)statusCode);
// Creates a view model for a user-friendly error page.
string text = null;
switch (statusCode) {
case HttpStatusCode.NotFound: text = "Page not found."; break;
// Add more as desired.
}
return View("Error", new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier, ErrorText = text });
}
ASP.NET核心将记录错误详细信息,以便您进行调试,因此状态代码可能就是您想要提供给(可能不受信任的)请求者的全部。如果你想显示更多的信息,你可以增强HttpException
来提供它。对于接口错误,可以通过return Json...
替换return StatusCode...
,将JSON编码的错误信息放在消息体中。
https://stackoverflow.com/questions/38630076
复制相似问题