MVC 2.0 - Пользовательская обработка всех ошибок для возврата json

У меня есть приложение MVC 2, и я хочу, чтобы все запросы возвращали json. Я переопределил HandleErrorAttribute и AuthorizeAttribute. Моя цель состоит в том, чтобы все ошибки (даже 403 и 404) возвращались в виде json.

Вот мой обработчик ошибок. ExceptionModel — это простой класс, определяющий любую ошибку, возвращаемую моим приложением. Обработчик исключений — это класс, который переводит сведения об ошибке в форматированное сообщение электронной почты и отправляет его мне.

public class HandleErrorJsonAttribute : System.Web.Mvc.HandleErrorAttribute
{
    public override void OnException(ExceptionContext context)
    {
        context.ExceptionHandled = true;

        RaiseErrorSignal(context.Exception);

        context.RequestContext.HttpContext.Response.ContentType = "application/json";

        JsonSerializer serializer = new JsonSerializer();
        serializer.Serialize(context.HttpContext.Response.Output, new ExceptionModel(context.Exception));
    }

    private static void RaiseErrorSignal(Exception ex)
    {
        IExceptionHandler handler = Resolve();

        handler.HandleError(ex.GetBaseException());
    }

    private static IExceptionHandler Resolve()
    {
        return ServiceLocator.Locate<IExceptionHandler>();
    }
}

Вот модель исключения для пояснения

public class ExceptionModel
{
    public int ErrorCode { get; set; }
    public string Message { get; set; }

    public ExceptionModel() : this(null)
    {

    }

    public ExceptionModel(Exception exception)
    {
        ErrorCode = 500;
        Message = "An unknown error ocurred";

        if (exception != null)
        {
            if (exception is HttpException)
                ErrorCode = ((HttpException)exception).GetHttpCode();

            Message = exception.Message;
        }
    }

    public ExceptionModel(int errorCode, string message)
    {
        ErrorCode = errorCode;
        Message = message;
    }
}

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

public class AuthorizeTokenAttribute : System.Web.Mvc.AuthorizeAttribute
{
    public bool SuperAdminOnly { get; set; }

    protected override bool AuthorizeCore(HttpContextBase httpContext)
    {
        bool authorized = base.AuthorizeCore(httpContext);

        if(!SuperAdminOnly)
            return authorized;

        if(!authorized)
            return authorized;

        return SessionHelper.UserIsSuperAdmin(httpContext.User.Identity.Name);
    }

    protected override void HandleUnauthorizedRequest(System.Web.Mvc.AuthorizationContext filterContext)
    {
        throw new HttpException(403, "Access Denied");
    }
}

Все это отлично работает для большинства ошибок, но не хватает одной вещи. У меня есть такое действие контроллера.

[AuthorizeToken]
[HttpPost]
public JsonResult MyAction()
{
    return new JsonResult();
}

Он отлично работает, когда вы отправляете по почте, но при получении я получаю необработанную ошибку 404.

Ошибка сервера в приложении '/'

Ресурс не может быть найден.

Описание: HTTP 404. Ресурс, который вы ищете (или одна из его зависимостей), мог быть удален, его имя было изменено или он временно недоступен. Просмотрите следующий URL-адрес и убедитесь, что он написан правильно.

Запрошенный URL-адрес: /MyController/MyAction

Информация о версии: Версия Microsoft .NET Framework: 4.0.30319; Версия ASP.NET: 4.0.30319.1

Это происходит при GET, что следует ожидать как поведение по умолчанию. Однако, как я могу справиться с этим условием, чтобы вместо этого я мог вернуть json, подобный этому

{"ErrorCode":404,"Message":"Page Not Found"}

person Josh    schedule 21.02.2011    source источник


Ответы (1)


Для обработки ошибок лично я предпочитаю событие Application_Error в Global.asax:

protected void Application_Error(object sender, EventArgs e)
{
    var exception = Server.GetLastError();
    Response.Clear();
    Server.ClearError();

    var httpException = exception as HttpException;
    var routeData = new RouteData();
    routeData.Values["controller"] = "Errors";
    routeData.Values["action"] = "Index";
    routeData.Values["error"] = exception;

    IController errorController = new ErrorsController();
    errorController.Execute(new RequestContext(new HttpContextWrapper(Context), routeData));
}

а затем иметь ErrorsController:

public class ErrorsController : Controller
{
    public ActionResult Index(Exception exception)
    {
        var errorCode = 500;
        var httpException = exception as HttpException;
        if (httpException != null)
        {
            errorCode = httpException.ErrorCode;
        }

        return Json(new
        {
            ErrorCode = errorCode,
            Message = exception.Message
        }, JsonRequestBehavior.AllowGet);
    }
}
person Darin Dimitrov    schedule 21.02.2011