在ASP.NET Web API 2中禁用* all *exception处理(为我自己腾出空间)?
我想在中间件组件中连接exception处理,如下所示:
public override async Task Invoke(IOwinContext context) { try { await Next.Invoke(context); } catch (Exception ex) { // Log error and return 500 response } }
然而,我想捕捉到的一些exception是在Web APIpipe道被捕获并转换为HttpErrorResponse
之前,我可以find它们。 在这个过程中,我失去了很多关于错误的细节,所以在debugging时我不能获得有用的堆栈跟踪信息(debugging程序甚至在引发exception时都不会停止 – 我必须手动执行代码并查看那里失败…)。
我尝试添加一个自定义exception处理程序与以下实现:
public Task HandleAsync(ExceptionHandlerContext context, CancellationToken cancellationToken) { var owinContext = context.Request.GetOwinContext(); owinContext.Set(Constants.ContextKeys.Exception, context.Exception); return Task.FromResult(0); }
通过config.Services.Replace(typeof(IExceptionHandler), new MyExceptionHandler());
注册config.Services.Replace(typeof(IExceptionHandler), new MyExceptionHandler());
在我的启动configuration,但通过执行Next.Invoke(context)
后,看着它
context.Get<Exception>(Constants.ContextKeys.Exception);
仍然没有给我所有我想要的细节,也没有停止在debugging器的故障点。
有没有办法可以完全closures所有内置的error handling,以便我自己的中间件可以照顾它?
澄清 ,因为很多人似乎误解了我之后:
- Web API中的内置error handling捕获了一些 (但不是全部)exception,并将它们重写为500个响应。
- 我想捕捉所有exception,做一些日志logging, 然后 用我select的信息发出500个响应(对于大多数情况,请参阅下一个项目符号)。
- 还有一些例外信号表示业务逻辑错误,为此我想返回40x错误。
- 我希望这是在(应用程序)pipe道的顶部,即包装在请求生命周期中的一切
- 我想要使用OWIN来处理这个问题,以使它可以移植到未来可能的自我托pipe的场景中(也就是说,这个应用程序总是托pipe在IIS-HTTP模块上,Global.asax.cs等是不相关的这里)。
更新 : 我博客这个 。 在研究博客post时,我发现了一些改进的潜力; 我已经更新了这个答案的相关部分。 有关为什么我认为这比所有其他build议,或者默认行为更好的细节,请阅读整篇文章:)
我现在已经采取了以下方法,即使不是100%符合我所寻找的,也可以正常工作:
-
创build一个类
PassthroughExceptionHandler
:public class PassthroughExceptionHandler : IExceptionHandler { public Task HandleAsync(ExceptionHandlerContext context, CancellationToken cancellationToken) { // don't just throw the exception; that will ruin the stack trace var info = ExceptionDispatchInfo.Capture(context.Exception); info.Throw(); return Task.CompletedTask; } }
-
让该类replace Web API的
IExceptionHandler
服务:config.Services.Replace(typeof(IExceptionHandler), new PassthroughExceptionHandler());
-
创build一个我想要的中间件类:
public class ExceptionHandlerMiddleware { public override async Task Invoke(IOwinContext context) { try { await Next?.Invoke(context); } catch (Exception ex) { // handle and/or log } } }
-
首先在堆栈中注册该中间件:
app.Use<ExceptionHandlerMiddleware>() .UseStageMarker(PipelineStage.Authenticate) // other middlewares omitted for brevity .UseStageMarker(PipelineStage.PreHandlerExecute) .UseWebApi(config);
我仍然会奖励给任何人 (赏金过期…)我仍然在寻找一个更好的解决scheme,例如,当一个未处理的exception抛出时,打破。 (当我在处理程序中重新抛出exception时,这种方法会导致VS中断,但是原来的调用堆栈已经丢失;我必须在故障线路上设置一个断点并再次debugging,以便能够拦截抛出exception时的状态。
不知道这是否适用于您,但我有一个类似的要求,即使没有发现错误发回所有的错误作为JSON。 我创build了一个基础控制器,并覆盖了ExecuteAsync,允许我创build自己的响应。
public class ControllerBase : ApiController { protected string ClassName = "ControllerBase::"; public override System.Threading.Tasks.Task<HttpResponseMessage> ExecuteAsync(System.Web.Http.Controllers.HttpControllerContext controllerContext, System.Threading.CancellationToken cancellationToken) { try { System.Threading.Tasks.Task<HttpResponseMessage> TaskList = base.ExecuteAsync(controllerContext, cancellationToken); if (TaskList.Exception != null && TaskList.Exception.GetBaseException() != null) { JSONErrorResponse AsyncError = new JSONErrorResponse(); AsyncError.ExceptionMessage = TaskList.Exception.GetBaseException().Message; AsyncError.ErrorMessage = string.Format("Unknown error {0} ExecuteAsync {1}", ClassName ,controllerContext.Request.RequestUri.AbsolutePath); AsyncError.HttpErrorCode = HttpStatusCode.BadRequest; HttpResponseMessage ErrorResponse = controllerContext.Request.CreateResponse(AsyncError.HttpErrorCode, AsyncError); return System.Threading.Tasks.Task.Run<HttpResponseMessage>(() => ErrorResponse); } return TaskList; } catch (Exception Error) { JSONErrorResponse BadParameters = new JSONErrorResponse(); BadParameters.ExceptionMessage = Error.Message; BadParameters.ErrorMessage = string.Format("Method [{0}], or URL [{1}] not found, verify your request", controllerContext.Request.Method.Method, controllerContext.Request.RequestUri.AbsolutePath); BadParameters.HttpErrorCode = HttpStatusCode.NotFound; HttpResponseMessage ErrorResponse = controllerContext.Request.CreateResponse(BadParameters.HttpErrorCode, BadParameters); return System.Threading.Tasks.Task.Run<HttpResponseMessage>(() => ErrorResponse); } } } public class JSONErrorResponse { //Possible message from exception public string ExceptionMessage { get; set; } //Possible custom error message public string ErrorMessage { get; set; } //Http error code public HttpStatusCode HttpErrorCode { get; set; } }
您也可以尝试创build自己的控制器激活器,拥有自定义exception处理程序并尝试使用ExceptionFilterAttribute。
-
创build你的控制器激活器
public class ExceptionHandlingControllerActivator : IHttpControllerActivator { private readonly IHttpControllerActivator _concreteActivator; public ExceptionHandlingControllerActivator(IHttpControllerActivator concreteActivator) { _concreteActivator = concreteActivator; } public IHttpController Create(HttpRequestMessage request, HttpControllerDescriptor controllerDescriptor, Type controllerType) { try { return _concreteActivator.Create(request, controllerDescriptor, controllerType); } catch (Exception ex) { // do stuff with the exception throw new HttpResponseException(request.CreateResponse(HttpStatusCode.InternalServerError, new ResponseModel(ex))); } } }
-
创buildExceptionFilterAttribute
public class ExceptionHandlingFilter : ExceptionFilterAttribute { public override void OnException(HttpActionExecutedContext context) { // do stuff with the exception var request = context.Request; ResponseModel respMod = null; // Example: if debug constant is not defined, mask exception, otherwise create normal object with message, inner exception and stacktrace #if !DEBUG respMod = new ResponseModel(context.Exception, context.Exception.Message, true); #else respMod = new ResponseModel(context.Exception); #endif context.Response = request.CreateResponse(HttpStatusCode.InternalServerError, respMod); } }
-
ResponseModel是一个我使用格式化程序序列化为JSON的类,并由所有控制器响应返回,因此除了HTTP状态代码之外,客户机还能够识别错误数据以及成功的响应。
config.Formatters.Clear(); // do not need any other config.Formatters.Add(new JsonMediaTypeFormatter());
-
接线
// ... [cut] ... config.Filters.Add(new ExceptionHandlingFilter()); // ... [cut] ... config.Services.Replace(typeof(IHttpControllerActivator), new ExceptionHandlingControllerActivator(config.Services.GetHttpControllerActivator()) ); // ... [cut] ... app.UseWebApi(config);
OWIN不应该处理这样的exception,因为web api有自己的内置error handling。OWIN被devise为与应用程序分离。 如果您在exception处理程序的HandleAsync方法上设置了一个断点,则应该能够检查上下文variables并查看exception的详细信息。
如果您只是为了debugging目的而尝试这样做,那么在那里设置断点应该允许您查看exception。 如果你需要loggingexception,在我看来,exception处理程序是最好的地方。
希望有所帮助。
这可能有所帮助:
https://stackoverflow.com/a/21382651/1419853
http://www.asp.net/web-api/overview/releases/whats-new-in-aspnet-web-api-21#global-error
本质上,有一些内置的支持来捕捉,处理和更改错误。
它看起来像这样:
public class ExceptionLogger : System.Web.Http.ExceptionHandling.ExceptionLogger { Logger _logger; public ExceptionLogger(Logger logger) { _logger = logger; } public override void Log(ExceptionLoggerContext context) { _logger.Error(context.ExceptionContext.Exception.ToString()); } }