MVC 2.0 - Пользовательская обработка всех ошибок, чтобы вернуть json - PullRequest
1 голос
/ 21 февраля 2011

У меня есть приложение 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"}

1 Ответ

2 голосов
/ 21 февраля 2011

Для обработки ошибок лично я предпочитаю событие 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);
    }
}
...