Как вернуть состояние 404, когда неверные параметры передаются в мой контроллер ASP.NET MVC? - PullRequest
16 голосов
/ 24 января 2012

Я хочу вернуть HTTP-статус 404, если неверные аргументы переданы моему контроллеру. Например, если у меня есть контроллер, который выглядит так:

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

Тогда я хочу вернуть 404, если, скажем, такие URL встречаются:

/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)
  {
    ...
  }
}

Но, похоже, это плохо работает.

Я видел этот пост и этот ответ , который почти решает мою проблему:

В этом ответе создается резюме 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);
  }
}

На первый взгляд, это похоже на работу, но я накапливаю какие-то проблемы при этом?

Это семантически правильная вещь?

Ответы [ 3 ]

6 голосов
/ 06 февраля 2012

Лучший способ?Атрибут селектора метода действия!

Чтобы фактически избежать обнуляемых аргументов метода, я предлагаю вам написать Атрибут селектора метода действия , который будет фактически соответствовать вашему методу действия только при условии id.Не будет сказано, что аргумент не был предоставлен, но что он не может соответствовать никаким методам действия для данного запроса.

Я бы назвал этот селектор действий RequireRouteValuesAttribute и работал бы так:

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

Почему это лучшее решение для вашей проблемы?

Если вы посмотрите на свой код, вы хотели бы вернуть 404 для действий, которые соответствуют имени, но привязка параметра не удалась (либо потому, что это былоне поставляется или по любой другой причине).Ваше действие, так сказать, требует определенного параметра действия, в противном случае возвращается 404.

Поэтому при добавлении атрибута селектора действия добавляется требование к действию, поэтому оно должно соответствовать имени (это задаетсяMVC), а также требуют определенных параметров действий.Если id не указано, это действие не соответствует.Если есть другое действие, которое соответствует, проблема не здесь, потому что это конкретное действие будет выполнено.Главное выполнено.Действие не совпадает с неверным запросом маршрута, и вместо него возвращается 404.

Для этого есть код приложения !

Проверьте сообщение в моем блоге который реализует этот вид атрибута, который вы можете использовать «из коробки».Он делает именно то, что вам нужно: он не будет соответствовать вашему методу действия, если предоставленные данные маршрута не будут иметь всех необходимых значений.

1 голос
/ 24 января 2012

Отказ от ответственности: это не охватывает все случаи

Для 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
);

И это все.Теперь любой соответствующий URL, который не имеет id или id, не является числовым, автоматически вызывает ошибку «не найдено» (для которой существует множество способов обработки, один в вашем примере, другой с помощью пользовательского HandleErrorAttribute и т. Д.).И вы можете использовать ненулевые int параметры для ваших действий.

0 голосов
/ 01 августа 2012

Мне удалось заставить это работать, добавив этот маршрут в конце всех маршрутов:

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

Примечание: Сначала я следовал этому: Как правильно обрабатывать 404 в ASP.NET MVC

...