我想在中间件组件中连接异常处理,如下所示:
public override async Task Invoke(IOwinContext context)
{
try
{
await Next.Invoke(context);
}
catch (Exception ex)
{
// Log error and return 500 response
}
}
但是,在我可以访问之前,我想要捕获的一些异常被Web API管道捕获并转换为HttpErrorResponse
。在这个过程中,我丢失了很多关于错误的细节,所以在调试时我无法获得有用的堆栈跟踪(调试器在抛出异常时甚至不会停止 - 我必须手动单步执行代码,看看它失败的地方......)。
我尝试使用以下实现添加自定义异常处理程序:
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());
注册,但在执行Next.Invoke(context)
后通过
context.Get<Exception>(Constants.ContextKeys.Exception);
仍然没有给我我想要的所有细节,也没有在调试器的故障点停止。
有没有办法完全关闭所有内置错误处理,以便我自己的中间件可以处理它?</ p>
澄清,因为很多人似乎误解了我之后的事情:
答案 0 :(得分:18)
更新:I blogged about this。在研究博客文章时,我发现了一些改进的潜力;我已经更新了这个答案的相关部分。有关为什么我认为这比其他所有建议或默认行为更好的详细信息,请阅读整篇文章:)
我现在已经采用了以下方法,即使不是100%符合我的要求,它似乎也可以正常工作:
创建课程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;
}
}
让该类替换 Web API的IExceptionHandler
服务:
config.Services.Replace(typeof(IExceptionHandler), new PassthroughExceptionHandler());
创建一个实现我想要的中间件类:
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);
我仍然会给那些提出的人奖励(赏金过期......)我仍在寻找更好的解决方案,例如,当抛出未处理的异常。 (当我在处理程序中重新抛出异常时,这种方法会使VS中断,但原始调用堆栈丢失;我必须在错误行处设置断点并再次调试,以便在抛出异常时拦截状态。)
答案 1 :(得分:1)
不确定这是否适合您,但我有类似的要求,即使未找到错误,也要将所有错误发送回JSON。我创建了一个基本控制器并覆盖了ExecuteAsync,允许我创建自己的响应。
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; }
}
答案 2 :(得分:1)
您也可以尝试创建自己的控制器激活器,拥有自定义异常处理程序并尝试使用ExceptionFilterAttribute。
创建控制器激活器
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)));
}
}
}
创建ExceptionFilterAttribute
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是一个使用Formatters序列化为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);
答案 3 :(得分:0)
OWIN不应该处理这样的异常,因为web api内置了自己的错误处理.OWIN旨在与应用程序分离。如果在异常处理程序的HandleAsync方法上设置断点,则应该能够检查上下文变量并查看异常的详细信息。
如果您只是出于调试目的而尝试这样做,那么在那里设置断点应该允许您查看异常。如果你需要记录异常,在我看来,异常处理程序是最好的做法。
希望有所帮助。
答案 4 :(得分:0)
这可能会有所帮助:
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());
}
}