Обработка ошибок в ASP.NET MVC - PullRequest
64 голосов
/ 01 мая 2009

Как правильно обрабатывать исключения, выданные из контроллеров в ASP.NET MVC? Атрибут HandleError, по-видимому, обрабатывает только исключения, создаваемые инфраструктурой MVC, а не исключения, создаваемые моим собственным кодом.

Использование этого web.config

<customErrors mode="On">
    <error statusCode="401" redirect="/Errors/Http401" />
</customErrors>

со следующим кодом

namespace MvcApplication1.Controllers
{
    [HandleError]
    public class HomeController : Controller
    {
        public ActionResult Index()
        {
            // Force a 401 exception for testing
            throw new HttpException(401, "Unauthorized");
        }
    }
}

не приводит к тому, на что я надеялся. Вместо этого я получаю общую страницу об ошибках ASP.NET, в которой говорится, что нужно изменить мой web.config, чтобы увидеть фактическую информацию об ошибке. Однако, если вместо того, чтобы вызвать исключение, я возвращаю недопустимое представление, я получаю страницу /Shared/Views/Error.aspx:

return View("DoesNotExist");

Создание исключений в контроллере, как я делал выше, похоже, обходит все функции HandleError, так как правильно создавать страницы ошибок и как мне хорошо играть с инфраструктурой MVC?

Ответы [ 8 ]

63 голосов
/ 01 мая 2009

Controller.OnException(ExceptionContext context). Переопределите это.

protected override void OnException(ExceptionContext filterContext)
{
    // Bail if we can't do anything; app will crash.
    if (filterContext == null)
        return;
        // since we're handling this, log to elmah

    var ex = filterContext.Exception ?? new Exception("No further information exists.");
    LogException(ex);

    filterContext.ExceptionHandled = true;
    var data = new ErrorPresentation
        {
            ErrorMessage = HttpUtility.HtmlEncode(ex.Message),
            TheException = ex,
            ShowMessage = !(filterContext.Exception == null),
            ShowLink = false
        };
    filterContext.Result = View("ErrorPage", data);
}
21 голосов
/ 01 мая 2009

Благодаря kazimanzurrashaid, вот что я закончил делать в Global.asax.cs:

protected void Application_Error()
{
    Exception unhandledException = Server.GetLastError();
    HttpException httpException = unhandledException as HttpException;
    if (httpException == null)
    {
        Exception innerException = unhandledException.InnerException;
        httpException = innerException as HttpException;
    }

    if (httpException != null)
    {
        int httpCode = httpException.GetHttpCode();
        switch (httpCode)
        {
            case (int) HttpStatusCode.Unauthorized:
                Response.Redirect("/Http/Error401");
                break;
        }
    }
}

Я смогу добавить больше страниц в HttpContoller на основе любых дополнительных кодов ошибок HTTP, которые мне нужно поддерживать.

13 голосов
/ 01 мая 2009

Кажется, что атрибут HandleError обрабатывает только исключения, создаваемые инфраструктурой MVC, а не исключения, создаваемые моим собственным кодом.

Это просто неправильно. Действительно, HandleError будет «обрабатывать» только исключения, добавленные в ваш собственный код или в код, вызываемый вашим собственным кодом. Другими словами, только исключения, когда ваше действие находится в стеке вызовов.

Реальным объяснением поведения, которое вы видите, является конкретное исключение, которое вы выбрасываете. HandleError ведет себя по-разному с HttpException. Из исходного кода:

        // If this is not an HTTP 500 (for example, if somebody throws an HTTP 404 from an action method),
        // ignore it.
        if (new HttpException(null, exception).GetHttpCode() != 500) {
            return;
        }
6 голосов
/ 01 мая 2009

Я не думаю, что вы сможете показать конкретную ErrorPage на основе HttpCode с атрибутом HandleError, и я бы предпочел использовать HttpModule для этой цели. Предполагая, что у меня есть папка «ErrorPages», где существует отдельная страница для каждой конкретной ошибки, и сопоставление указывается в файле web.config так же, как в обычном приложении веб-формы. Ниже приведен код, который используется для отображения страницы с ошибкой:

public class ErrorHandler : BaseHttpModule{

public override void OnError(HttpContextBase context)
{
    Exception e = context.Server.GetLastError().GetBaseException();
    HttpException httpException = e as HttpException;
    int statusCode = (int) HttpStatusCode.InternalServerError;

    // Skip Page Not Found and Service not unavailable from logging
    if (httpException != null)
    {
        statusCode = httpException.GetHttpCode();

        if ((statusCode != (int) HttpStatusCode.NotFound) && (statusCode != (int) HttpStatusCode.ServiceUnavailable))
        {
            Log.Exception(e);
        }
    }

    string redirectUrl = null;

    if (context.IsCustomErrorEnabled)
    {
        CustomErrorsSection section = IoC.Resolve<IConfigurationManager>().GetSection<CustomErrorsSection>("system.web/customErrors");

        if (section != null)
        {
            redirectUrl = section.DefaultRedirect;

            if (httpException != null)
            {
                if (section.Errors.Count > 0)
                {
                    CustomError item = section.Errors[statusCode.ToString(Constants.CurrentCulture)];

                    if (item != null)
                    {
                        redirectUrl = item.Redirect;
                    }
                }
            }
        }
    }

    context.Response.Clear();
    context.Response.StatusCode = statusCode;
    context.Response.TrySkipIisCustomErrors = true;

    context.ClearError();

    if (!string.IsNullOrEmpty(redirectUrl))
    {
        context.Server.Transfer(redirectUrl);
    }
}

}

3 голосов
/ 20 мая 2009

Еще одна возможность (не соответствует действительности в вашем случае), с которой могут столкнуться другие, читающие это, заключается в том, что ваша страница с ошибкой выдает саму ошибку или не реализует:

 System.Web.Mvc.ViewPage<System.Web.Mvc.HandleErrorInfo>

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

Эта модель является моделью, отправленной на страницу ошибки. Если ваша страница ошибок использует ту же главную страницу, что и остальная часть вашего сайта, и требует какой-либо другой информации о модели, вам нужно либо создать собственный тип атрибута [HandleError], либо переопределить OnException, либо что-то еще.

2 голосов
/ 10 октября 2011
     protected override void OnException (ExceptionContext filterContext )
    {
        if (filterContext != null && filterContext.Exception != null)
        {
            filterContext.ExceptionHandled = true;
            this.View("Error").ViewData["Exception"] = filterContext.Exception.Message;
            this.View("Error").ExecuteResult(this.ControllerContext);
        }
    }
2 голосов
/ 10 августа 2010

Я выбрал подход Controller.OnException (), который для меня является логичным выбором - поскольку я выбрал ASP.NET MVC, я предпочитаю оставаться на уровне инфраструктуры и избегать путаницы с базовой механикой, если возможно.

Я столкнулся со следующей проблемой:

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

Я исправил это, очистив ответ перед установкой filterContext.Result - вот так:

        filterContext.HttpContext.Response.Clear(); // gets rid of any garbage
        filterContext.Result = View("ErrorPage", data);

Надеюсь, это сэкономит кому-то еще время: -)

1 голос
/ 23 апреля 2012

Джефф Этвуд Удобный для пользователя модуль обработки исключений отлично работает для 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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...