C# 如何从 WebApi 消息处理程序优雅地返回异常
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/11866114/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to gracefully return exception from a WebApi Message Handler
提问by Shrike
I have a global ExceptionFilter in my Mvc\WebApi application registered as:
我的 Mvc\WebApi 应用程序中有一个全局 ExceptionFilter 注册为:
public virtual void RegisterHttpFilters(HttpConfiguration config)
{
config.Filters.Add(new MyExceptionFilter(_exceptionHandler));
}
where MyExceptionFilter is:
其中 MyExceptionFilter 是:
public class MyExceptionFilter : ExceptionFilterAttribute
{
private readonly IMyExceptionHandler m_exceptionHandler;
public MyExceptionFilter(IMyExceptionHandler exceptionHandler)
{
m_exceptionHandler = exceptionHandler;
}
public override void OnException(HttpActionExecutedContext context)
{
Exception ex = context.Exception;
if (ex != null)
{
object response = null;
HttpStatusCode statusCode = m_exceptionHandler != null
? m_exceptionHandler.HandleException(ex, out response)
: HttpStatusCode.InternalServerError;
context.Response = context.Request.CreateResponse(statusCode, response ?? ex);
}
base.OnException(context);
}
}
This filter returns all exceptions as json-objects and allows some IMyExceptionHandler-implementation to customize the object being returned.
此过滤器将所有异常作为 json 对象返回,并允许某些 IMyExceptionHandler 实现自定义正在返回的对象。
All this works well. Till I have an exception in some of my message handlers:
所有这些都运作良好。直到我的一些消息处理程序出现异常:
public class FooMessageHandler : DelegatingHandler
{
private readonly Func<IBar> _barFactory;
public FooMessageHandler(Func<IBar> barFactory)
{
_barFactory = varFactory;
}
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
request.Properties["MY_BAR"] = _barFactory();
return base.SendAsync(request, cancellationToken);
}
}
As you can see this handler creates some component and put it into the current http request message. When an exception happens in FuncOfIBar then I get the Yellow Screen of Death. My ExceptionFilter isn't called.
如您所见,此处理程序创建了一些组件并将其放入当前的 http 请求消息中。当 FuncOfIBar 中发生异常时,我会收到黄屏死机。我的 ExceptionFilter 没有被调用。
I tried to specifically catch the exception in the message handler and return HttpResponseException but it doesn't change anything - still getting YSOD:
我试图在消息处理程序中专门捕获异常并返回 HttpResponseException 但它没有改变任何东西 - 仍然得到 YSOD:
public class XApplicationInitializerMessageHandler : DelegatingHandler
{
private readonly Func<IXCoreApplication> _appFactory;
private readonly IXExceptionHandler m_exceptionHandler;
public XApplicationInitializerMessageHandler(Func<IXCoreApplication> appFactory, IXExceptionHandler exceptionHandler)
{
ArgumentValidator.EnsureArgumentNotNull(appFactory, "appFactory");
_appFactory = appFactory;
m_exceptionHandler = exceptionHandler;
}
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
try
{
request.SetApplication(_appFactory());
}
catch (Exception ex)
{
object resultObject = null;
HttpStatusCode statusCode = m_exceptionHandler != null
? m_exceptionHandler.HandleException(ex, out resultObject)
: HttpStatusCode.InternalServerError;
HttpResponseMessage responseMessage = request.CreateResponse(statusCode, resultObject ?? ex);
throw new HttpResponseException(responseMessage);
}
return base.SendAsync(request, cancellationToken);
}
}
}
}
I want behavior of my app to be same regardless where an exception happens: in a ApiController or a message handler.
无论异常发生在哪里,我都希望我的应用程序的行为是相同的:在 ApiController 或消息处理程序中。
How to do this?
这该怎么做?
I know about Application_Error but I'd like to keep HttpApplication customization untouchable.
我知道 Application_Error 但我想保持 HttpApplication 自定义不可触碰。
回答by Shrike
Instead of throwing HttpResponseExceptionI should just return an HttpResponseMessage:
而不是抛出HttpResponseException我应该只返回一个HttpResponseMessage:
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
try
{
request.SetApplication(_appFactory());
}
catch (Exception ex)
{
object resultObject = null;
HttpStatusCode statusCode = m_exceptionHandler != null
? m_exceptionHandler.HandleException(ex, out resultObject)
: HttpStatusCode.InternalServerError;
HttpResponseMessage responseMessage = request.CreateResponse(statusCode, resultObject ?? ex);
return Task<HttpResponseMessage>.Factory.StartNew(() => responseMessage);
}
return base.SendAsync(request, cancellationToken);
}
回答by Mads Tj?rnelund Toustrup
What I have done with succes is using an attribute-based approach. You place an custom attribute on each of the actions that should used exception handling:
我对成功所做的是使用基于属性的方法。您在应该使用异常处理的每个操作上放置一个自定义属性:
[ExceptionHandling]
public IEnumerable<string> Get()
Your custom exception handling attribute is implemented like this:
您的自定义异常处理属性是这样实现的:
public class ExceptionHandlingAttribute : ExceptionFilterAttribute
{
public override void OnException(HttpActionExecutedContext context)
{}
As you can see you, the custom attribute is inherited from ExceptionFilterAttribute. You then just override the OnException method. What you then can do is look at the type of the exception and handle it according to business rules or what ever fits your needs. In some cases you might just swallow the exception on the server. If you want to notify the client, you can throw an HttpResponseException, which can hold all relevant information, such as message, call stack, inner exception, etc.
如您所见,自定义属性继承自 ExceptionFilterAttribute。然后您只需覆盖 OnException 方法。然后您可以做的是查看异常的类型并根据业务规则或适合您需要的方式处理它。在某些情况下,您可能只是在服务器上吞下异常。如果要通知客户端,可以抛出一个HttpResponseException,里面可以保存所有相关信息,比如消息、调用栈、内部异常等。
For more inspiration take a look at this great blog post: ASP.NET Web API Exception Handling
如需更多灵感,请查看这篇很棒的博客文章:ASP.NET Web API 异常处理
回答by Alex
I had the same issue with global exception filters not running. The solution was that Web API exception filters must be configured in a different global collection than ASP.NET MVC exception filters.
我在全局异常过滤器未运行时遇到了同样的问题。解决方案是必须在与 ASP.NET MVC 异常过滤器不同的全局集合中配置 Web API 异常过滤器。
So to configure ASP.NET MVC error handling you would run:
因此,要配置 ASP.NET MVC 错误处理,您将运行:
System.Web.Mvc.GlobalFilters.Filters.Add(new MyMvcExceptionFilter());
But for Web API error handling you must run:
但是对于 Web API 错误处理,您必须运行:
System.Web.Http.GlobalConfiguration.Configuration.Filters.Add(new MyWebApiExceptionFilter());
From my limited experience with Web API this type of situation is typical: superficially the patterns used will be very similar to ASP.NET MVC and so instantly familiar. This gives the illusion that writing one piece of code will have an effect on both frameworks, but actually their implementations underneath it all are largely or completely separate and you'll need to write two versions of the code that are very similar to make it all work.
根据我对 Web API 的有限经验,这种情况是典型的:从表面上看,所使用的模式与 ASP.NET MVC 非常相似,因此非常熟悉。这给人的错觉是,编写一段代码会对两个框架都有影响,但实际上它们在其下的实现在很大程度上或完全是分开的,您需要编写两个非常相似的代码版本才能完成所有工作工作。
Excellent reference on Web API exception handling: http://www.asp.net/web-api/overview/web-api-routing-and-actions/exception-handling
关于 Web API 异常处理的优秀参考:http: //www.asp.net/web-api/overview/web-api-routing-and-actions/exception-handling
回答by LMF
In startup.cs:
在startup.cs中:
config.Services.Replace(typeof(IExceptionHandler), new GlobalExceptionHandler());
I found that using GlobalExceptionHandler can catch the exception in delegating handler and return custom JSON objects.
我发现使用 GlobalExceptionHandler 可以捕获委托处理程序中的异常并返回自定义 JSON 对象。
回答by Vittória Zago
I found this blog with very good example: http://nodogmablog.bryanhogan.net/2016/07/getting-web-api-exception-details-from-a-httpresponsemessage/
我发现这个博客有很好的例子:http: //nodogmablog.bryanhogan.net/2016/07/getting-web-api-exception-details-from-a-httpresponsemessage/
I adopted the code with some updates:
我采用了一些更新的代码:
if ((int)response.StatusCode >= 400)
{
exceptionResponse = JsonConvert.DeserializeObject<ExceptionResponse>(LogRequisicao.CorpoResposta);
LogRequisicao.CorpoResposta = exceptionResponse.ToString() ;
if (exceptionResponse.InnerException != null)
LogRequisicao.CorpoResposta += "\r\n InnerException: " + exceptionResponse.ToString();
}
using object:
使用对象:
public class ExceptionResponse
{
public string Message { get; set; }
public string ExceptionMessage { get; set; }
public string ExceptionType { get; set; }
public string StackTrace { get; set; }
public ExceptionResponse InnerException { get; set; }
public override String ToString()
{
return "Message: " + Message + "\r\n "
+ "ExceptionMessage: " + ExceptionMessage + "\r\n "
+ "ExceptionType: " + ExceptionType + " \r\n "
+ "StackTrace: " + StackTrace + " \r\n ";
}
}

