Как отловить ошибку в контроллере при выбросе из атрибута? - PullRequest
1 голос
/ 11 марта 2012

У меня есть ActionFilterAttribute, который выдает ошибку, если запрос недействителен.

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class AjaxOnlyAttribute : ActionFilterAttribute
{
    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        if((!filterContext.HttpContext.Request.IsAjaxRequest() ||
            !filterContext.HttpContext.Request.IsXMLHttpRequest()) &&
           (!filterContext.HttpContext.Request.IsLocal))
        {
            throw new InvalidOperationException("This operation can only be accessed via Ajax requests.");
        }
    }
}

Теперь в моем контроллере я бы хотел отловить ошибку и передать ее в представление

[AjaxOnly]
public JsonpResult List()
{
    try
    {
    var vimeoService = new VimeoService();
    var videos = vimeoService.GetVideosFromChannel(this._vimeoChannelId);

    return this.Jsonp(videos);
    }
    catch (Exception ex)
    {
        var err = new ErrorsModel() { ErrorMessage = ex.Message };
        return this.Jsonp(err, false);
    }
}

Теперь, поскольку атрибут запускается до запуска действия контроллера, я никогда не могу "перехватить" ошибку в контроллере и, следовательно, не могу передать ошибку в представление.

Как мне этого добиться?

1 Ответ

1 голос
/ 12 марта 2012

Хорошо, поэтому краткий ответ ( tl; dr ) заключается в том, что вам нужно обрабатывать все исключения в чистом и организованном порядке.И это НЕ для того, чтобы делать это индивидуально в каждом действии контроллера, как я (как фиктивный) изначально планировал.

Вместо этого я подключил свой метод Global Application_Error для обработки всех ошибок и пробивал ихк контроллеру ошибок с одним index действием.

    private void Application_Error()
    {
        // setup the route to Send the error to
        var routeData = new RouteData();
        routeData.Values.Add("action", "Index");

        // Execute the ErrorController instead of the intended controller
        IController errorController = new Controllers.ErrorController();
        errorController.Execute(new RequestContext(new HttpContextWrapper(this.Context), routeData));

        // After the controller executes, clear the error.
        this.Server.ClearError();
    }

Мой контроллер ошибок довольно прост.По сути, он принимает последнюю ошибку и передает соответствующие данные клиенту (в моем случае сериализует их как JsonP).

public class ErrorController : Controller
{
    public JsonpResult Index()
    {
        var lastError = Server.GetLastError();
        var message = lastError.Message;
        int statusCode;

        // If the lastError is a System.Exception, then we
        // need to manually set the Http StatusCode.
        if (lastError.GetType() == typeof(System.Exception))
        {
            statusCode = 500;
        }
        else
        {
            var httpException = (HttpException)this.Server.GetLastError();
            statusCode = httpException.GetHttpCode();
        }

        // Set the status code header.
        this.Response.StatusCode = statusCode;

        // create a new ErrorsModel that can be past to the client
        var err = new ErrorsModel()
            {
                ErrorMessage = message, 
                StatusCode = statusCode
            };
        return this.Jsonp(err, false);
    }
}

Теперь, чтобы добавить немного надежности в мое приложение, я создал пользовательскийExceptions, которые соответствуют Http StatusCodes.

public sealed class UnauthorizedException : HttpException
{
    /// <summary>
    /// Similar to 403 Forbidden, but specifically for use when authentication is possible but has failed or not yet been provided
    /// </summary>
    public UnauthorizedException(string message) : base((int)StatusCode.Unauthorized, message) { }
}

Теперь я могу генерировать исключения во всем приложении, где это применимо, и все они будут обрабатываться и отправляться клиенту простым в управлении способом.

Вот пример выброса одной из упомянутых ошибок.

if (!User.Identity.IsAuthenticated)
    throw new UnauthorizedException("You are not authorized");
...