Вопрос

Я использую RC2

Использование маршрутизации URL-адресов:

routes.MapRoute(
    "Error",
     "{*url}",
     new { controller = "Errors", action = "NotFound" }  // 404s
);

Приведенное выше, похоже, заботится о подобных запросах (при условии настройки таблиц маршрутов по умолчанию в начальном проекте MVC):"/бла/бла/бла/бла"

Переопределение функции HandleUnknownAction() в самом контроллере:

// 404s - handle here (bad action requested
protected override void HandleUnknownAction(string actionName) {
    ViewData["actionName"] = actionName;
    View("NotFound").ExecuteResult(this.ControllerContext);
}  

Однако предыдущие стратегии не обрабатывают запрос к Плохому / Неизвестному контроллеру.Например, у меня нет "/ IDoNotExist", если я запрашиваю это, я получаю общую страницу 404 с веб-сервера, а не мою 404, если я использую маршрутизацию + переопределение.

Итак, наконец, мой вопрос заключается в следующем: Есть ли какой-нибудь способ перехватить запрос такого типа, используя маршрут или что-то еще в самой платформе MVC?

ИЛИ я должен просто по умолчанию использовать Web.Config customErrors в качестве моего обработчика 404 и забыть все это?Я предполагаю, что если я использую customErrors, мне придется сохранить общую страницу 404 за пределами /Views из-за ограничений Web.Config на прямой доступ.

Это было полезно?

Решение

Код взят из http://blogs.microsoft.co.il/blogs/shay/archive/2009/03/06/real-world-error-hadnling-in-asp-net -mvc-rc2.aspx , а также работает в ASP.net MVC 1.0

Вот как я обрабатываю исключения http:

protected void Application_Error(object sender, EventArgs e)
{
   Exception exception = Server.GetLastError();
   // Log the exception.

   ILogger logger = Container.Resolve<ILogger>();
   logger.Error(exception);

   Response.Clear();

   HttpException httpException = exception as HttpException;

   RouteData routeData = new RouteData();
   routeData.Values.Add("controller", "Error");

   if (httpException == null)
   {
       routeData.Values.Add("action", "Index");
   }
   else //It's an Http Exception, Let's handle it.
   {
       switch (httpException.GetHttpCode())
       {
          case 404:
              // Page not found.
              routeData.Values.Add("action", "HttpError404");
              break;
          case 500:
              // Server error.
              routeData.Values.Add("action", "HttpError500");
              break;

           // Here you can handle Views to other error codes.
           // I choose a General error template  
           default:
              routeData.Values.Add("action", "General");
              break;
      }
  }           

  // Pass exception details to the target error View.
  routeData.Values.Add("error", exception);

  // Clear the error on server.
  Server.ClearError();

  // Avoid IIS7 getting in the middle
  Response.TrySkipIisCustomErrors = true; 

  // Call target Controller and pass the routeData.
  IController errorController = new ErrorController();
  errorController.Execute(new RequestContext(    
       new HttpContextWrapper(Context), routeData));
}

Другие советы

Требования к 404

Ниже приведены мои требования к решению 404, и ниже я показываю, как я его реализую:

  • Я хочу обрабатывать совпадающие маршруты с плохими действиями
  • Я хочу обрабатывать совпадающие маршруты с плохими контроллерами
  • Я хочу обрабатывать несовпадающие маршруты (произвольные URL-адреса, которые мое приложение не может понять) - я не хочу, чтобы они передавались в Global.asax или IIS, потому что тогда я не могу перенаправить обратно в свое приложение MVC должным образом
  • Мне нужен способ обработки таким же образом, как указано выше, пользовательский 404s - like, когда идентификатор отправляется для объекта, который не существует (возможно, удален)
  • Я хочу, чтобы все мои 404 возвращали представление MVC (не статическую страницу), в которое я могу перекачать больше данных позже, если это необходимо (хороший дизайн 404) и они должен верните код состояния HTTP 404

Решение

Я думаю, тебе следует сэкономить Application_Error в Global.asax для более высоких вещей, таких как необработанные исключения и ведение журнала (например Ответ Шэя Джейкоби показывает), но не обработку 404.Вот почему мое предложение не содержит материал 404 из глобального файла.asax.

Шаг 1:Есть общее место для логики ошибок 404

Это хорошая идея для удобства обслуживания.Используйте Контроллер ошибок так что будущие улучшения вашего хорошо оформленная 404 страница может легко адаптироваться.Также, убедитесь, что в вашем ответе указан код 404!

public class ErrorController : MyController
{
    #region Http404

    public ActionResult Http404(string url)
    {
        Response.StatusCode = (int)HttpStatusCode.NotFound;
        var model = new NotFoundViewModel();
        // If the url is relative ('NotFound' route) then replace with Requested path
        model.RequestedUrl = Request.Url.OriginalString.Contains(url) & Request.Url.OriginalString != url ?
            Request.Url.OriginalString : url;
        // Dont get the user stuck in a 'retry loop' by
        // allowing the Referrer to be the same as the Request
        model.ReferrerUrl = Request.UrlReferrer != null &&
            Request.UrlReferrer.OriginalString != model.RequestedUrl ?
            Request.UrlReferrer.OriginalString : null;

        // TODO: insert ILogger here

        return View("NotFound", model);
    }
    public class NotFoundViewModel
    {
        public string RequestedUrl { get; set; }
        public string ReferrerUrl { get; set; }
    }

    #endregion
}

Шаг 2:Используйте базовый класс контроллера, чтобы вы могли легко вызвать свое пользовательское действие 404 и подключить его HandleUnknownAction

404-е в ASP.NET MVC должны быть пойманы в нескольких местах.Первый - это HandleUnknownAction.

Тот Самый InvokeHttp404 метод создает общее место для перенаправления к ErrorController и наш новый Http404 Экшен.Подумай СУХОЙ!

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
}

Шаг 3:Используйте внедрение зависимостей на вашей фабрике контроллеров и подключите 404 HttpExceptions

Вот так (это не обязательно должна быть StructureMap):

Пример MVC1.0:

