Пользовательские страницы ошибок на asp.net MVC3

Я разрабатываю базовый сайт MVC3, и я ищу решение для обработки ошибок и создания пользовательских представлений для каждого типа ошибок. Итак, представьте, что у меня есть контроллер "Error", где его основное действие - "Index" (общая страница ошибок), и у этого контроллера будет еще несколько действий для ошибок, которые могут появиться пользователю, например "Handle500" или "HandleActionNotFound".

Таким образом, каждая ошибка, которая может произойти на веб-сайте, может обрабатываться этим контроллером "Ошибка" (примеры: "Контроллер" или "Действие" не найдены, 500, 404, исключение dbException и т.д.).

Я использую файл Sitemap для определения пути к веб-сайту (а не маршрута).

Этот вопрос уже был дан ответ, это ответ Gweebz

Мой последний метод applicaiton_error следующий:

protected void Application_Error() {
//while my project is running in debug mode
if (HttpContext.Current.IsDebuggingEnabled && WebConfigurationManager.AppSettings["EnableCustomErrorPage"].Equals("false"))
{
    Log.Logger.Error("unhandled exception: ", Server.GetLastError());
}
else
{
    try
    {
        var exception = Server.GetLastError();

        Log.Logger.Error("unhandled exception: ", exception);

        Response.Clear();
        Server.ClearError();
        var routeData = new RouteData();
        routeData.Values["controller"] = "Errors";
        routeData.Values["action"] = "General";
        routeData.Values["exception"] = exception;

        IController errorsController = new ErrorsController();
        var rc = new RequestContext(new HttpContextWrapper(Context), routeData);
        errorsController.Execute(rc);
    }
    catch (Exception e)
    {
        //if Error controller failed for same reason, we will display static HTML error page
        Log.Logger.Fatal("failed to display error page, fallback to HTML error: ", e);
        Response.TransmitFile("~/error.html");
    }
}
}

Ответ 1

Вот пример того, как я обрабатываю пользовательские ошибки. Я определяю ErrorsController с действиями, обрабатывающими разные ошибки HTTP:

public class ErrorsController : Controller
{
    public ActionResult General(Exception exception)
    {
        return Content("General failure", "text/plain");
    }

    public ActionResult Http404()
    {
        return Content("Not found", "text/plain");
    }

    public ActionResult Http403()
    {
        return Content("Forbidden", "text/plain");
    }
}

а затем я подписываюсь на Application_Error в Global.asax и вызываю этот контроллер:

protected void Application_Error()
{
    var exception = Server.GetLastError();
    var httpException = exception as HttpException;
    Response.Clear();
    Server.ClearError();
    var routeData = new RouteData();
    routeData.Values["controller"] = "Errors";
    routeData.Values["action"] = "General";
    routeData.Values["exception"] = exception;
    Response.StatusCode = 500;
    if (httpException != null)
    {
        Response.StatusCode = httpException.GetHttpCode();
        switch (Response.StatusCode)
        {
            case 403:
                routeData.Values["action"] = "Http403";
                break;
            case 404:
                routeData.Values["action"] = "Http404";
                break;
        }
    }

    IController errorsController = new ErrorsController();
    var rc = new RequestContext(new HttpContextWrapper(Context), routeData);
    errorsController.Execute(rc);
}

Ответ 3

Вы также можете сделать это в файле Web.Config. Вот пример, который работает в IIS 7.5.

     <system.webServer>
          <httpErrors errorMode="DetailedLocalOnly" defaultResponseMode="File">
                <remove statusCode="502" subStatusCode="-1" />
                <remove statusCode="501" subStatusCode="-1" />
                <remove statusCode="412" subStatusCode="-1" />
                <remove statusCode="406" subStatusCode="-1" />
                <remove statusCode="405" subStatusCode="-1" />
                <remove statusCode="404" subStatusCode="-1" />
                <remove statusCode="403" subStatusCode="-1" />
                <remove statusCode="401" subStatusCode="-1" />
                <remove statusCode="500" subStatusCode="-1" />
                <error statusCode="500" path="/notfound.html" responseMode="ExecuteURL" />
                <error statusCode="401" prefixLanguageFilePath="" path="/500.html" responseMode="ExecuteURL" />
                <error statusCode="403" prefixLanguageFilePath="" path="/403.html" responseMode="ExecuteURL" />
                <error statusCode="404" prefixLanguageFilePath="" path="/404.html" responseMode="ExecuteURL" />
                <error statusCode="405" prefixLanguageFilePath="" path="/405.html" responseMode="ExecuteURL" />
                <error statusCode="406" prefixLanguageFilePath="" path="/406.html" responseMode="ExecuteURL" />
                <error statusCode="412" prefixLanguageFilePath="" path="/412.html" responseMode="ExecuteURL" />
                <error statusCode="501" prefixLanguageFilePath="" path="/501.html" responseMode="ExecuteURL" />
                <error statusCode="502" prefixLanguageFilePath="" path="/genericerror.html" responseMode="ExecuteURL" />
           </httpErrors>
