Двухэтапная обработка ошибок MVC 3 с использованием пользовательского контроллера ошибок и атрибута HandleErrorAttribute

Я использую комбинацию HandleErrorAttribute и Custom Error Controller в разделе Custom Errors для обработки ошибок в приложении MVC3. Логика заключается в том, чтобы обрабатывать любые ошибки запроса Ajax через обработчик OnException в атрибуте HandleErrorAttribute, а остальные ошибки — через ErrorController. Ниже приведен код -

// Handle any ajax error via HandleErrorAttribute 
public class HandleAjaxErrorAttribute : System.Web.Mvc.HandleErrorAttribute
 {
    public override void OnException(System.Web.Mvc.ExceptionContext filterContext)
    {



        filterContext.HttpContext.Response.StatusCode = (int)System.Net.HttpStatusCode.InternalServerError;
        var exception = filterContext.Exception;

        if (filterContext.HttpContext.Request.IsAjaxRequest())
        {
            //some logic
            filterContext.ExceptionHandled = true;
        }

    }
}

//Handle remaining errors in the Error Controller

public class ErrorController : Controller
{

    protected override void HandleUnknownAction(string actionName)
    {
        var exception = Server.GetLastError(); //Can't get the exception object here.
        //some logic
    }



}

Настройки Web.config:

<customErrors mode="On" defaultRedirect="~/Error">
</customErrors>

При возникновении любого исключения, отличного от ajax, управление передается от блока OnException к HandleUnknownAction в контроллере ошибок. Однако я не могу получить объект Exception. Как я могу получить объект Exception в контроллере ошибок?

Кроме того, считаете ли вы, что этот двухэтапный подход является правильным способом обработки ошибок в MVC3? Я думал об обработке ошибок в централизованном месте с помощью обработчика событий Application_Error, но, согласно моим исследованиям, это не рекомендуемый подход для приложений MVC.


person devanalyst    schedule 13.06.2012    source источник
comment
Почему бы просто не исправить другие ошибки так же, как вы это делаете с Ajax? Я имею в виду, что если Ajax что-то делает, а если нет, вызовите представление и передайте ему объект исключения (это то, что я делаю в своем проекте MVC). Вы должны понимать, что CustomErrors просто перенаправляет на заданный URL-адрес и отправляет ему URL-адрес, вызвавший ошибку. Однако он не хранит никакой информации, это просто перенаправление URL.   -  person oamsel    schedule 13.06.2012
comment
HandleErrorAttribute не может обрабатывать ошибки, которые находятся за пределами конвейера MVC (404, 403 и т. д.), поэтому мне все еще нужны CustomErrors, и мне нужно вызывать их через действие, а не напрямую вызывать представление. Также я не хочу жестко кодировать имя представления в обработчике OnException. Кроме того, лучше иметь всю обработку ошибок в одном месте.   -  person devanalyst    schedule 13.06.2012
comment
@OmerAmsel, было бы полезно, если бы вы опубликовали код.   -  person devanalyst    schedule 13.06.2012


Ответы (3)


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

Этот код находится внутри BaseController (настоятельно рекомендуется сделать его, см. эту статью: http://weblogs.asp.net/rashid/archive/2009/04/01/asp-net-mvc-best-practices-part-1.aspx< /а>)

и я переопределил метод OnException (моя главная цель здесь состояла в том, чтобы различать вызовы Ajax и обычные вызовы):

protected override void OnException(ExceptionContext filterContext)
    {
        // Set to true or else it'll still propagate
        filterContext.ExceptionHandled = true;

        string message;



        // Check if it's an Ajax request
        if (filterContext.HttpContext.Request.IsAjaxRequest())
        {
            // Here I check if the custom error is on, if it isn't I'll paste the
            // stacktrace and the error
            if (!filterContext.HttpContext.IsCustomErrorEnabled)
            {
                message = filterContext.Exception.Message + filterContext.Exception.StackTrace;
            }
            else
            {
                message = "Something went wrong";
            }

            // TODO: Decide what to do if ajax
            filterContext.HttpContext.Response.StatusCode = 401;
            var json = new JsonResult {Data = message};
            json.ExecuteResult(this.ControllerContext);
        }
        else
        {
            // This is basically the same conditional, but instead of sending a JSON
            // I'll redirect to a view and give it the exception
            if (!filterContext.HttpContext.IsCustomErrorEnabled)
            {
                ViewData.Model = filterContext.Exception;
                this.View("Error").ExecuteResult(this.ControllerContext);
            }
            else
            {
               this.View("../Error/ServerError").ExecuteResult(this.ControllerContext);
            }

        }
    }
}

Важно отметить, что мой комментарий выше заключается в том, что я не говорю не использовать CustomError, а думать об этом только тогда, когда возникают ошибки, которые действительно находятся за пределами конвейера (например, 401).

person oamsel    schedule 13.06.2012

Я могу ошибаться, но я считаю, что вам не нужно беспокоиться об информации об ошибке в вашем контроллере, потому что MVC автоматически подключает ее к представлению. Итак, если вы сделаете свой (Razor) вид таким, он должен работать:

@model System.Web.Mvc.HandleErrorInfo

Любое ведение журнала может быть выполнено в вашем атрибуте, но если вам нужно сделать это в контроллере ошибок, создайте действие LogError в вашем контроллере ошибок, которое принимает аргумент HandleErrorInfo, и назовите его примерно так:

@Html.RenderAction("LogError", Модель)

PS Я не проверял это, но это должно работать.

person Rob Kent    schedule 13.06.2012

Мое сообщение об ошибке не отображалось, и ErrorController не срабатывал, пока я не выполнил следующую настройку.

<customErrors mode="On" defaultRedirect="~/Error">
</customErrors>
person user2766623    schedule 13.09.2013