2013-10-15 41 views
2

我有一个使用MVC4构建的面向互联网的网站,我偶尔会从漫游器或好奇的用户那里获取错误报告,这些用户发送不完整的URL请求。忽略由于缺少控制器参数导致的异常

例如:

public class ProductController : Controller 
{ 
    [HttpGet] 
    public void View(int id) 
    { 
     // ... 
  • GET请求到/product/view/1是有效的。
  • 由于未指定参数,所以对/product/view的GET请求无效。

这种无效的请求,引发异常相似:

System.ArgumentException: The parameters dictionary contains a null entry 
for parameter 'id' of non-nullable type 'System.Int32' for method 
'System.Web.Mvc.ActionResult View(Int32)' in 'Foo.ProductController'. An 
optional parameter must be a reference type, a nullable type, or be declared 
as an optional parameter. 

Parameter name: parameters 
    at System.Web.Mvc.ActionDescriptor.ExtractParameterFromDictionary(ParameterInfo parameterInfo, IDictionary`2 parameters, MethodInfo methodInfo) 
    at System.Web.Mvc.ReflectedActionDescriptor.<>c__DisplayClass1.<Execute>b__0(ParameterInfo parameterInfo) 
    ... 

由于异常消息的状态,我可以使id参数空,但操作方法内检查,但我有许多控制器与许多行动。

我想返回一个BadRequest/NotFound对任何无法将参数绑定到操作参数的请求的响应,并在代码中的一个位置指定此值以跨所有控制器应用。

这怎么办?

+0

在你定义控制器和动作和参数定义的路由配置中,你可以指定参数为可空。 –

+0

@AnirudhAgarwal当尝试将null转换为int时,可能会抛出int –

+0

@AirirhAgarwal,如果我为'id'包含一个我不想做的默认值,那么工作的唯一方法就是。如果在操作中定义了“id”并且它没有提供,那么客户端犯了一个错误,我想给他们发送404。另外,我有各种不同的控制器,每个控制器都需要在我的路由配置中输入。这里的要点是,我不希望在我的操作和配置中进行自定义更改,以防万一某个用户/机器人发送无效请求 - 它们应该集中处理,并且不会引发导致我收到错误电子邮件的异常只需删除。 –

回答

1

一种方法,所以才会把它放在那里。)

protected override void OnActionExecuted(ActionExecutedContext filterContext) 
{ 
    if (filterContext.Exception == null) 
     return; 

    // Avoid 'action parameter missing' exceptions by simply returning an error response 
    if (filterContext.Exception.TargetSite.DeclaringType == typeof(ActionDescriptor) && 
     filterContext.Exception.TargetSite.Name == "ExtractParameterFromDictionary") 
    { 
     filterContext.ExceptionHandled = true; 
     filterContext.Result = new HttpStatusCodeResult((int)HttpStatusCode.BadRequest); 
    } 
} 

感觉有点不舒服要做到这一点,因为它可以在框架的未来版本打破。但是,如果它确实发生中断,那么该网站将恢复为500而不是400。

+0

您也可以重写OnException方法以获得相同的效果。不知道它是否重要,但我猜OnActionExecuted是为每个调用的操作方法调用的,而OnException仅在发生异常时调用。只是一个想法。 – Doktorn

0

您可以使用HandleError属性来处理应用程序中的错误。 HandleError属性可以在控制器级别和方法级别指定。我已经使用之前是这样的:

[HandleError(ExceptionType = typeof(ArgumentException), View = "MissingArgument")] 
public ActionResult Test(int id) 
{ 
    return View(); 
} 

如果你不想赶在每个方法的基础上,你可以把属性上的一流水平,而不是例外:

[HandleError(ExceptionType = typeof(ArgumentException), View = "MissingArgument")] 
public class HomeController : Controller 
{ 
} 

如果你想你可以在应用程序启动文件夹,将其添加到一个FilterConfig类的中央位置处理这个问题:

public static void RegisterGlobalFilters(GlobalFilterCollection filters) 
{ 
    var error = new HandleErrorAttribute(); 
    error.ExceptionType = typeof (ArgumentException); 
    error.View = "MissingArgument"; 
    filters.Add(error); 
} 

的MissingArgument视图应位于共享视图文件夹。 如果要发送一个特定的HTTP错误代码返回给客户端,你可以把在视图中:

,似乎工作是在控制器(我用的基本控制器覆盖 OnActionExecuted
@{ 
    ViewBag.Title = "Error"; 
    Context.Response.StatusCode = (int) HttpStatusCode.BadRequest; 
} 

<h2>Not found</h2> 
+0

有趣的是,虽然这将处理所有'ArgumentException's,而我只希望那些与无法绑定操作参数相关的。另外,我想把这个过滤器放在一个中央位置,而不是每个控制器上(我认为这可以通过一些小的代码修改来实现。) –

+0

如果你希望这个过滤器集中在FilterConfig中,我已经更新了我的回答来证明这一点。 – Doktorn

+0

感谢您的更新。最后,我在基础控制器中重写了'OnActionExecuted',并针对确切的错误,而不是所有的'ArgumentException'实例。我担心的是,我会禁止在不同情况下抛出的合法参数异常的错误日志记录/电子邮件。 –

相关问题