如何从属性中抛出控制器中的错误?

时间:2012-03-10 21:46:12

标签: asp.net-mvc-3 error-handling custom-attributes

如果请求无效,我有一个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 个答案:

答案 0 :(得分:1)

好的,所以简短的回答( tl; dr )就是你需要以干净有序的方式处理所有异常。这并不是每个控制器动作都单独进行的,因为我(作为假人)最初计划。

相反,我已经连接了我的全局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);
    }
}

现在为我的应用添加一点健壮性,我创建了与Http StatusCodes对应的自定义Exceptions

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");