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

时间:2021-07-11 04:13:39

I want to return a HTTP status 404 if invalid arguments are passed to my controller. For example if I have a controller that looks like:

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

public ActionResult GetAccount(int id)
{
   ...
}

Then I want to return a 404 if say urls such as these are encountered:

然后我想返回404,如果遇到这样的网址:

/GetAccount
/GetAccount/notanumber

i.e. I want to trap the ArgumentException that is thrown.

即我想捕获抛出的ArgumentException。

I know I could use a nullable type:

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

public ActionResult GetAccount(int? id)
{
  if(id == null) throw new HttpException(404, "Not found");
}

But that's pretty icky and repetitious.

但那非常狡猾和重复。

I was hoping I could add this to my controllers where necessary:

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

[HandleError(View="Error404", ExceptionType = typeof(ArgumentException))]
public class AccountsController : Controller
{
  public ActionResult GetAccount(int id)
  {
    ...
  }
}

But that doesn't appear to work well.

但这似乎并不奏效。

I saw this post and this answer which nearly solves my problem:

我看到这篇文章和这个答案几乎解决了我的问题:

In that answer an abstract BaseController is created from which you derive all your other controllers from:

在该答案中,创建了一个抽象的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
}

This works great at handling unknown actions with a 404 but doesn't allow me to handle invalid data as a 404.

这非常适合处理404的未知操作,但不允许我将无效数据作为404处理。

Can I safely override Controller.OnException(ExceptionContext filterContext) like this:

我可以安全地覆盖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);
  }
}

On the surface it seems to work, but am I storing up any problems by doing this?

从表面上看它似乎有效,但我是否通过这样做存储任何问题?

Is this semantically correct thing to do?

这个语义是否正确?

3 个解决方案

#1


6  

Best way? Action method selector attribute!

To actually avoid nullable method arguments I suggest that you write an Action Method Selector attribute that will actually only match your action method when id is supplied. It won't say that argument wasn't supplied but that it couldn't match any action methods for the given request.

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

I would call this action selector RequireRouteValuesAttribute and would work this way:

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

[RequireRouteValues("id")]
public ActionResult GetAccount(int id)
{
    ...
}

Why is this the best solution for your problem?

If you look at your code you'd like to return a 404 on actions that match name but parameter binding failed (either because it wasn't supplied or any other reason). Your action so to speak requires particular action parameter otherwise a 404 is returned.

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

So when adding action selector attribute adds the requirement on the action so it has to match name (this is given by MVC) and also require particular action parameters. Whenever id is not supplied this action is not matched. If there's another action that does match is not the issue here because that particular action will get executed. The main thing is accomplished. Action doesn't match for invalid route request and a 404 is returned instead.

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

There's an app code for that!

Check my blog post that implements this kind of attribute that you can use out of the box. It does exactly what you're after: it won't match your action method if route data provided doesn't have all required values.

查看我的博客帖子,它实现了您可以立即使用的这种属性。它完全符合您的要求:如果提供的路径数据没有所有必需值,它将与您的操作方法不匹配。

#2


1  

Disclaimer: this does not cover all the cases

免责声明:这并未涵盖所有案例

For urls in your examples, returning 404 can be done in single line. Just add route constraint for id parameter.

对于示例中的网址,返回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
);

And that's all. Now any matching url that has no id or id is not numeric, autimatically triggers not found error (for which there are plenty of ways to handle, one in your example, another by using custom HandleErrorAttribute, etc). And you can use non-nullable int parameters on your actions.

就这样。现在任何没有id或id的匹配url都不是数字,自动触发找不到错误(有很多方法可以处理,一个在你的例子中,另一个通过使用自定义HandleErrorAttribute等)。并且您可以在操作中使用不可为空的int参数。

#3


0  

I managed to get this working by adding this route at the end of all routes:

我设法通过在所有路线的末尾添加此路线来实现此目的:

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

Note: First I followed this: How can I properly handle 404 in ASP.NET MVC?

注意:首先我遵循这个:如何在ASP.NET MVC中正确处理404?

#1


6  

Best way? Action method selector attribute!

To actually avoid nullable method arguments I suggest that you write an Action Method Selector attribute that will actually only match your action method when id is supplied. It won't say that argument wasn't supplied but that it couldn't match any action methods for the given request.

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

I would call this action selector RequireRouteValuesAttribute and would work this way:

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

[RequireRouteValues("id")]
public ActionResult GetAccount(int id)
{
    ...
}

Why is this the best solution for your problem?

If you look at your code you'd like to return a 404 on actions that match name but parameter binding failed (either because it wasn't supplied or any other reason). Your action so to speak requires particular action parameter otherwise a 404 is returned.

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

So when adding action selector attribute adds the requirement on the action so it has to match name (this is given by MVC) and also require particular action parameters. Whenever id is not supplied this action is not matched. If there's another action that does match is not the issue here because that particular action will get executed. The main thing is accomplished. Action doesn't match for invalid route request and a 404 is returned instead.

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

There's an app code for that!

Check my blog post that implements this kind of attribute that you can use out of the box. It does exactly what you're after: it won't match your action method if route data provided doesn't have all required values.

查看我的博客帖子,它实现了您可以立即使用的这种属性。它完全符合您的要求:如果提供的路径数据没有所有必需值,它将与您的操作方法不匹配。

#2


1  

Disclaimer: this does not cover all the cases

免责声明:这并未涵盖所有案例

For urls in your examples, returning 404 can be done in single line. Just add route constraint for id parameter.

对于示例中的网址,返回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
);

And that's all. Now any matching url that has no id or id is not numeric, autimatically triggers not found error (for which there are plenty of ways to handle, one in your example, another by using custom HandleErrorAttribute, etc). And you can use non-nullable int parameters on your actions.

就这样。现在任何没有id或id的匹配url都不是数字,自动触发找不到错误(有很多方法可以处理,一个在你的例子中,另一个通过使用自定义HandleErrorAttribute等)。并且您可以在操作中使用不可为空的int参数。

#3


0  

I managed to get this working by adding this route at the end of all routes:

我设法通过在所有路线的末尾添加此路线来实现此目的:

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

Note: First I followed this: How can I properly handle 404 in ASP.NET MVC?

注意:首先我遵循这个:如何在ASP.NET MVC中正确处理404?