public class StructureMapControllerFactory : DefaultControllerFactory
{
    protected override IController GetControllerInstance(Type controllerType)
    {
        try
        {
            if (controllerType == null)
                return base.GetControllerInstance(controllerType);
        }
        catch (HttpException ex)
        {
            if (ex.GetHttpCode() == (int)HttpStatusCode.NotFound)
            {
                IController errorController = ObjectFactory.GetInstance<ErrorController>();
                ((ErrorController)errorController).InvokeHttp404(RequestContext.HttpContext);

                return errorController;
            }
            else
                throw ex;
        }

        return ObjectFactory.GetInstance(controllerType) as Controller;
    }
}

Пример MVC2.0:

    protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
    {
        try
        {
            if (controllerType == null)
                return base.GetControllerInstance(requestContext, controllerType);
        }
        catch (HttpException ex)
        {
            if (ex.GetHttpCode() == 404)
            {
                IController errorController = ObjectFactory.GetInstance<ErrorController>();
                ((ErrorController)errorController).InvokeHttp404(requestContext.HttpContext);

                return errorController;
            }
            else
                throw ex;
        }

        return ObjectFactory.GetInstance(controllerType) as Controller;
    }

Я думаю, что лучше отлавливать ошибки ближе к месту их возникновения.Вот почему я предпочитаю вышесказанное Application_Error обработчик.

Это второе место, где можно поймать 404-х человек.

Шаг 4:Добавьте не найденный маршрут в Global.asax для URL-адресов, которые не могут быть проанализированы в вашем приложении

Этот маршрут должен указывать на наш Http404 Экшен.Обратите внимание на url параметр будет относительным URL, потому что механизм маршрутизации удаляет здесь доменную часть?Вот почему у нас есть вся эта логика условных URL-адресов на шаге 1.

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

Это третье и последнее место для перехвата 404-х в приложении MVC, которое вы не вызываете самостоятельно.Если вы не поймаете здесь непревзойденные маршруты, то MVC передаст проблему в ASP.NET (Global.asax), и вы действительно не хотите этого в этой ситуации.

Шаг 5:Наконец, вызовите 404s, когда ваше приложение не может что-то найти

Например, когда неверный идентификатор отправляется моему контролеру кредитов (происходит из MyController):

    //
    // GET: /Detail/ID

    public ActionResult Detail(int ID)
    {
        Loan loan = this._svc.GetLoans().WithID(ID);
        if (loan == null)
            return this.InvokeHttp404(HttpContext);
        else
            return View(loan);
    }

Было бы неплохо, если бы все это можно было подключить в меньшем количестве мест с меньшим количеством кода, но я думаю, что это решение более ремонтопригодно, более тестируемо и достаточно прагматично.

Спасибо за отзыв на данный момент.Я бы с удовольствием получил больше.

ПРИМЕЧАНИЕ:Это было существенно отредактировано по сравнению с моим первоначальным ответом, но цель / требования те же - вот почему я не добавил новый ответ

ASP.NET MVC не очень хорошо поддерживает пользовательские страницы 404.Фабрика пользовательских контроллеров, универсальный маршрут, базовый класс контроллера с HandleUnknownAction - аргх!

Пользовательские страницы ошибок IIS на данный момент являются лучшей альтернативой:

web.config

<system.webServer>
  <httpErrors errorMode="Custom" existingResponse="Replace">
    <remove statusCode="404" />
    <error statusCode="404" responseMode="ExecuteURL" path="/Error/PageNotFound" />
  </httpErrors>
</system.webServer>

Контроллер ошибок

public class ErrorController : Controller
{
    public ActionResult PageNotFound()
    {
        Response.StatusCode = 404;
        return View();
    }
}

Примерный проект

Быстрый ответ / TL;DR

enter image description here

Для самых ленивых людей на свете:

Install-Package MagicalUnicornMvcErrorToolkit -Version 1.0

Затем удалите эту строку из global.asax

GlobalFilters.Filters.Add(new HandleErrorAttribute());

И это только для IIS7+ и IIS Express.

Если вы используете " Кассини " ..что ж ..um ..er..неловко ...awkward


Длинный, объясненный ответ

Я знаю, что на этот вопрос уже был дан ответ.Но ответ ДЕЙСТВИТЕЛЬНО ПРОСТ (выпьем за Дэвид Фаулер и Дэмиан Эдвардс за то, что действительно ответил на этот вопрос).

Есть не нужно делать ничего на заказ.

Для ASP.NET MVC3, все обрывки на месте.

Шаг 1 -> Обновите web.config в ДВУХ местах.

<system.web>
    <customErrors mode="On" defaultRedirect="/ServerError">
      <error statusCode="404" redirect="/NotFound" />
    </customErrors>

и

<system.webServer>
    <httpErrors errorMode="Custom">
      <remove statusCode="404" subStatusCode="-1" />
      <error statusCode="404" path="/NotFound" responseMode="ExecuteURL" />
      <remove statusCode="500" subStatusCode="-1" />
      <error statusCode="500" path="/ServerError" responseMode="ExecuteURL" />
    </httpErrors>    

...
<system.webServer>
...
</system.web>

Теперь внимательно обратите внимание на МАРШРУТЫ, которые я решил использовать.Вы можете использовать что угодно, но мои маршруты следующие

  • /NotFound <- для 404 не найдено, страница с ошибкой.
  • /ServerError <- для любой другой ошибки укажите ошибки, которые происходят в моем коде.это внутренняя ошибка сервера 500

Посмотрите, как выглядит первый раздел в <system.web> только имеет один пользовательская запись?Тот Самый statusCode="404" вход?Я перечислил только один код состояния, потому что все остальные ошибки, включая 500 Server Error (то есть.те досадные ошибки, которые возникают, когда в вашем коде есть ошибка и происходит сбой запроса пользователя)..все остальные ошибки обрабатываются настройкой defaultRedirect="/ServerError" ..в котором говорится, что если вы не нашли страницу 404, то, пожалуйста, перейдите к маршруту /ServerError.

ОК.это исключено из правил..теперь перейдем к моим маршрутам, перечисленным в global.asax

Шаг 2 - Создание маршрутов в Global.asax

Вот полный раздел моего маршрута..

