我在ASP.NET WebAPI中处理所有类型的错误时遇到了麻烦。
我使用ExceptionFilter成功处理了我的操作方法中引发的异常,invalid routes, invalid controller or action name发生了404错误。但是,我正在努力处理找到控制器和操作的错误,但模型绑定的参数是不正确的类型。
这是我的行动,路由到/api/users/{id}
。
[HttpGet]
public virtual TPoco Get(long id)
{
...
}
如果我请求网址/api/users/notinteger
,我会收到在我的代码之外处理的400 Bad Request
错误:
{
Message: "The request is invalid.",
MessageDetail: "The parameters dictionary contains a null entry for parameter 'id' of non-nullable type 'System.Int64' for method '___ Get(Int64)' in '___'. An optional parameter must be a reference type, a nullable type, or be declared as an optional parameter."
}
如何拦截此错误并回复我自己的错误消息?最好不要在控制器本身,因为我想以相同的方式处理几个控制器。
我已尝试按this question使用global.asax.cs
的{{1}}事件,但在这种情况下似乎没有调用。
答案 0 :(得分:1)
似乎这些错误作为模型绑定错误添加到ModelState中。动作选择器选择正确的动作,动作调用者调用它而不会抛出任何错误。
我想出的解决方法是创建一个动作调用程序,检查ModelState
是否有错误。如果找到,则将第一个传递给我的ExceptionFilter
和ErrorController
使用的异常处理方法。
internal class ThrowModelStateErrorsActionInvoker : ApiControllerActionInvoker
{
public override Task<HttpResponseMessage> InvokeActionAsync(HttpActionContext actionContext, CancellationToken cancellationToken)
{
foreach (var error in actionContext.ModelState.SelectMany(kvp => kvp.Value.Errors))
{
var exception = error.Exception ?? new ArgumentException(error.ErrorMessage);
//invoke global exception handling
}
return base.InvokeActionAsync(actionContext, cancellationToken);
}
}
这很讨厌,但它确实有效。这占用了我的大部分时间,我很高兴终于到了某个地方。
我很想知道这对后果有何影响。还有什么在Web API中使用ModelState错误?任何人都可以在这个解决方案中添加一些可能的缺陷吗?
答案 1 :(得分:0)
如果您使用此处讨论的新WebApi 2.1 Global错误处理,会更好一些, http://aspnetwebstack.codeplex.com/wikipage?title=Global%20Error%20Handling&referringTitle=Specs http://www.asp.net/web-api/overview/releases/whats-new-in-aspnet-web-api-21#global-error
如果您不想使用WebApi 2.1是有正当理由的,那么您可以试试。 (注意我没有测试,但你可以尝试)。通过继承ReflectedHttpActionDescriptor并处理ExecuteAsync来创建自定义操作描述符。这就是我的意思,
public class HttpNotFoundActionDescriptor : ReflectedHttpActionDescriptor
{
ReflectedHttpActionDescriptor _descriptor;
public HttpNotFoundActionDescriptor(ReflectedHttpActionDescriptor descriptor)
{
_descriptor = descriptor;
}
public override Task<object> ExecuteAsync(HttpControllerContext controllerContext, IDictionary<string, object> arguments, CancellationToken cancellationToken)
{
try
{
return descriptor.ExecuteAsync(controllerContext, arguments, cancellationToken);
}
catch (HttpResponseException ex)
{
//..........................
}
}
}
public class HttpNotFoundAwareControllerActionSelector : ApiControllerActionSelector
{
public HttpNotFoundAwareControllerActionSelector()
{
}
public override HttpActionDescriptor SelectAction(HttpControllerContext controllerContext)
{
HttpActionDescriptor decriptor = null;
try
{
decriptor = base.SelectAction(controllerContext);
}
catch (HttpResponseException ex)
{
var code = ex.Response.StatusCode;
if (code != HttpStatusCode.NotFound && code != HttpStatusCode.MethodNotAllowed)
throw;
var routeData = controllerContext.RouteData;
routeData.Values["action"] = "Handle404";
IHttpController httpController = new ErrorController();
controllerContext.Controller = httpController;
controllerContext.ControllerDescriptor = new HttpControllerDescriptor(controllerContext.Configuration, "Error", httpController.GetType());
decriptor = base.SelectAction(controllerContext);
}
return new HttpNotFoundActionDescriptor(decriptor);
}
}
请注意,您需要覆盖所有虚拟方法。