处理绑定操作参数的错误以路由错误类型的值

时间:2014-01-20 17:02:49

标签: c# asp.net-mvc-4 asp.net-web-api

我在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}}事件,但在这种情况下似乎没有调用。

2 个答案:

答案 0 :(得分:1)

似乎这些错误作为模型绑定错误添加到ModelState中。动作选择器选择正确的动作,动作调用者调用它而不会抛出任何错误。

我想出的解决方法是创建一个动作调用程序,检查ModelState是否有错误。如果找到,则将第一个传递给我的ExceptionFilterErrorController使用的异常处理方法。

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);
    }
}

请注意,您需要覆盖所有虚拟方法。