Получение сообщения из исключения, выданного действием контроллера ASP.NET MVC, вызванным из jQuery ajax - PullRequest
4 голосов
/ 05 ноября 2010

Я пытаюсь защитить действие контроллера, которое вызывается с помощью функции jQuery ajax.

В моем методе действия код окружен блоком try-catch, поэтому я фиксирую все свои исключения и возвращаю результат JSON с сообщением об исключении. К этому моменту все прекрасно работает.

Проблема начинается, когда я выкидываю исключение вне блока try-catch, например, если исключение возникает в атрибуте действия фильтра. В этом случае я не могу вернуть результат JSON, потому что поток резко останавливается.

jQuery перехватывает это исключение в функции обратного вызова с ошибкой. Но единственное место, где мне удалось увидеть сообщение об исключении - это xhr.responseText, но оно содержит целую ошибку «желтой страницы смерти» от asp.net.

Очень уродливое и хакерское решение, которое я использовал для получения сообщения об исключении, заключается в извлечении текста между тегом . Но я действительно надеюсь, что есть лучший способ сделать это !!

Что бы вы сделали в этом сценарии? Как бы вы защитили свои действия Ajax без написания этой логики внутри метода действия? Как бы вы показали пользователю сообщение от необработанного исключения, выданного действием контроллера ASP.NET MVC, вызванного из jQuery ajax?

jQuery Ajax Call:

$.ajax({
  type: "POST",
  contentType: "application/json; charset=utf-8",
  dataType: "json",
  url: url + "?id=" + id,
  success: function(data) {
                if(data.success){
                    alert('Success');
                } else {
                    alert('Fail: ' + data.message);
                },
  error: function(xhr, status, err) {
    // There has to be a better way to do this!!
    var title = xhr.responseText.split("<title>")[1].split("</title>")[0];
    alert(title);
  }
});

Действие контроллера:

[MyAttribute]
public ActionResult MyAction(int id)
{
    try
    {
        // Do something

        return Json(new { success = true }, JsonRequestBehavior.AllowGet);
    }
    catch (Exception exception)
    {
        return Json(new { success = false, message = exception.Message }, JsonRequestBehavior.AllowGet);
    }
}

Атрибут фильтра действий:

public override void OnActionExecuting(ActionExecutingContext filterContext)
{
    // This causes a 401 Unauthorized HTTP error.
    throw new UnauthorizedAccessException("Access Denied.");
}

Ответы [ 2 ]

4 голосов
/ 05 ноября 2010

Вы можете использовать метод Application_Error в Global.asax:

protected void Application_Error(object sender, EventArgs e)
{
    HttpApplication app = (HttpApplication)sender;
    HttpContext context = app.Context;
    Exception ex = context.Server.GetLastError();
    bool isAjaxCall = string.Equals(
        "XMLHttpRequest", 
        context.Request.Headers["x-requested-with"], 
        StringComparison.OrdinalIgnoreCase
    );
    if (isAjaxCall)
    {
        context.Response.StatusCode = 200;
        context.Response.ContentType = "application/json";
        var json = new JavaScriptSerializer().Serialize(new { error = ex.Message });
        context.Response.Write(json);
    }
    else
    {
        // TODO: Handle the case of non async calls
    }
}
1 голос
/ 05 ноября 2010

Возможно, вы сможете реализовать собственный класс из атрибута HandleError и заставить его возвращать Json при любом исключении, я собираюсь проверить код MVC и отредактировать этот ответ позже.

* Редактировать * Проверьте этот класс.

public class ErrorHandlingJSon : HandleErrorAttribute
{
    public override void OnException(ExceptionContext filterContext)
    {
        /*****  Original code from MVC source  ******/

        if (filterContext == null)
        {
            throw new ArgumentNullException("filterContext");
        }
        if (filterContext.IsChildAction)
        {
            return;
        }

        // If custom errors are disabled, we need to let the normal ASP.NET exception handler
        // execute so that the user can see useful debugging information.
        if (filterContext.ExceptionHandled || !filterContext.HttpContext.IsCustomErrorEnabled)
        {
            return;
        }

        Exception exception = filterContext.Exception;

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

        if (!ExceptionType.IsInstanceOfType(exception))
        {
            return;
        }

        //***** This is the new code  *****//
        if (filterContext.HttpContext.Request.IsAjaxRequest()) // If it's a ajax request
        {
            filterContext.Result = new JsonResult // Set the response to JSon
            {
                Data = new { success = false, message = exception.Message }
            };

            filterContext.ExceptionHandled = true;
            filterContext.HttpContext.Response.Clear();
            filterContext.HttpContext.Response.StatusCode = 200;  // Maybe it should be 500, but this way you handle the JQuery on the success event
            filterContext.HttpContext.Response.TrySkipIisCustomErrors = true;
        }
        else //*** From here, is the original code againg **//
        {
            string controllerName = (string)filterContext.RouteData.Values["controller"];
            string actionName = (string)filterContext.RouteData.Values["action"];
            HandleErrorInfo model = new HandleErrorInfo(filterContext.Exception, controllerName, actionName);
            filterContext.Result = new ViewResult
            {
                ViewName = View,
                MasterName = Master,
                ViewData = new ViewDataDictionary<HandleErrorInfo>(model),
                TempData = filterContext.Controller.TempData
            };
            filterContext.ExceptionHandled = true;
            filterContext.HttpContext.Response.Clear();
            filterContext.HttpContext.Response.StatusCode = 500;

            // Certain versions of IIS will sometimes use their own error page when
            // they detect a server error. Setting this property indicates that we
            // want it to try to render ASP.NET MVC's error page instead.
            filterContext.HttpContext.Response.TrySkipIisCustomErrors = true;
        }
    }

Я использовал тот же код из источника MVC, но добавил другой ответ, когда запрос Ajax, поэтому он возвращает результат JSon.Я установил код состояния на 200, так что вы обрабатываете исключение для опции успеха JQuery.Если вы возвращаете 500, то вам следует обработать исключение для опции ошибки, возможно, это лучший способ сделать это.

Для этого просто используйте [ErrorHandlingJSon] в верхней части вашего контроллера.Вы должны установить пользовательскую ошибку веб-конфигурации на.В атрибуте есть строка, где он проверяет, включены ли customErrors или нет, вы можете вернуть сюда Json, чтобы он работал, когда customErrors выключен.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...