</system.webServer>

Ответ 4

Я вижу, что вы добавили значение конфигурации для EnableCustomErrorPage, и вы также проверяете IsDebuggingEnabled, чтобы определить, следует ли запускать обработку ошибок.

Поскольку в ASP.NET уже существует <customErrors/>, то это проще всего сказать:

    protected void Application_Error()
    {
        if (HttpContext.Current == null) 
        {
                // errors in Application_Start will end up here                
        }
        else if (HttpContext.Current.IsCustomErrorEnabled)
        {
                // custom exception handling
        }
    }

Затем в конфиге вы положили бы <customErrors mode="RemoteOnly" />, который можно безопасно развернуть, и когда вам нужно проверить свою страницу с ошибкой, вы должны установить ее на <customErrors mode="On" />, чтобы вы могли убедиться, что она работает.

Обратите внимание, что вам также нужно проверить, является ли HttpContext.Current нулевым, поскольку исключение в Application_Start будет по-прежнему использоваться для этого метода, хотя активного контекста не будет.

Ответ 5

Вы можете отобразить удобную страницу с ошибкой с правильным кодом состояния http, внедряя Jeff Atwood "Пользовательский модуль обработки исключений" с небольшой модификацией для кода статуса http. Он работает без каких-либо переадресаций. Хотя код с 2004 года (!), Он хорошо работает с MVC. Он может быть полностью сконфигурирован в вашем web.config, без каких-либо изменений исходного кода проекта MVC.

Модификация, необходимая для возврата исходного статуса HTTP, а не статуса 200, описана в этой связанной теме форума.

В принципе, в Handler.vb вы можете добавить что-то вроде:

' In the header...
Private _exHttpEx As HttpException = Nothing

' At the top of Public Sub HandleException(ByVal ex As Exception)...
HttpContext.Current.Response.StatusCode = 500
If TypeOf ex Is HttpException Then
    _exHttpEx = CType(ex, HttpException)
    HttpContext.Current.Response.StatusCode = _exHttpEx.GetHttpCode()
End If

Ответ 6

Я использую MVC 4.5, и у меня возникли проблемы с решением Darin. Примечание. Решение Darin превосходно, и я использовал его для решения моего решения. Здесь мое модифицированное решение:

protected void Application_Error(object sender, EventArgs e)
{           
var exception = Server.GetLastError();
var httpException = exception as HttpException;
Response.StatusCode = httpException.GetHttpCode();

Response.Clear();
Server.ClearError();


if (httpException != null)
{
    var httpContext = HttpContext.Current;

    httpContext.RewritePath("/Errors/InternalError", false);

    // MVC 3 running on IIS 7+
    if (HttpRuntime.UsingIntegratedPipeline)
    {
        switch (Response.StatusCode)
        {
            case 403:
                httpContext.Server.TransferRequest("/Errors/Http403", true);
                break;
            case 404:
                httpContext.Server.TransferRequest("/Errors/Http404", true);
                break;
            default:
                httpContext.Server.TransferRequest("/Errors/InternalError", true);
                break;
        }
    }
    else
    {
        switch (Response.StatusCode)
        {
            case 403:
                httpContext.RewritePath(string.Format("/Errors/Http403", true));
                break;
            case 404:
                httpContext.RewritePath(string.Format("/Errors/Http404", true));
                break;
            default:
                httpContext.RewritePath(string.Format("/Errors/InternalError", true));
                break;
        }

        IHttpHandler httpHandler = new MvcHttpHandler();
        httpHandler.ProcessRequest(httpContext);
    }
}
}