Подтвердить что ты не робот

Обработка ошибок в ASP.NET MVC

Как я могу правильно обрабатывать исключения, выведенные из контроллеров в 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?

4b9b3361

Ответ 1

Благодаря 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, которые мне нужно поддерживать.

Ответ 2

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);
}

Ответ 3

Атрибут 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;
        }

Ответ 4

Я не думаю, что вы сможете показать конкретную ошибку на основе 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);
    }
}

}

Ответ 5

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

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

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

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

Ответ 6

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

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

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

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

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

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

Ответ 7

     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);
        }
    }

Ответ 8

Jeff Atwood Пользовательский модуль обработки исключений отлично подходит для 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