public static void RegisterRoutes(RouteCollection routes)
{
    routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
    routes.IgnoreRoute("{*favicon}", new {favicon = @"(.*/)?favicon.ico(/.*)?"});

    routes.MapRoute(
        "Error - 404",
        "NotFound",
        new { controller = "Error", action = "NotFound" }
        );

    routes.MapRoute(
        "Error - 500",
        "ServerError",
        new { controller = "Error", action = "ServerError"}
        );

    routes.MapRoute(
        "Default", // Route name
        "{controller}/{action}/{id}", // URL with parameters
        new {controller = "Home", action = "Index", id = UrlParameter.Optional}
        );
}

В нем перечислены два маршрута игнорирования -> axd's и favicons (ооо!бонусный маршрут игнорирования, для вас!) Затем (и порядок здесь ОБЯЗАТЕЛЕН) у меня есть два явных маршрута обработки ошибок..за ним следуют любые другие маршруты.В данном случае - по умолчанию.Конечно, у меня есть и другие, но это особенность моего веб-сайта. Просто убедитесь, что маршруты ошибок находятся в верхней части списка.Порядок - это императив.

Наконец, пока мы находимся внутри нашего global.asax файл, мы НЕ регистрируем атрибут HandleError глобально.Нет, нет, нет, сэр.Надда.Нет.Nien.Отрицательный.Нееееееееееет...

Удалите эту строку из global.asax

GlobalFilters.Filters.Add(new HandleErrorAttribute());

Шаг 3 - Создайте контроллер с помощью методов действия

Сейчас же ..мы добавляем контроллер с двумя методами действия ...

public class ErrorController : Controller
{
    public ActionResult NotFound()
    {
        Response.StatusCode = (int)HttpStatusCode.NotFound;
        return View();
    }

    public ActionResult ServerError()
    {
        Response.StatusCode = (int)HttpStatusCode.InternalServerError;

        // Todo: Pass the exception into the view model, which you can make.
        //       That's an exercise, dear reader, for -you-.
        //       In case u want to pass it to the view, if you're admin, etc.
        // if (User.IsAdmin) // <-- I just made that up :) U get the idea...
        // {
        //     var exception = Server.GetLastError();
        //     // etc..
        // }

        return View();
    }

    // Shhh .. secret test method .. ooOOooOooOOOooohhhhhhhh
    public ActionResult ThrowError()
    {
        throw new NotImplementedException("Pew ^ Pew");
    }
}

Хорошо, давайте проверим это.Прежде всего, есть НЕТ [HandleError] атрибут здесь.Почему?Потому что встроенный ASP.NET фреймворк уже обрабатывает ошибки, И мы указали все, что нам нужно сделать, чтобы обработать ошибку :) Это в этом методе!

Далее, у меня есть два метода действия.Там нет ничего сложного.Если вы хотите показать какую-либо информацию об исключении, то вы можете использовать Server.GetLastError() чтобы получить эту информацию.

Бонусный WTF:Да, я создал третий метод действия, чтобы протестировать обработку ошибок.

Шаг 4 - Создайте представления

И, наконец, создайте два представления.Поместите их в обычное место просмотра для этого контроллера.

enter image description here

Бонусные комментарии

  • Вам не нужен Application_Error(object sender, EventArgs e)
  • Все вышеперечисленные шаги работают на 100% идеально с Эльма.Эльма, мать твою, роксана!

И это, друзья мои, должно быть именно так.

А теперь поздравляю с тем, что вы так много прочитали, и получаю Единорога в качестве приза!

enter image description here

Я провел расследование ОЧЕНЬ МНОГО о том, как правильно управлять 404s в MVC (в частности, MVC3), и это, ИМХО, лучшее решение, которое я придумал:

В global.asax:

public class MvcApplication : HttpApplication
{
    protected void Application_EndRequest()
    {
        if (Context.Response.StatusCode == 404)
        {
            Response.Clear();

            var rd = new RouteData();
            rd.DataTokens["area"] = "AreaName"; // In case controller is in another area
            rd.Values["controller"] = "Errors";
            rd.Values["action"] = "NotFound";

            IController c = new ErrorsController();
            c.Execute(new RequestContext(new HttpContextWrapper(Context), rd));
        }
    }
}

Контроллер ошибок:

public sealed class ErrorsController : Controller
{
    public ActionResult NotFound()
    {
        ActionResult result;

        object model = Request.Url.PathAndQuery;

        if (!Request.IsAjaxRequest())
            result = View(model);
        else
            result = PartialView("_NotFound", model);

        return result;
    }
}

(Необязательно)

Объяснение:

AFAIK, есть 6 различных случаев, когда приложения ASP.NET MVC3 могут генерировать 404 секунды.

(Автоматически генерируется ASP.NET Фреймворком:)

(1) URL-адрес не находит соответствия в таблице маршрутов.

(Автоматически генерируется с помощью ASP.NET MVC Framework:)

(2) URL-адрес находит соответствие в таблице маршрутов, но указывает несуществующий контроллер.

(3) URL-адрес находит соответствие в таблице маршрутов, но указывает несуществующее действие.

(Сгенерировано вручную:)

(4) Действие возвращает HttpNotFoundResult с помощью метода HttpNotFound().

(5) Действие вызывает исключение HttpException с кодом состояния 404.

(6) Действие вручную изменяет ответ.Свойство StatusCode на 404.

Обычно вы хотите достичь 3 целей:

(1) Покажите пользователю пользовательскую страницу с ошибкой 404.

(2) Сохраняйте код статуса 404 в ответе клиента (особенно важно для SEO).

(3) Отправьте ответ напрямую, без использования перенаправления 302.

Существуют различные способы попытаться достичь этого:

(1)

<system.web>
    <customErrors mode="On">
        <error statusCode="404" redirect="~/Errors/NotFound"/>
    </customError>
</system.web>

Проблемы с этим решением:

  1. Не соответствует цели (1) в случаях, когда (1), (4), (6).
  2. Автоматически не соответствует цели (2).Он должен быть запрограммирован вручную.
  3. Не соответствует цели (3).

(2)

<system.webServer>
    <httpErrors errorMode="Custom">
        <remove statusCode="404"/>
        <error statusCode="404" path="App/Errors/NotFound" responseMode="ExecuteURL"/>
    </httpErrors>
