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

144

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

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

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

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

Мой последний метод applicationaiton_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");
    }
}
}
Джон Лурос
источник
Какие настройки должны быть в файле web.config для поддержки этого? Вы наверняка не включили бы настройки httperrors?
Philbird
forums.asp.net/p/1782402/4894514.aspx/… есть несколько полезных советов, например, IE не будет отображать страницу с ошибкой, если она меньше 512 байт
RickAndMSFT,

Ответы:

201

Вот пример того, как я обрабатываю пользовательские ошибки. Я определяю 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);
}
Дарин димитров
источник
4
Просто маленькая записка. Поскольку я хотел визуализировать View в каждом случае (404, 500 и т. Д.) Для каждого ActionResult, я вернул View. Однако я попытался обойти содержимое Application_Error и в случае сбоя возвращается статическая HTML-страница. (Я могу опубликовать код, если кто-то желает)
Джон Лурос
4
Я не могу получить бритвенные изображения для рендеринга с использованием этого решения на MVC3. Например, при возврате (модель) отображается только пустой экран.
Extrakun
2
Добавлены TrySkipIisCustomErrors, чтобы исправить это для интегрированного IIS7. См. Stackoverflow.com/questions/1706934/…
Павел Савара
1
@ajbeaven, Executeэто метод, определенный в IControllerинтерфейсе. Это не может быть защищено. Посмотрите на мой код более внимательно: IController errorsController = new ErrorsController();обратите внимание на тип errorsControllerпеременной, для которой я вызываю Executeметод. Это тип, IControllerпоэтому ничто не мешает вам вызывать этот метод. И, кстати, Executeбыл защищен и в классе Controller в MVC 3, так что никаких изменений в этом отношении нет.
Дарин Димитров
2
Исправлено путем явного указания типа содержимого ответа:Response.ContentType = "text/html";
ajbeaven
6

Вы также можете сделать это в файле 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>
Бретт Оллред
источник
3

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

Поскольку <customErrors/>в ASP.NET уже есть конфигурация (которая предназначена именно для этой цели), проще всего сказать:

    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является ли значение null, потому что исключение по- Application_Startпрежнему будет использовать этот метод, хотя активного контекста не будет.

Simon_Weaver
источник
2

Вы можете отобразить удобную страницу ошибок с правильным кодом статуса http, внедрив модуль удобной обработки исключений Джеффа Этвуда с небольшим изменением кода статуса 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
Martin_W
источник
0

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

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);
    }
}
}
MVCdragon
источник
2
Какие проблемы у вас были с решением Дарина?
Кенни Эвитт
Вы не описали проблему, с которой столкнулись, и вызвали конкурирующий ответ.
Иванжонас