Правильное сообщение 404 для отсутствующего контроллера ASP.Net MVC - PullRequest
3 голосов
/ 18 апреля 2011

У меня есть приложение MVC 2, которое всегда должно выдавать «хорошую» страницу 404.

Однако в настоящее время я получаю низкоуровневое .Net one: «Ошибка сервера в приложении / sitename»... "

У меня есть базовый контроллер, у которого есть действие NotFound, которое отобразит красивую страницу 404.

Обработка пропущенных действий:

protected override void HandleUnknownAction(string actionName)
{
    this.NotFound(actionName).ExecuteResult(this.ControllerContext);
}

Таким образом, посещение {site}/ValidController/NotAnAction проходит правильно.

Однако посещение {site}/NotAController не выполняется.

У меня настроены маршруты с перехватом всех:

routes.MapRoute(
    "MVC routes",
    "{controller}/{action}/{id}",
    new { action = "Index", id = UrlParameter.Optional });

routes.MapRoute(
    "Catch All", 
    "{*url}",
    new { controller = "System", action = "NotFound" });

Поймать все правильно, ловит маршруты, которые не совпадают.

Таким образом, {site}/Invalid/Action/id/extra правильно маршрутизируется через перехватить все.

Однако {site}/Invalid выбирается через«Маршруты MVC» и ASP.Net ищут InvalidController и выдают тупое исключение, когда не находят его.

Я знаю, что могу переопределить это на уровне web.config, ноэто просто перенаправляет на страницу.Я хотел бы знать, когда шаблон маршрута соответствует, но контроллер не является допустимым именем контроллера.

Где я могу поймать и изменить это поведение?

Ответы [ 2 ]

5 голосов
/ 18 апреля 2011

Я наконец нашел ответ на этот вопрос, хотя он все еще не идеален.

Вы можете ограничить имена контроллеров, которым разрешено совпадать с маршрутом, используя регулярное выражение, поэтому, если мы примем реализацию контроллера по умолчаниюНа фабрике мы можем выяснить все возможные имена классов, которые поддерживаются:

// build up a list of known controllers, so that we don't let users hit ones that don't exist
var allMvcControllers = 
    from t in typeof(Global).Assembly.GetTypes()
    where t != null &&
        t.IsPublic &&
        !t.IsAbstract &&
        t.Name.EndsWith("Controller", StringComparison.OrdinalIgnoreCase) &&
        typeof(IController).IsAssignableFrom(t)
    select t.Name.Substring(0, t.Name.Length - 10);

// create a route constraint that requires the controller to be one of the reflected class names
var controllerConstraint = new
{
    controller = "(" + string.Join("|", allMvcControllers.ToArray()) + ")"
};

// default MVC route
routes.MapRoute(
    "MVC",
    "{controller}/{action}/{id}",
    new { action = "Index", id = UrlParameter.Optional },
    controllerConstraint);

// fall back route for unmatched patterns or invalid controller names
routes.MapRoute(
    "Catch All", 
    "{*url}",
    new { controller = "System", action = "NotFound" });

Это не идеально, это добавляет удар при запуске приложения и все еще кажется слишком сложным, но у него есть желаемоеэффект.

0 голосов
/ 18 апреля 2011

Настройка на уровне web.config не означает "просто перенаправить на страницу". В приложении MVC вы даете ему "{controller/action}" URL-адрес, который фактически будет вызывать это действие:

<customErrors mode="On" defaultRedirect="/system/problem"> 
    <error statusCode="404" redirect="/system/notfound" />
</customErrors>

Это вызовет NotFound на SystemController.

В вашем действии вы можете, например, получить значение HttpContext.Request.RawUrl, чтобы увидеть, что был ошибочный запрос: "/system/notfound?aspxerrorpath=/Invalid". В этом случае я пытался перейти на InvalidController.

Между прочим, хороший способ справиться с этим - реализовать ELMAH (или Модули регистрации ошибок и обработчики . Скотт Хансельман написал "вступительную" статью об этом , но В настоящее время ELMAH доступен в виде пакета NuGet.

Возможно, вы захотите взглянуть на этот вопрос / вопрос о том, как использовать его с ASP.NET MVC: Как заставить ELMAH работать с атрибутом ASP.NET MVC [HandleError]?

...