</system.webServer>

Проблемы с этим решением:

  1. Работает только на IIS 7+.
  2. Не соответствует цели (1) в случаях, когда (2), (3), (5).
  3. Автоматически не соответствует цели (2).Он должен быть запрограммирован вручную.

(3)

<system.webServer>
    <httpErrors errorMode="Custom" existingResponse="Replace">
        <remove statusCode="404"/>
        <error statusCode="404" path="App/Errors/NotFound" responseMode="ExecuteURL"/>
    </httpErrors>
</system.webServer>

Проблемы с этим решением:

  1. Работает только на IIS 7+.
  2. Автоматически не соответствует цели (2).Он должен быть запрограммирован вручную.
  3. Это скрывает http-исключения уровня приложения.Например.не удается использовать раздел customErrors, System.Web.Mvc.HandleErrorAttribute и т.д.Он не может показывать только общие страницы ошибок.

(4)

<system.web>
    <customErrors mode="On">
        <error statusCode="404" redirect="~/Errors/NotFound"/>
    </customError>
</system.web>

и

<system.webServer>
    <httpErrors errorMode="Custom">
        <remove statusCode="404"/>
        <error statusCode="404" path="App/Errors/NotFound" responseMode="ExecuteURL"/>
    </httpErrors>
</system.webServer>

Проблемы с этим решением:

  1. Работает только на IIS 7+.
  2. Автоматически не соответствует цели (2).Он должен быть запрограммирован вручную.
  3. Не соответствует цели (3) в случаях, когда (2), (3), (5).

Люди, которые раньше сталкивались с этим, даже пытались создавать свои собственные библиотеки (см. http://aboutcode.net/2011/02/26/handling-not-found-with-asp-net-mvc3.html).Но предыдущее решение, похоже, охватывает все случаи без сложности использования внешней библиотеки.

Мне действительно нравится решение коттсаков, и я думаю, что оно очень четко объяснено. единственным моим дополнением было изменение шага 2 следующим образом

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
}

По сути, это останавливает URL-адреса, содержащие недопустимые действия и контроллеры, от запуска процедуры исключения дважды. например, для таких URL, как asdfsdf / dfgdfgd

Единственный способ заставить метод @ cottsak работать с недопустимыми контроллерами - это изменить существующий запрос маршрута в CustomControllerFactory, например, так:

public class CustomControllerFactory : DefaultControllerFactory
{
    protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
    {
        try
        {
            if (controllerType == null)
                return base.GetControllerInstance(requestContext, controllerType); 
            else
                return ObjectFactory.GetInstance(controllerType) as Controller;
        }
        catch (HttpException ex)
        {
            if (ex.GetHttpCode() == (int)HttpStatusCode.NotFound)
            {
                requestContext.RouteData.Values["controller"] = "Error";
                requestContext.RouteData.Values["action"] = "Http404";
                requestContext.RouteData.Values.Add("url", requestContext.HttpContext.Request.Url.OriginalString);

                return ObjectFactory.GetInstance<ErrorController>();
            }
            else
                throw ex;
        }
    }
}

Я должен упомянуть, что я использую MVC 2.0.

Вот еще один метод, использующий инструменты MVC, с помощью которых вы можете обрабатывать запросы к неверным именам контроллеров, неверным именам маршрутов и любым другим критериям, которые вы считаете подходящими для метода действия.Лично я предпочитаю избегать как можно большего количества настроек web.config, потому что они выполняют перенаправление 302/200 и не поддерживают ResponseRewrite (Server.Transfer) использование Razor views.Я бы предпочел вернуть 404 с пользовательской страницей ошибок по соображениям SEO.

Кое-что из этого - новый взгляд на технику котсака, описанную выше.

Это решение также использует минимальные настройки web.config, отдавая предпочтение фильтрам ошибок MVC 3.

Использование

Просто создайте HttpException из action или пользовательского ActionFilterAttribute.

Throw New HttpException(HttpStatusCode.NotFound, "[Custom Exception Message Here]")

Шаг 1

Добавьте следующий параметр в свой web.config.Это необходимо для использования атрибута HandleErrorAttribute MVC.

<customErrors mode="On" redirectMode="ResponseRedirect" />

Шаг 2

Добавьте пользовательский атрибут HandleHttpErrorAttribute, аналогичный атрибуту HandleErrorAttribute платформы MVC, за исключением ошибок HTTP:

<AttributeUsage(AttributeTargets.All, AllowMultiple:=True)>
Public Class HandleHttpErrorAttribute
    Inherits FilterAttribute
    Implements IExceptionFilter

    Private Const m_DefaultViewFormat As String = "ErrorHttp{0}"

    Private m_HttpCode As HttpStatusCode
    Private m_Master As String
    Private m_View As String

    Public Property HttpCode As HttpStatusCode
        Get
            If m_HttpCode = 0 Then
                Return HttpStatusCode.NotFound
            End If
            Return m_HttpCode
        End Get
        Set(value As HttpStatusCode)
            m_HttpCode = value
        End Set
    End Property

    Public Property Master As String
        Get
            Return If(m_Master, String.Empty)
        End Get
        Set(value As String)
            m_Master = value
        End Set
    End Property

    Public Property View As String
        Get
            If String.IsNullOrEmpty(m_View) Then
                Return String.Format(m_DefaultViewFormat, Me.HttpCode)
            End If
            Return m_View
        End Get
        Set(value As String)
            m_View = value
        End Set
    End Property

    Public Sub OnException(filterContext As System.Web.Mvc.ExceptionContext) Implements System.Web.Mvc.IExceptionFilter.OnException
        If filterContext Is Nothing Then Throw New ArgumentException("filterContext")

        If filterContext.IsChildAction Then
            Return
        End If

        If filterContext.ExceptionHandled OrElse Not filterContext.HttpContext.IsCustomErrorEnabled Then
            Return
        End If

        Dim ex As HttpException = TryCast(filterContext.Exception, HttpException)
        If ex Is Nothing OrElse ex.GetHttpCode = HttpStatusCode.InternalServerError Then
            Return
        End If

        If ex.GetHttpCode <> Me.HttpCode Then
            Return
        End If

        Dim controllerName As String = filterContext.RouteData.Values("controller")
        Dim actionName As String = filterContext.RouteData.Values("action")
        Dim model As New HandleErrorInfo(filterContext.Exception, controllerName, actionName)

        filterContext.Result = New ViewResult With {
            .ViewName = Me.View,
            .MasterName = Me.Master,
            .ViewData = New ViewDataDictionary(Of HandleErrorInfo)(model),
            .TempData = filterContext.Controller.TempData
        }
        filterContext.ExceptionHandled = True
        filterContext.HttpContext.Response.Clear()
        filterContext.HttpContext.Response.StatusCode = Me.HttpCode
        filterContext.HttpContext.Response.TrySkipIisCustomErrors = True
    End Sub
