如何返回 404 状态,其中无效参数被传递到我的 ASP.NET MVC 控制器?

Posted

技术标签:

【中文标题】如何返回 404 状态,其中无效参数被传递到我的 ASP.NET MVC 控制器?【英文标题】:How do I return a 404 status where invalid parameters are passed to my ASP.NET MVC controller? 【发布时间】:2012-02-17 09:32:04 【问题描述】:

如果将无效参数传递给我的控制器,我想返回 HTTP 状态 404。例如,如果我有一个看起来像这样的控制器:

public ActionResult GetAccount(int id)

   ...

如果遇到诸如此类的网址,我想返回404

/GetAccount /GetAccount/notanumber

即我想捕获被抛出的ArgumentException

我知道我可以使用可为空的类型:

public ActionResult GetAccount(int? id)

  if(id == null) throw new HttpException(404, "Not found");

但这很讨厌而且重复。

我希望可以在必要时将其添加到我的控制器中:

[HandleError(View="Error404", ExceptionType = typeof(ArgumentException))]
public class AccountsController : Controller

  public ActionResult GetAccount(int id)
  
    ...
  

但这似乎效果不佳。

我看到this post 和this answer 几乎解决了我的问题:

在该答案中,创建了一个抽象的 BaseController,您可以从中派生所有其他控制器:

public abstract class MyController : Controller

    #region Http404 handling

    protected override void HandleUnknownAction(string actionName)
    
        // If controller is ErrorController dont 'nest' exceptions
        if (this.GetType() != typeof(ErrorController))
            this.InvokeHttp404(HttpContext);
    

    public ActionResult InvokeHttp404(HttpContextBase httpContext)
    
        IController errorController = ObjectFactory.GetInstance<ErrorController>();
        var errorRoute = new RouteData();
        errorRoute.Values.Add("controller", "Error");
        errorRoute.Values.Add("action", "Http404");
        errorRoute.Values.Add("url", httpContext.Request.Url.OriginalString);
        errorController.Execute(new RequestContext(
             httpContext, errorRoute));

        return new EmptyResult();
    

    #endregion

这非常适合使用404 处理未知操作,但不允许我以404 处理无效数据。

我可以像这样安全地覆盖Controller.OnException(ExceptionContext filterContext)

protected override void OnException(ExceptionContext filterContext)

  if(filterContext.Exception.GetType() == typeof(ArgumentException))
  
    filterContext.ExceptionHandled = true;
    this.InvokeHttp404(filterContext.HttpContext);
  
  else
  
    base.OnException(filterContext);
  

从表面上看,它似乎可以工作,但我这样做是否会存储任何问题?

这样做在语义上正确吗?

【问题讨论】:

【参考方案1】:

最好的方法?动作方法选择器属性!

为了真正避免可以为空的方法参数,我建议您编写一个Action Method Selector 属性,该属性实际上仅在提供id 时才匹配您的操作方法。它不会说没有提供该参数,而是说它无法匹配给定请求的任何操作方法。

我会将此操作选择器称为RequireRouteValuesAttribute 并以这种方式工作:

[RequireRouteValues("id")]
public ActionResult GetAccount(int id)

    ...

为什么这是解决您问题的最佳解决方案?

如果您查看您的代码,您希望在与名称匹配但参数绑定失败的操作上返回 404(因为未提供或任何其他原因)。可以这么说,您的操作需要特定的操作参数,否则会返回 404。

因此,当添加动作选择器属性时,会添加对动作的要求,因此它必须匹配名称(由 MVC 给出)并且还需要特定的动作参数。每当未提供 id 时,此操作不匹配。如果有另一个匹配的操作不是这里的问题,因为该特定操作将被执行。主要的事情已经完成。操作与无效路由请求不匹配,而是返回 404。

有一个应用代码!

检查my blog post,它实现了您可以开箱即用的这种属性。它完全符合您的要求:如果提供的路由数据没有所有必需的值,它将与您的操作方法不匹配。

【讨论】:

谢谢@jgauffin...MVC 实际上有很多我们甚至不知道的自定义钩子...我知道我不知道很多...;)【参考方案2】:

我设法通过在所有路线的末尾添加这条路线来完成这项工作:

routes.MapRoute("CatchAllErrors", "*url",
    new  controller = "Error", action = "NotFound" 
);

注意:首先我关注的是:How can I properly handle 404 in ASP.NET MVC?

【讨论】:

【参考方案3】:

免责声明:这并不涵盖所有情况

对于您示例中的 url,返回 404 可以在单行中完成。只需为id 参数添加路由约束即可。

routes.MapRoute(
    "Default", // Route name
    "controller/action/id", // URL with parameters
    new  controller = "Home", action = "Index" , // Parameter defaults
    new  id = @"\d+"  // restrict id to be required and numeric
);

仅此而已。现在,任何没有 idid 的匹配 url 都不是数字,自动触发未找到错误(有很多方法可以处理,一种在您的示例中,另一种通过使用自定义 HandleErrorAttribute 等)。您可以在您的操作中使用不可为空的int 参数。

【讨论】:

但是...他可能不需要所有控制器和操作来提供 ID...这只是某些边缘情况的部分解决方案。

以上是关于如何返回 404 状态,其中无效参数被传递到我的 ASP.NET MVC 控制器?的主要内容,如果未能解决你的问题,请参考以下文章

为啥没有查询参数被传递到我的 NancyFX 模块?

预检响应具有无效的 HTTP 状态代码 404”

用户授权后如何在所有子组件之间传递状态?

预检具有无效的 HTTP 状态代码 404 Jquery AJAX POST

通过 Angular JS $http.post 使用 slim 框架时出现 404 无效的 http 状态代码

如何将 Parse Cloud Code 中的值返回到我的 iOS 应用程序?