End Class

Шаг 3

Добавьте фильтры в GlobalFilterCollection (GlobalFilters.Filters) в Global.asax.В этом примере все ошибки InternalServerError (500) будут перенаправлены в общее представление ошибок (Views/Shared/Error.vbhtml).Обнаруженные ошибки (404) также будут отправлены в ErrorHttp404.vbhtml в общих представлениях.Я добавил сюда ошибку 401, чтобы показать вам, как это можно расширить для дополнительных кодов ошибок HTTP.Обратите внимание, что это должны быть общие представления, и все они используют System.Web.Mvc.HandleErrorInfo объект как модель.

filters.Add(New HandleHttpErrorAttribute With {.View = "ErrorHttp401", .HttpCode = HttpStatusCode.Unauthorized})
filters.Add(New HandleHttpErrorAttribute With {.View = "ErrorHttp404", .HttpCode = HttpStatusCode.NotFound})
filters.Add(New HandleErrorAttribute With {.View = "Error"})

Шаг 4

Создайте базовый класс контроллера и наследуйте от него в своих контроллерах.Этот шаг позволяет нам обрабатывать неизвестные имена действий и вызывать ошибку HTTP 404 в нашем HandleHttpErrorAttribute.

Public Class BaseController
    Inherits System.Web.Mvc.Controller

    Protected Overrides Sub HandleUnknownAction(actionName As String)
        Me.ActionInvoker.InvokeAction(Me.ControllerContext, "Unknown")
    End Sub

    Public Function Unknown() As ActionResult
        Throw New HttpException(HttpStatusCode.NotFound, "The specified controller or action does not exist.")
        Return New EmptyResult
    End Function
End Class

Шаг 5

Создайте переопределение ControllerFactory и переопределите его в вашем глобальном файле.asax в Application_Start.Этот шаг позволяет нам вызвать исключение HTTP 404, когда было указано недопустимое имя контроллера.

Public Class MyControllerFactory
    Inherits DefaultControllerFactory

    Protected Overrides Function GetControllerInstance(requestContext As System.Web.Routing.RequestContext, controllerType As System.Type) As System.Web.Mvc.IController
        Try
            Return MyBase.GetControllerInstance(requestContext, controllerType)
        Catch ex As HttpException
            Return DependencyResolver.Current.GetService(Of BaseController)()
        End Try
    End Function
End Class

'In Global.asax.vb Application_Start:

controllerBuilder.Current.SetControllerFactory(New MyControllerFactory)

Шаг 6

Включите специальный маршрут в вашу таблицу маршрутизации.Маршруты для неизвестного действия BaseController.Это поможет нам вызвать 404 в случае, когда пользователь обращается к неизвестному контроллеру или неизвестному действию.

'BaseController
routes.MapRoute( _
    "Unknown", "BaseController/{action}/{id}", _
    New With {.controller = "BaseController", .action = "Unknown", .id = UrlParameter.Optional} _
)

Краткие сведения

В этом примере продемонстрировано, как можно использовать MVC framework для возврата 404 кодов ошибок Http в браузер без перенаправления, используя атрибуты фильтра и общие представления ошибок.Он также демонстрирует отображение той же пользовательской страницы ошибок при указании недопустимых имен контроллеров и действий.

Я добавлю скриншот с недопустимым именем контроллера, именем действия и пользовательским 404, созданным из действия Home / TriggerNotFound, если наберу достаточно голосов, чтобы опубликовать его =).Fiddler возвращает сообщение 404, когда я получаю доступ к следующим URL-адресам, используя это решение:

/InvalidController
/Home/InvalidRoute
/InvalidController/InvalidRoute
/Home/TriggerNotFound

пост котсака выше и эти статьи были хорошими ссылками.

Мое сокращенное решение, которое работает с необработанными областями, контроллерами и действиями:

<Ол>
  • Создайте представление 404.cshtml.

  • Создайте базовый класс для ваших контроллеров:

    public class Controller : System.Web.Mvc.Controller
    {
        protected override void HandleUnknownAction(string actionName)
        {
            Http404().ExecuteResult(ControllerContext);
        }
    
        protected virtual ViewResult Http404()
        {
            Response.StatusCode = (int)HttpStatusCode.NotFound;
            return View("404");
        }
    }
    
  • Создайте фабрику пользовательских контроллеров, возвращающую базовый контроллер как запасной вариант:

    public class ControllerFactory : DefaultControllerFactory
    {
        protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
        {
            if (controllerType != null)
                return base.GetControllerInstance(requestContext, controllerType);
    
            return new Controller();
        }
    }
    
  • Добавьте в Application_Start() следующую строку:

    ControllerBuilder.Current.SetControllerFactory(typeof(ControllerFactory));
    
  • В MVC4 WebAPI 404 может быть обработан следующим образом,

    КУРСЫ APICONTROLLER

        // GET /api/courses/5
        public HttpResponseMessage<Courses> Get(int id)
        {
            HttpResponseMessage<Courses> resp = null;
    
            var aCourse = _courses.Where(c => c.Id == id).FirstOrDefault();
    
            resp = aCourse == null ? new HttpResponseMessage<Courses>(System.Net.HttpStatusCode.NotFound) : new HttpResponseMessage<Courses>(aCourse);
    
            return resp;
        }
    

    ДОМАШНИЙ КОНТРОЛЛЕР

    public ActionResult Course(int id)
    {
        return View(id);
    }
    

    Вид

    <div id="course"></div>
    <script type="text/javascript">
        var id = @Model;
        var course = $('#course');
        $.ajax({    
            url: '/api/courses/' + id,
            success: function (data) {
                course.text(data.Name);
            },
            statusCode: {
                404: function() 
                {
                    course.text('Course not available!');    
                }
            }
        });
    </script>
    

    ГЛОБАЛЬНЫЙ

    public static void RegisterRoutes(RouteCollection routes)
    {
        routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
    
        routes.MapHttpRoute(
            name: "DefaultApi",
            routeTemplate: "api/{controller}/{id}",
            defaults: new { id = RouteParameter.Optional }
        );
    
        routes.MapRoute(
            name: "Default",
            url: "{controller}/{action}/{id}",
            defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
        );
    }
    

    Результаты

    enter image description here

    Попробуйте NotFoundMVC на nuget. Это работает, без настройки.

    Мое решение, если кто-то найдет его полезным.

    В Web.config:

    <system.web>
        <customErrors mode="On" defaultRedirect="Error" >
          <error statusCode="404" redirect="~/Error/PageNotFound"/>
        </customErrors>
        ...
    </system.web>
    

    В Controllers/ErrorController.cs:

    public class ErrorController : Controller
    {
        public ActionResult PageNotFound()
        {
            if(Request.IsAjaxRequest()) {
                Response.StatusCode = (int)HttpStatusCode.NotFound;
                return Content("Not Found", "text/plain");
            }
    
            return View();
        }
    }
    

    Добавьте PageNotFound.cshtml в папку Shared и все.

    Мне кажется, что стандартный CustomErrors конфигурация должно просто сработать однако из-за зависимости от Server.Transfer похоже, что внутренняя реализация ResponseRewrite не совместим с MVC.

    Мне это кажется вопиющей функциональной дырой, поэтому я решил повторно реализовать эту функцию с помощью HTTP-модуля.Приведенное ниже решение позволяет вам обрабатывать любой код состояния HTTP (включая 404) путем перенаправления на любой допустимый маршрут MVC точно так же, как вы делали бы это обычно.

    <customErrors mode="RemoteOnly" redirectMode="ResponseRewrite">
        <error statusCode="404" redirect="404.aspx" />
        <error statusCode="500" redirect="~/MVCErrorPage" />
    </customErrors>
    

    Это было протестировано на следующих платформах;

    • MVC4 в режиме интегрированного конвейера (IIS Express 8)
    • MVC4 в классическом режиме (VS Development Server, Cassini)
    • MVC4 в классическом режиме (IIS6)

    Преимущества

    • Универсальное решение, которое может быть добавлено в любой проект MVC
    • Включает поддержку традиционной пользовательской конфигурации ошибок
    • Работает как в интегрированном конвейерном, так и в классическом режимах

    Решение

    namespace Foo.Bar.Modules {
    
        /// <summary>
        /// Enables support for CustomErrors ResponseRewrite mode in MVC.
        /// </summary>
        public class ErrorHandler : IHttpModule {
    
            private HttpContext HttpContext { get { return HttpContext.Current; } }
            private CustomErrorsSection CustomErrors { get; set; }
    
            public void Init(HttpApplication application) {
                System.Configuration.Configuration configuration = WebConfigurationManager.OpenWebConfiguration("~");
                CustomErrors = (CustomErrorsSection)configuration.GetSection("system.web/customErrors");
    
                application.EndRequest += Application_EndRequest;
            }
    
            protected void Application_EndRequest(object sender, EventArgs e) {
    
                // only handle rewrite mode, ignore redirect configuration (if it ain't broke don't re-implement it)
                if (CustomErrors.RedirectMode == CustomErrorsRedirectMode.ResponseRewrite && HttpContext.IsCustomErrorEnabled) {
    
                    int statusCode = HttpContext.Response.StatusCode;
    
                    // if this request has thrown an exception then find the real status code
                    Exception exception = HttpContext.Error;
                    if (exception != null) {
                        // set default error status code for application exceptions
                        statusCode = (int)HttpStatusCode.InternalServerError;
                    }
    
                    HttpException httpException = exception as HttpException;
                    if (httpException != null) {
                        statusCode = httpException.GetHttpCode();
                    }
    
                    if ((HttpStatusCode)statusCode != HttpStatusCode.OK) {
    
                        Dictionary<int, string> errorPaths = new Dictionary<int, string>();
    
                        foreach (CustomError error in CustomErrors.Errors) {
                            errorPaths.Add(error.StatusCode, error.Redirect);
                        }
    
                        // find a custom error path for this status code
                        if (errorPaths.Keys.Contains(statusCode)) {
                            string url = errorPaths[statusCode];
    
                            // avoid circular redirects
                            if (!HttpContext.Request.Url.AbsolutePath.Equals(VirtualPathUtility.ToAbsolute(url))) {
    
                                HttpContext.Response.Clear();
                                HttpContext.Response.TrySkipIisCustomErrors = true;
    
                                HttpContext.Server.ClearError();
    
                                // do the redirect here
                                if (HttpRuntime.UsingIntegratedPipeline) {
                                    HttpContext.Server.TransferRequest(url, true);
                                }
                                else {
                                    HttpContext.RewritePath(url, false);
    
                                    IHttpHandler httpHandler = new MvcHttpHandler();
                                    httpHandler.ProcessRequest(HttpContext);
                                }
    
                                // return the original status code to the client
                                // (this won't work in integrated pipleline mode)
                                HttpContext.Response.StatusCode = statusCode;
    
                            }
                        }
    
                    }
    
                }
    
            }
    
            public void Dispose() {
    
            }
    
    
        }
    
    }
    

    Использование

    Включите это в качестве окончательного HTTP-модуля в свой web.config

      <system.web>
        <httpModules>
          <add name="ErrorHandler" type="Foo.Bar.Modules.ErrorHandler" />
        </httpModules>
      </system.web>
    
      <!-- IIS7+ -->
      <system.webServer>
        <modules>
          <add name="ErrorHandler" type="Foo.Bar.Modules.ErrorHandler" />
        </modules>
      </system.webServer>
    

    Для тех из вас, кто обращает внимание, вы заметите, что в режиме интегрированного конвейера это всегда будет отвечать HTTP 200 из-за способа Server.TransferRequest работает.Чтобы вернуть правильный код ошибки, я использую следующий контроллер ошибок.

    public class ErrorController : Controller {
    
        public ErrorController() { }
    
        public ActionResult Index(int id) {
            // pass real error code to client
            HttpContext.Response.StatusCode = id;
            HttpContext.Response.TrySkipIisCustomErrors = true;
    
            return View("Errors/" + id.ToString());
        }
    
    }
    

    Работа с ошибками в ASP.NET MVC - это просто заноза в заднице.Я перепробовал множество предложений на этой странице и на других вопросах и сайтах, но ничего хорошего не вышло.Одно из предложений состояло в том, чтобы обрабатывать ошибки на web.config внутри система.веб-сервер но это всего лишь возвращает пустые страницы.

    Моя цель при разработке этого решения состояла в том, чтобы;

    • НЕ ПЕРЕНАПРАВЛЯТЬ
    • Возвращайте ПРАВИЛЬНЫЕ КОДЫ СОСТОЯНИЯ, а не 200 / Ok, как при обработке ошибок по умолчанию

    Вот мое решение.

    1.Добавьте следующее к система.веб Раздел

       <system.web>
         <customErrors mode="On" redirectMode="ResponseRewrite">
          <error statusCode="404"  redirect="~/Error/404.aspx" />
          <error statusCode="500" redirect="~/Error/500.aspx" />
         </customErrors>
        <system.web>
    

    Приведенное выше обрабатывает любые URL-адреса, не обработанные маршруты.конфигурация и необработанные исключения, особенно те, которые встречаются в представлениях.Обратите внимание, что я использовал aspx нет HTML.Это для того, чтобы я мог добавить код ответа о коде, стоящем за ним.

    2.Создайте папку под названием Ошибка (или что бы вы ни предпочитали) в корневой каталог вашего проекта и добавьте две веб-формы.Ниже приведена моя страница 404;

    <%@ Page Language="C#" AutoEventWireup="true" CodeBehind="404.aspx.cs" Inherits="Myapp.Error._404" %>
    
    <!DOCTYPE html>
    <html>
    <head>
        <meta charset="utf-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title >Page Not found</title>
        <link href="<%=ResolveUrl("~/Content/myapp.css")%>" rel="stylesheet" />
    </head>
    <body>
        <div class="top-nav">
          <a runat="server" class="company-logo" href="~/"></a>
        </div>
        <div>
            <h1>404 - Page Not found</h1>
            <p>The page you are looking for cannot be found.</p>
            <hr />
            <footer></footer>
        </div>
    </body>
    </html>
    

    И в коде позади я устанавливаю код ответа

    protected void Page_Load(object sender, EventArgs e)
    {
        Response.StatusCode = 404;
    }
    

    Проделайте то же самое для 500 страниц

    3. Для обработки ошибок внутри контроллеров.Есть много способов сделать это.Это то, что сработало у меня.Все мои контроллеры наследуются от базового контроллера.В базовом контроллере у меня есть следующие методы

    protected ActionResult ShowNotFound()
    {
        return ShowNotFound("Page not found....");
    }
    
    protected ActionResult ShowNotFound(string message)
    {
        return ShowCustomError(HttpStatusCode.NotFound, message);
    }
    
    protected ActionResult ShowServerError()
    {
        return ShowServerError("Application error....");
    }
    
    protected ActionResult ShowServerError(string message)
    {
        return ShowCustomError(HttpStatusCode.InternalServerError, message);
    }
    
    protected ActionResult ShowNotAuthorized()
    {
        return ShowNotAuthorized("You are not allowed ....");
    
    }
    
    protected ActionResult ShowNotAuthorized(string message)
    {
        return ShowCustomError(HttpStatusCode.Forbidden, message);
    }
    
    protected ActionResult ShowCustomError(HttpStatusCode statusCode, string message)
    {
        Response.StatusCode = (int)statusCode;
        string title = "";
        switch (statusCode)
        {
            case HttpStatusCode.NotFound:
                title = "404 - Not found";
                break;
            case HttpStatusCode.Forbidden:
                title = "403 - Access Denied";
                break;
            default:
                title = "500 - Application Error";
                break;
        }
        ViewBag.Title = title;
        ViewBag.Message = message;
        return View("CustomError");
    }
    

    4.Добавьте файл CustomError.cshtml в свой Общий доступ просматривает папку.Ниже приведен мой;

    <h1>@ViewBag.Title</h1>
    <br />
    <p>@ViewBag.Message</p>
    

    Теперь в вашем прикладном контроллере вы можете сделать что-то вроде этого;

    public class WidgetsController : ControllerBase
    {
      [HttpGet]
      public ActionResult Edit(int id)
      {
        Try
        {
           var widget = db.getWidgetById(id);
           if(widget == null)
              return ShowNotFound();
              //or return ShowNotFound("Invalid widget!");
           return View(widget);
        }
        catch(Exception ex)
        {
           //log error
           logger.Error(ex)
           return ShowServerError();
        }
      }
    }
    

    Теперь перейдем к предостережение.Он не будет обрабатывать статические ошибки файлов.Итак, если у вас есть маршрут, такой как example.com/widgets и пользователь меняет его на example.com/widgets.html, они получат страницу ошибок IIS по умолчанию, поэтому вам придется обрабатывать ошибки уровня IIS каким-либо другим способом.

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

    Это одновременно комментарий и вопросы к сообщению / ответу unicorn:

    https://stackoverflow.com/a/7499406/687549

    Я предпочитаю этот ответ другим из-за его простоты и того факта, что, по-видимому, были проведены консультации с некоторыми людьми в Microsoft.Однако у меня есть три вопроса, и если на них можно ответить, я назову этот ответ святым граалем из всех ответов на ошибки 404/500 в Интернете для приложения ASP.NET MVC (x).

    @Pure.Krome

    1. Можете ли вы обновить свой ответ с помощью SEO-материалов из комментариев, указанных GWB (в вашем ответе никогда не упоминалось об этом) - <customErrors mode="On" redirectMode="ResponseRewrite"> и <httpErrors errorMode="Custom" existingResponse="Replace">?

    2. Можете ли вы спросить своих ASP.NET друзей по команде, можно ли так поступать - было бы неплохо получить какое-то подтверждение - возможно, менять это категорически нельзя redirectMode и existingResponse таким образом, чтобы иметь возможность хорошо играть с SEO?!

    3. Можете ли вы добавить некоторые пояснения ко всему этому (customErrors redirectMode="ResponseRewrite", customErrors redirectMode="ResponseRedirect", httpErrors errorMode="Custom" existingResponse="Replace", УДАЛИТЬ customErrors ПОЛНОСТЬЮ, как кто-то предложил) после разговора с вашими друзьями в Microsoft?

    Как я уже говорил;было бы замечательно, если бы мы могли сделать ваш ответ более полным, поскольку это, похоже, довольно популярный вопрос с более чем 54 000 просмотров.

    Обновить:Unicorn answer выдает 302 найденных и 200 OK и не может быть изменен, чтобы возвращать только 404 с использованием маршрута.Это должен быть физический файл, который не очень похож на MVC: ish.Итак, переходим к другому решению.Очень жаль, потому что на данный момент это казалось окончательным ответом MVC: ish.

    Добавление моего решения, которое почти идентично решению Германа Кана, с небольшой складкой, чтобы оно работало для моего проекта.

    Создайте собственный контроллер ошибок:

    public class Error404Controller : BaseController
    {
        [HttpGet]
        public ActionResult PageNotFound()
        {
            Response.StatusCode = 404;
            return View("404");
        }
    }
    

    Затем создайте фабрику пользовательских контроллеров:

    public class CustomControllerFactory : DefaultControllerFactory
    {
        protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
        {
            return controllerType == null ? new Error404Controller() : base.GetControllerInstance(requestContext, controllerType);
        }
    }
    

    Наконец, добавьте переопределение в пользовательский контроллер ошибок:

    protected override void HandleUnknownAction(string actionName)
    {
        var errorRoute = new RouteData();
        errorRoute.Values.Add("controller", "Error404");
        errorRoute.Values.Add("action", "PageNotFound");
        new Error404Controller().Execute(new RequestContext(HttpContext, errorRoute));
    }
    

    И это все. Нет необходимости вносить изменения в Web.config.

    1) Сделать абстрактный класс Controller.

    public abstract class MyController:Controller
    {
        public ActionResult NotFound()
        {
            Response.StatusCode = 404;
            return View("NotFound");
        }
    
        protected override void HandleUnknownAction(string actionName)
        {
            this.ActionInvoker.InvokeAction(this.ControllerContext, "NotFound");
        }
        protected override void OnAuthorization(AuthorizationContext filterContext) { }
    }  
    

    2) Сделайте наследование от этого абстрактного класса во всех ваших контроллерах

    public class HomeController : MyController
    {}  
    

    3) И добавьте представление с именем " NotFound " в папке View-Shared.

    Я просмотрел большинство решений, опубликованных в этой теме.Хотя этот вопрос может быть старым, он все еще очень применим к новым проектам даже сейчас, поэтому я потратил довольно много времени на чтение ответов, представленных здесь, а также где-либо еще.

    Поскольку @Marco указал на различные случаи, при которых может произойти 404, я сверил решение, которое я скомпилировал вместе, с этим списком.В дополнение к его списку требований я добавил еще одно.

    • Решение должно быть способно обрабатывать вызовы MVC, а также AJAX / WebAPI наиболее подходящим образом.(т.е.если 404 происходит в MVC, он должен показывать страницу "Не найдено", а если 404 происходит в WebAPI, он не должен перехватывать ответ XML / JSON, чтобы потребляющий Javascript мог легко его разобрать).

    Это решение в 2 раза больше:

    Первая часть этого взята от @Guillaume в https://stackoverflow.com/a/27354140/2310818.Их решение заботится о любых 404, которые были вызваны из-за недопустимого маршрута, недопустимого контроллера и недопустимого действия.

    Идея состоит в том, чтобы создать веб-форму, а затем заставить ее вызывать действие NotFound вашего контроллера ошибок MVC.Он делает все это без какого-либо перенаправления, поэтому вы не увидите ни одного 302 в Fiddler.Исходный URL-адрес также сохранен, что делает это решение фантастическим!


    Вторая часть этого взята от @Germán по адресу https://stackoverflow.com/a/5536676/2310818.Их решение обрабатывает любые 404, возвращаемые вашими действиями в виде HttpNotFoundResult() или throw new HttpException() !

    Идея состоит в том, чтобы фильтр просматривал ответ, а также исключение, генерируемое вашими контроллерами MVC, и вызывал соответствующее действие в вашем контроллере ошибок.Опять же, это решение работает без какого-либо перенаправления, и исходный URL сохраняется!


    Как вы можете видеть, оба этих решения вместе предлагают очень надежный механизм обработки ошибок и удовлетворяют всем требованиям, перечисленным @Marco, а также моим требованиям.Если вы хотели бы увидеть рабочий образец или демо-версию этого решения, пожалуйста, оставьте в комментариях, и я был бы рад собрать его воедино.

    Я просмотрел все статьи, но у меня ничего не работает: По моему требованию пользователь должен указать что-нибудь на вашей странице 404 URL-адреса. Я подумал, что это очень просто. Но вы должны правильно понимать обработку 404:

     <system.web>
        <customErrors mode="On" redirectMode="ResponseRewrite">
          <error statusCode="404" redirect="~/PageNotFound.aspx"/>
        </customErrors>
      </system.web>
    <system.webServer>
        <httpErrors errorMode="Custom">
          <remove statusCode="404"/>
          <error statusCode="404" path="/PageNotFound.html" responseMode="ExecuteURL"/>
        </httpErrors>
    </system.webServer>
    

    Я нашел эту статью очень полезной. Ее следует прочитать сразу. Страница ошибок Custome Бен Фостер

    Лицензировано под: CC-BY-SA с атрибуция
    Не связан с StackOverflow
    scroll top