C# MVC3和WebApi错误处理

C# MVC3和WebApi错误处理,c#,asp.net-mvc,asp.net-mvc-3,asp.net-web-api,C#,Asp.net Mvc,Asp.net Mvc 3,Asp.net Web Api,我正在编辑一个使用MVC3的旧项目。 它有一个Global.asax文件,可处理如下错误: protected void Application_Error(object sender, EventArgs e) { var currentController = " "; var currentAction = " "; var currentRouteData = RouteTable.Routes.GetRouteData(new HttpContextWrappe

我正在编辑一个使用MVC3的旧项目。 它有一个Global.asax文件,可处理如下错误:

protected void Application_Error(object sender, EventArgs e)
{
    var currentController = " ";
    var currentAction = " ";
    var currentRouteData = RouteTable.Routes.GetRouteData(new HttpContextWrapper(Context));

    if (currentRouteData != null)
    {
        if (currentRouteData.Values["controller"] != null && !String.IsNullOrEmpty(currentRouteData.Values["controller"].ToString()))
            currentController = currentRouteData.Values["controller"].ToString();

        if (currentRouteData.Values["action"] != null && !String.IsNullOrEmpty(currentRouteData.Values["action"].ToString()))
            currentAction = currentRouteData.Values["action"].ToString();
    }

    var ex = Server.GetLastError();
    var controller = new ErrorController();
    var routeData = new RouteData();
    var action = "Index";

    var code = (ex is HttpException) ? (ex as HttpException).GetHttpCode() : 500;

    switch (code)
    {
        case 400:
            action = "BadRequest";
            break;
        case 401:
            action = "Unauthorized";
            break;
        case 403:
            action = "Forbidden";
            break;
        case 404:
            action = "NotFound";
            break;
        case 500:
            action = "InternalServerError";
            break;
        default:
            action = "Index";
            break;
    }

    Server.ClearError();
    Response.Clear();
    Response.StatusCode = code;
    Response.TrySkipIisCustomErrors = true;

    routeData.Values["controller"] = "Error";
    routeData.Values["action"] = action;

    controller.ViewData.Model = new HandleErrorInfo(ex, currentController, currentAction);
    ((IController)controller).Execute(new RequestContext(new HttpContextWrapper(Context), routeData));
}
/// <summary>
/// Used to make a Get request to a specified url
/// </summary>
/// <param name="url">The target url</param>
/// <returns>Returns a string</returns>
public async Task<string> MakeApiCall(string url)
{
    return await MakeApiCall(url, HttpMethod.GET, null);
}

/// <summary>
/// Used to make a Post request to a specified url
/// </summary>
/// <param name="url">The target url</param>
/// <param name="method">The Http method</param>
/// <param name="data">The object to send to the api</param>
/// <returns>Returns a string</returns>
public async Task<string> MakeApiCall(string url, HttpMethod method, object data)
{

    // Create our local variables
    var client = new HttpClient();
    var user = Session["AccessToken"];
    var authenticating = user == null;

    // If we are not authenticating, set our auth token
    if (!authenticating)
        client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", Session["AccessToken"].ToString());

    // Check to see what HTTP method is being used
    switch (method)
    {
        case HttpMethod.POST:

            // If we are POSTing, then perform a post request
            return await PostRequest(client, url, data, authenticating);
        default:

            // If we are GETing, then perform a get request
            return await GetRequest(client, url);
    }
}

#region Helper methods

/// <summary>
/// Posts data to a specifed url
/// </summary>
/// <param name="client">The HttpClient used to make the api call</param>
/// <param name="url">The target url</param>
/// <param name="data">The object to send to the api</param>
/// <param name="authenticating">Used to set the content type when authenticating</param>
/// <returns>Returns a string</returns>
private async Task<string> PostRequest(HttpClient client, string url, object data, bool authenticating)
{

    // If the data is a string, then do a normal post, otherwise post as json
    var response = (data is string) ? await client.PostAsync(this.apiUrl + url, new StringContent(data.ToString())) : await client.PostAsJsonAsync(this.apiUrl + url, data);

    // If we are authenticating, set the content type header
    if (authenticating == true)
        response.Content.Headers.ContentType = new MediaTypeHeaderValue("application/x-www-form-urlencoded");

    // Handle our response
    return await HandleResponse(response);
}

/// <summary>
/// Gets data from a specifed url
/// </summary>
/// <param name="client">The HttpClient used to make the api call</param>
/// <param name="url">The target url</param>
/// <returns>Returns a string</returns>
private async Task<string> GetRequest(HttpClient client, string url)
{

    // Perform the get request
    var response = await client.GetAsync(this.apiUrl + url);

    // Handle our response
    return await HandleResponse(response);
}

/// <summary>
/// Used to handle the api response
/// </summary>
/// <param name="response">The HttpResponseMessage</param>
/// <returns>Returns a string</returns>
private async Task<string> HandleResponse(HttpResponseMessage response)
{

    // Read our response content
    var result = await response.Content.ReadAsStringAsync();

    // If there was an error, throw an HttpException
    if (response.StatusCode != HttpStatusCode.OK)
        throw new HttpException((int)response.StatusCode, result);

    // Return our result if there are no errors
    return result;
}

#endregion
/// <summary>
/// Custom Api Exception
/// </summary>
public class ApiException : Exception
{

    /// <summary>
    /// Default constructor
    /// </summary>
    public ApiException()
    {
    }

    /// <summary>
    /// Constructor with message
    /// </summary>
    /// <param name="message">The error message as a string</param>
    public ApiException(string message)
        : base(message)
    {
    }

    /// <summary>
    /// Constructor with message and inner exception
    /// </summary>
    /// <param name="message">The error message as a string</param>
    /// <param name="inner">The inner exception</param>
    public ApiException(string message, Exception inner)
        : base(message, inner)
    {
    }
}
/// <summary>
/// Used to handle the api response
/// </summary>
/// <param name="response">The HttpResponseMessage</param>
/// <returns>Returns a string</returns>
private async Task<string> HandleResponse(HttpResponseMessage response)
{

    // Read our response content
    var result = await response.Content.ReadAsStringAsync();

    // If there was an error, throw an HttpException
    if (response.StatusCode != HttpStatusCode.OK)
        throw new ApiException(result);

    // Return our result if there are no errors
    return result;
}
public class ExceptionAttribute : IExceptionFilter
{

    /// <summary>
    /// Handles any exception
    /// </summary>
    /// <param name="filterContext">The current context</param>
    public void OnException(ExceptionContext filterContext)
    {

        // If our exception has been handled, exit the function
        if (filterContext.ExceptionHandled)
            return;

        // If our exception is not an ApiException
        if (!(filterContext.Exception is ApiException))
        {

            // Set our base status code
            var statusCode = (int)HttpStatusCode.InternalServerError;

            // If our exception is an http exception
            if (filterContext.Exception is HttpException)
            {

                // Cast our exception as an HttpException
                var exception = (HttpException)filterContext.Exception;

                // Get our real status code
                statusCode = exception.GetHttpCode();
            }

            // Set our context result
            var result = CreateActionResult(filterContext, statusCode);

            // Set our handled property to true
            filterContext.ExceptionHandled = true;
        }
    }

    /// <summary>
    /// Creats an action result from the status code
    /// </summary>
    /// <param name="filterContext">The current context</param>
    /// <param name="statusCode">The status code of the error</param>
    /// <returns></returns>
    protected virtual ActionResult CreateActionResult(ExceptionContext filterContext, int statusCode)
    {

        // Create our context
        var context = new ControllerContext(filterContext.RequestContext, filterContext.Controller);
        var statusCodeName = ((HttpStatusCode)statusCode).ToString();

        // Create our route
        var controller = (string)filterContext.RouteData.Values["controller"];
        var action = (string)filterContext.RouteData.Values["action"];
        var model = new HandleErrorInfo(filterContext.Exception, controller, action);

        // Create our result
        var view = SelectFirstView(context, string.Format("~/Views/Error/{0}.cshtml", statusCodeName), "~/Views/Error/Index.cshtml", statusCodeName);
        var result = new ViewResult { ViewName = view, ViewData = new ViewDataDictionary<HandleErrorInfo>(model) };

        // Return our result
        return result;
    }

    /// <summary>
    /// Gets the first view name that matches the supplied names
    /// </summary>
    /// <param name="context">The current context</param>
    /// <param name="viewNames">A list of view names</param>
    /// <returns></returns>
    protected string SelectFirstView(ControllerContext context, params string[] viewNames)
    {
        return viewNames.First(view => ViewExists(context, view));
    }

    /// <summary>
    /// Checks to see if a view exists
    /// </summary>
    /// <param name="context">The current context</param>
    /// <param name="name">The name of the view to check</param>
    /// <returns></returns>
    protected bool ViewExists(ControllerContext context, string name)
    {
        var result = ViewEngines.Engines.FindView(context, name, null);

        return result.View != null;
    }
}
当我的MVC项目中出现错误时,这可以正常工作。 还有一个基类调用外部API,如下所示:

protected void Application_Error(object sender, EventArgs e)
{
    var currentController = " ";
    var currentAction = " ";
    var currentRouteData = RouteTable.Routes.GetRouteData(new HttpContextWrapper(Context));

    if (currentRouteData != null)
    {
        if (currentRouteData.Values["controller"] != null && !String.IsNullOrEmpty(currentRouteData.Values["controller"].ToString()))
            currentController = currentRouteData.Values["controller"].ToString();

        if (currentRouteData.Values["action"] != null && !String.IsNullOrEmpty(currentRouteData.Values["action"].ToString()))
            currentAction = currentRouteData.Values["action"].ToString();
    }

    var ex = Server.GetLastError();
    var controller = new ErrorController();
    var routeData = new RouteData();
    var action = "Index";

    var code = (ex is HttpException) ? (ex as HttpException).GetHttpCode() : 500;

    switch (code)
    {
        case 400:
            action = "BadRequest";
            break;
        case 401:
            action = "Unauthorized";
            break;
        case 403:
            action = "Forbidden";
            break;
        case 404:
            action = "NotFound";
            break;
        case 500:
            action = "InternalServerError";
            break;
        default:
            action = "Index";
            break;
    }

    Server.ClearError();
    Response.Clear();
    Response.StatusCode = code;
    Response.TrySkipIisCustomErrors = true;

    routeData.Values["controller"] = "Error";
    routeData.Values["action"] = action;

    controller.ViewData.Model = new HandleErrorInfo(ex, currentController, currentAction);
    ((IController)controller).Execute(new RequestContext(new HttpContextWrapper(Context), routeData));
}
/// <summary>
/// Used to make a Get request to a specified url
/// </summary>
/// <param name="url">The target url</param>
/// <returns>Returns a string</returns>
public async Task<string> MakeApiCall(string url)
{
    return await MakeApiCall(url, HttpMethod.GET, null);
}

/// <summary>
/// Used to make a Post request to a specified url
/// </summary>
/// <param name="url">The target url</param>
/// <param name="method">The Http method</param>
/// <param name="data">The object to send to the api</param>
/// <returns>Returns a string</returns>
public async Task<string> MakeApiCall(string url, HttpMethod method, object data)
{

    // Create our local variables
    var client = new HttpClient();
    var user = Session["AccessToken"];
    var authenticating = user == null;

    // If we are not authenticating, set our auth token
    if (!authenticating)
        client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", Session["AccessToken"].ToString());

    // Check to see what HTTP method is being used
    switch (method)
    {
        case HttpMethod.POST:

            // If we are POSTing, then perform a post request
            return await PostRequest(client, url, data, authenticating);
        default:

            // If we are GETing, then perform a get request
            return await GetRequest(client, url);
    }
}

#region Helper methods

/// <summary>
/// Posts data to a specifed url
/// </summary>
/// <param name="client">The HttpClient used to make the api call</param>
/// <param name="url">The target url</param>
/// <param name="data">The object to send to the api</param>
/// <param name="authenticating">Used to set the content type when authenticating</param>
/// <returns>Returns a string</returns>
private async Task<string> PostRequest(HttpClient client, string url, object data, bool authenticating)
{

    // If the data is a string, then do a normal post, otherwise post as json
    var response = (data is string) ? await client.PostAsync(this.apiUrl + url, new StringContent(data.ToString())) : await client.PostAsJsonAsync(this.apiUrl + url, data);

    // If we are authenticating, set the content type header
    if (authenticating == true)
        response.Content.Headers.ContentType = new MediaTypeHeaderValue("application/x-www-form-urlencoded");

    // Handle our response
    return await HandleResponse(response);
}

/// <summary>
/// Gets data from a specifed url
/// </summary>
/// <param name="client">The HttpClient used to make the api call</param>
/// <param name="url">The target url</param>
/// <returns>Returns a string</returns>
private async Task<string> GetRequest(HttpClient client, string url)
{

    // Perform the get request
    var response = await client.GetAsync(this.apiUrl + url);

    // Handle our response
    return await HandleResponse(response);
}

/// <summary>
/// Used to handle the api response
/// </summary>
/// <param name="response">The HttpResponseMessage</param>
/// <returns>Returns a string</returns>
private async Task<string> HandleResponse(HttpResponseMessage response)
{

    // Read our response content
    var result = await response.Content.ReadAsStringAsync();

    // If there was an error, throw an HttpException
    if (response.StatusCode != HttpStatusCode.OK)
        throw new HttpException((int)response.StatusCode, result);

    // Return our result if there are no errors
    return result;
}

#endregion
/// <summary>
/// Custom Api Exception
/// </summary>
public class ApiException : Exception
{

    /// <summary>
    /// Default constructor
    /// </summary>
    public ApiException()
    {
    }

    /// <summary>
    /// Constructor with message
    /// </summary>
    /// <param name="message">The error message as a string</param>
    public ApiException(string message)
        : base(message)
    {
    }

    /// <summary>
    /// Constructor with message and inner exception
    /// </summary>
    /// <param name="message">The error message as a string</param>
    /// <param name="inner">The inner exception</param>
    public ApiException(string message, Exception inner)
        : base(message, inner)
    {
    }
}
/// <summary>
/// Used to handle the api response
/// </summary>
/// <param name="response">The HttpResponseMessage</param>
/// <returns>Returns a string</returns>
private async Task<string> HandleResponse(HttpResponseMessage response)
{

    // Read our response content
    var result = await response.Content.ReadAsStringAsync();

    // If there was an error, throw an HttpException
    if (response.StatusCode != HttpStatusCode.OK)
        throw new ApiException(result);

    // Return our result if there are no errors
    return result;
}
public class ExceptionAttribute : IExceptionFilter
{

    /// <summary>
    /// Handles any exception
    /// </summary>
    /// <param name="filterContext">The current context</param>
    public void OnException(ExceptionContext filterContext)
    {

        // If our exception has been handled, exit the function
        if (filterContext.ExceptionHandled)
            return;

        // If our exception is not an ApiException
        if (!(filterContext.Exception is ApiException))
        {

            // Set our base status code
            var statusCode = (int)HttpStatusCode.InternalServerError;

            // If our exception is an http exception
            if (filterContext.Exception is HttpException)
            {

                // Cast our exception as an HttpException
                var exception = (HttpException)filterContext.Exception;

                // Get our real status code
                statusCode = exception.GetHttpCode();
            }

            // Set our context result
            var result = CreateActionResult(filterContext, statusCode);

            // Set our handled property to true
            filterContext.ExceptionHandled = true;
        }
    }

    /// <summary>
    /// Creats an action result from the status code
    /// </summary>
    /// <param name="filterContext">The current context</param>
    /// <param name="statusCode">The status code of the error</param>
    /// <returns></returns>
    protected virtual ActionResult CreateActionResult(ExceptionContext filterContext, int statusCode)
    {

        // Create our context
        var context = new ControllerContext(filterContext.RequestContext, filterContext.Controller);
        var statusCodeName = ((HttpStatusCode)statusCode).ToString();

        // Create our route
        var controller = (string)filterContext.RouteData.Values["controller"];
        var action = (string)filterContext.RouteData.Values["action"];
        var model = new HandleErrorInfo(filterContext.Exception, controller, action);

        // Create our result
        var view = SelectFirstView(context, string.Format("~/Views/Error/{0}.cshtml", statusCodeName), "~/Views/Error/Index.cshtml", statusCodeName);
        var result = new ViewResult { ViewName = view, ViewData = new ViewDataDictionary<HandleErrorInfo>(model) };

        // Return our result
        return result;
    }

    /// <summary>
    /// Gets the first view name that matches the supplied names
    /// </summary>
    /// <param name="context">The current context</param>
    /// <param name="viewNames">A list of view names</param>
    /// <returns></returns>
    protected string SelectFirstView(ControllerContext context, params string[] viewNames)
    {
        return viewNames.First(view => ViewExists(context, view));
    }

    /// <summary>
    /// Checks to see if a view exists
    /// </summary>
    /// <param name="context">The current context</param>
    /// <param name="name">The name of the view to check</param>
    /// <returns></returns>
    protected bool ViewExists(ControllerContext context, string name)
    {
        var result = ViewEngines.Engines.FindView(context, name, null);

        return result.View != null;
    }
}
这反过来又被Global.asax中的应用程序错误方法捕获。问题是,由于这是一个API调用,控制器无法重定向到ErrorController

所以我的问题是:

  • 我是否可以忽略Global.asax错误处理,只返回JSON,这样我的JavaScript就可以决定如何处理错误或错误了
  • 有更好的方法吗
  • 如果你有任何问题,请提问。我试图确保这篇文章不仅仅是一堵文字墙

    更新1

    因此,我尝试使用AttributeFilter来帮助解决这个问题。 我使用了两个用户建议的两种方法。首先,我创建了一个自定义的异常,如下所示:

    protected void Application_Error(object sender, EventArgs e)
    {
        var currentController = " ";
        var currentAction = " ";
        var currentRouteData = RouteTable.Routes.GetRouteData(new HttpContextWrapper(Context));
    
        if (currentRouteData != null)
        {
            if (currentRouteData.Values["controller"] != null && !String.IsNullOrEmpty(currentRouteData.Values["controller"].ToString()))
                currentController = currentRouteData.Values["controller"].ToString();
    
            if (currentRouteData.Values["action"] != null && !String.IsNullOrEmpty(currentRouteData.Values["action"].ToString()))
                currentAction = currentRouteData.Values["action"].ToString();
        }
    
        var ex = Server.GetLastError();
        var controller = new ErrorController();
        var routeData = new RouteData();
        var action = "Index";
    
        var code = (ex is HttpException) ? (ex as HttpException).GetHttpCode() : 500;
    
        switch (code)
        {
            case 400:
                action = "BadRequest";
                break;
            case 401:
                action = "Unauthorized";
                break;
            case 403:
                action = "Forbidden";
                break;
            case 404:
                action = "NotFound";
                break;
            case 500:
                action = "InternalServerError";
                break;
            default:
                action = "Index";
                break;
        }
    
        Server.ClearError();
        Response.Clear();
        Response.StatusCode = code;
        Response.TrySkipIisCustomErrors = true;
    
        routeData.Values["controller"] = "Error";
        routeData.Values["action"] = action;
    
        controller.ViewData.Model = new HandleErrorInfo(ex, currentController, currentAction);
        ((IController)controller).Execute(new RequestContext(new HttpContextWrapper(Context), routeData));
    }
    
    /// <summary>
    /// Used to make a Get request to a specified url
    /// </summary>
    /// <param name="url">The target url</param>
    /// <returns>Returns a string</returns>
    public async Task<string> MakeApiCall(string url)
    {
        return await MakeApiCall(url, HttpMethod.GET, null);
    }
    
    /// <summary>
    /// Used to make a Post request to a specified url
    /// </summary>
    /// <param name="url">The target url</param>
    /// <param name="method">The Http method</param>
    /// <param name="data">The object to send to the api</param>
    /// <returns>Returns a string</returns>
    public async Task<string> MakeApiCall(string url, HttpMethod method, object data)
    {
    
        // Create our local variables
        var client = new HttpClient();
        var user = Session["AccessToken"];
        var authenticating = user == null;
    
        // If we are not authenticating, set our auth token
        if (!authenticating)
            client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", Session["AccessToken"].ToString());
    
        // Check to see what HTTP method is being used
        switch (method)
        {
            case HttpMethod.POST:
    
                // If we are POSTing, then perform a post request
                return await PostRequest(client, url, data, authenticating);
            default:
    
                // If we are GETing, then perform a get request
                return await GetRequest(client, url);
        }
    }
    
    #region Helper methods
    
    /// <summary>
    /// Posts data to a specifed url
    /// </summary>
    /// <param name="client">The HttpClient used to make the api call</param>
    /// <param name="url">The target url</param>
    /// <param name="data">The object to send to the api</param>
    /// <param name="authenticating">Used to set the content type when authenticating</param>
    /// <returns>Returns a string</returns>
    private async Task<string> PostRequest(HttpClient client, string url, object data, bool authenticating)
    {
    
        // If the data is a string, then do a normal post, otherwise post as json
        var response = (data is string) ? await client.PostAsync(this.apiUrl + url, new StringContent(data.ToString())) : await client.PostAsJsonAsync(this.apiUrl + url, data);
    
        // If we are authenticating, set the content type header
        if (authenticating == true)
            response.Content.Headers.ContentType = new MediaTypeHeaderValue("application/x-www-form-urlencoded");
    
        // Handle our response
        return await HandleResponse(response);
    }
    
    /// <summary>
    /// Gets data from a specifed url
    /// </summary>
    /// <param name="client">The HttpClient used to make the api call</param>
    /// <param name="url">The target url</param>
    /// <returns>Returns a string</returns>
    private async Task<string> GetRequest(HttpClient client, string url)
    {
    
        // Perform the get request
        var response = await client.GetAsync(this.apiUrl + url);
    
        // Handle our response
        return await HandleResponse(response);
    }
    
    /// <summary>
    /// Used to handle the api response
    /// </summary>
    /// <param name="response">The HttpResponseMessage</param>
    /// <returns>Returns a string</returns>
    private async Task<string> HandleResponse(HttpResponseMessage response)
    {
    
        // Read our response content
        var result = await response.Content.ReadAsStringAsync();
    
        // If there was an error, throw an HttpException
        if (response.StatusCode != HttpStatusCode.OK)
            throw new HttpException((int)response.StatusCode, result);
    
        // Return our result if there are no errors
        return result;
    }
    
    #endregion
    
    /// <summary>
    /// Custom Api Exception
    /// </summary>
    public class ApiException : Exception
    {
    
        /// <summary>
        /// Default constructor
        /// </summary>
        public ApiException()
        {
        }
    
        /// <summary>
        /// Constructor with message
        /// </summary>
        /// <param name="message">The error message as a string</param>
        public ApiException(string message)
            : base(message)
        {
        }
    
        /// <summary>
        /// Constructor with message and inner exception
        /// </summary>
        /// <param name="message">The error message as a string</param>
        /// <param name="inner">The inner exception</param>
        public ApiException(string message, Exception inner)
            : base(message, inner)
        {
        }
    }
    
    /// <summary>
    /// Used to handle the api response
    /// </summary>
    /// <param name="response">The HttpResponseMessage</param>
    /// <returns>Returns a string</returns>
    private async Task<string> HandleResponse(HttpResponseMessage response)
    {
    
        // Read our response content
        var result = await response.Content.ReadAsStringAsync();
    
        // If there was an error, throw an HttpException
        if (response.StatusCode != HttpStatusCode.OK)
            throw new ApiException(result);
    
        // Return our result if there are no errors
        return result;
    }
    
    public class ExceptionAttribute : IExceptionFilter
    {
    
        /// <summary>
        /// Handles any exception
        /// </summary>
        /// <param name="filterContext">The current context</param>
        public void OnException(ExceptionContext filterContext)
        {
    
            // If our exception has been handled, exit the function
            if (filterContext.ExceptionHandled)
                return;
    
            // If our exception is not an ApiException
            if (!(filterContext.Exception is ApiException))
            {
    
                // Set our base status code
                var statusCode = (int)HttpStatusCode.InternalServerError;
    
                // If our exception is an http exception
                if (filterContext.Exception is HttpException)
                {
    
                    // Cast our exception as an HttpException
                    var exception = (HttpException)filterContext.Exception;
    
                    // Get our real status code
                    statusCode = exception.GetHttpCode();
                }
    
                // Set our context result
                var result = CreateActionResult(filterContext, statusCode);
    
                // Set our handled property to true
                filterContext.ExceptionHandled = true;
            }
        }
    
        /// <summary>
        /// Creats an action result from the status code
        /// </summary>
        /// <param name="filterContext">The current context</param>
        /// <param name="statusCode">The status code of the error</param>
        /// <returns></returns>
        protected virtual ActionResult CreateActionResult(ExceptionContext filterContext, int statusCode)
        {
    
            // Create our context
            var context = new ControllerContext(filterContext.RequestContext, filterContext.Controller);
            var statusCodeName = ((HttpStatusCode)statusCode).ToString();
    
            // Create our route
            var controller = (string)filterContext.RouteData.Values["controller"];
            var action = (string)filterContext.RouteData.Values["action"];
            var model = new HandleErrorInfo(filterContext.Exception, controller, action);
    
            // Create our result
            var view = SelectFirstView(context, string.Format("~/Views/Error/{0}.cshtml", statusCodeName), "~/Views/Error/Index.cshtml", statusCodeName);
            var result = new ViewResult { ViewName = view, ViewData = new ViewDataDictionary<HandleErrorInfo>(model) };
    
            // Return our result
            return result;
        }
    
        /// <summary>
        /// Gets the first view name that matches the supplied names
        /// </summary>
        /// <param name="context">The current context</param>
        /// <param name="viewNames">A list of view names</param>
        /// <returns></returns>
        protected string SelectFirstView(ControllerContext context, params string[] viewNames)
        {
            return viewNames.First(view => ViewExists(context, view));
        }
    
        /// <summary>
        /// Checks to see if a view exists
        /// </summary>
        /// <param name="context">The current context</param>
        /// <param name="name">The name of the view to check</param>
        /// <returns></returns>
        protected bool ViewExists(ControllerContext context, string name)
        {
            var result = ViewEngines.Engines.FindView(context, name, null);
    
            return result.View != null;
        }
    }
    
    //
    ///自定义Api异常
    /// 
    公共类异常:异常
    {
    /// 
    ///默认构造函数
    /// 
    公共例外()
    {
    }
    /// 
    ///带消息的构造函数
    /// 
    ///以字符串形式显示错误消息
    公共API异常(字符串消息)
    :base(消息)
    {
    }
    /// 
    ///具有消息和内部异常的构造函数
    /// 
    ///以字符串形式显示错误消息
    ///内部例外
    公共ApiException(字符串消息,内部异常)
    :base(消息,内部)
    {
    }
    }
    
    然后,我在基本控制器中更新了我的HandlerResponse方法,如下所示:

    protected void Application_Error(object sender, EventArgs e)
    {
        var currentController = " ";
        var currentAction = " ";
        var currentRouteData = RouteTable.Routes.GetRouteData(new HttpContextWrapper(Context));
    
        if (currentRouteData != null)
        {
            if (currentRouteData.Values["controller"] != null && !String.IsNullOrEmpty(currentRouteData.Values["controller"].ToString()))
                currentController = currentRouteData.Values["controller"].ToString();
    
            if (currentRouteData.Values["action"] != null && !String.IsNullOrEmpty(currentRouteData.Values["action"].ToString()))
                currentAction = currentRouteData.Values["action"].ToString();
        }
    
        var ex = Server.GetLastError();
        var controller = new ErrorController();
        var routeData = new RouteData();
        var action = "Index";
    
        var code = (ex is HttpException) ? (ex as HttpException).GetHttpCode() : 500;
    
        switch (code)
        {
            case 400:
                action = "BadRequest";
                break;
            case 401:
                action = "Unauthorized";
                break;
            case 403:
                action = "Forbidden";
                break;
            case 404:
                action = "NotFound";
                break;
            case 500:
                action = "InternalServerError";
                break;
            default:
                action = "Index";
                break;
        }
    
        Server.ClearError();
        Response.Clear();
        Response.StatusCode = code;
        Response.TrySkipIisCustomErrors = true;
    
        routeData.Values["controller"] = "Error";
        routeData.Values["action"] = action;
    
        controller.ViewData.Model = new HandleErrorInfo(ex, currentController, currentAction);
        ((IController)controller).Execute(new RequestContext(new HttpContextWrapper(Context), routeData));
    }
    
    /// <summary>
    /// Used to make a Get request to a specified url
    /// </summary>
    /// <param name="url">The target url</param>
    /// <returns>Returns a string</returns>
    public async Task<string> MakeApiCall(string url)
    {
        return await MakeApiCall(url, HttpMethod.GET, null);
    }
    
    /// <summary>
    /// Used to make a Post request to a specified url
    /// </summary>
    /// <param name="url">The target url</param>
    /// <param name="method">The Http method</param>
    /// <param name="data">The object to send to the api</param>
    /// <returns>Returns a string</returns>
    public async Task<string> MakeApiCall(string url, HttpMethod method, object data)
    {
    
        // Create our local variables
        var client = new HttpClient();
        var user = Session["AccessToken"];
        var authenticating = user == null;
    
        // If we are not authenticating, set our auth token
        if (!authenticating)
            client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", Session["AccessToken"].ToString());
    
        // Check to see what HTTP method is being used
        switch (method)
        {
            case HttpMethod.POST:
    
                // If we are POSTing, then perform a post request
                return await PostRequest(client, url, data, authenticating);
            default:
    
                // If we are GETing, then perform a get request
                return await GetRequest(client, url);
        }
    }
    
    #region Helper methods
    
    /// <summary>
    /// Posts data to a specifed url
    /// </summary>
    /// <param name="client">The HttpClient used to make the api call</param>
    /// <param name="url">The target url</param>
    /// <param name="data">The object to send to the api</param>
    /// <param name="authenticating">Used to set the content type when authenticating</param>
    /// <returns>Returns a string</returns>
    private async Task<string> PostRequest(HttpClient client, string url, object data, bool authenticating)
    {
    
        // If the data is a string, then do a normal post, otherwise post as json
        var response = (data is string) ? await client.PostAsync(this.apiUrl + url, new StringContent(data.ToString())) : await client.PostAsJsonAsync(this.apiUrl + url, data);
    
        // If we are authenticating, set the content type header
        if (authenticating == true)
            response.Content.Headers.ContentType = new MediaTypeHeaderValue("application/x-www-form-urlencoded");
    
        // Handle our response
        return await HandleResponse(response);
    }
    
    /// <summary>
    /// Gets data from a specifed url
    /// </summary>
    /// <param name="client">The HttpClient used to make the api call</param>
    /// <param name="url">The target url</param>
    /// <returns>Returns a string</returns>
    private async Task<string> GetRequest(HttpClient client, string url)
    {
    
        // Perform the get request
        var response = await client.GetAsync(this.apiUrl + url);
    
        // Handle our response
        return await HandleResponse(response);
    }
    
    /// <summary>
    /// Used to handle the api response
    /// </summary>
    /// <param name="response">The HttpResponseMessage</param>
    /// <returns>Returns a string</returns>
    private async Task<string> HandleResponse(HttpResponseMessage response)
    {
    
        // Read our response content
        var result = await response.Content.ReadAsStringAsync();
    
        // If there was an error, throw an HttpException
        if (response.StatusCode != HttpStatusCode.OK)
            throw new HttpException((int)response.StatusCode, result);
    
        // Return our result if there are no errors
        return result;
    }
    
    #endregion
    
    /// <summary>
    /// Custom Api Exception
    /// </summary>
    public class ApiException : Exception
    {
    
        /// <summary>
        /// Default constructor
        /// </summary>
        public ApiException()
        {
        }
    
        /// <summary>
        /// Constructor with message
        /// </summary>
        /// <param name="message">The error message as a string</param>
        public ApiException(string message)
            : base(message)
        {
        }
    
        /// <summary>
        /// Constructor with message and inner exception
        /// </summary>
        /// <param name="message">The error message as a string</param>
        /// <param name="inner">The inner exception</param>
        public ApiException(string message, Exception inner)
            : base(message, inner)
        {
        }
    }
    
    /// <summary>
    /// Used to handle the api response
    /// </summary>
    /// <param name="response">The HttpResponseMessage</param>
    /// <returns>Returns a string</returns>
    private async Task<string> HandleResponse(HttpResponseMessage response)
    {
    
        // Read our response content
        var result = await response.Content.ReadAsStringAsync();
    
        // If there was an error, throw an HttpException
        if (response.StatusCode != HttpStatusCode.OK)
            throw new ApiException(result);
    
        // Return our result if there are no errors
        return result;
    }
    
    public class ExceptionAttribute : IExceptionFilter
    {
    
        /// <summary>
        /// Handles any exception
        /// </summary>
        /// <param name="filterContext">The current context</param>
        public void OnException(ExceptionContext filterContext)
        {
    
            // If our exception has been handled, exit the function
            if (filterContext.ExceptionHandled)
                return;
    
            // If our exception is not an ApiException
            if (!(filterContext.Exception is ApiException))
            {
    
                // Set our base status code
                var statusCode = (int)HttpStatusCode.InternalServerError;
    
                // If our exception is an http exception
                if (filterContext.Exception is HttpException)
                {
    
                    // Cast our exception as an HttpException
                    var exception = (HttpException)filterContext.Exception;
    
                    // Get our real status code
                    statusCode = exception.GetHttpCode();
                }
    
                // Set our context result
                var result = CreateActionResult(filterContext, statusCode);
    
                // Set our handled property to true
                filterContext.ExceptionHandled = true;
            }
        }
    
        /// <summary>
        /// Creats an action result from the status code
        /// </summary>
        /// <param name="filterContext">The current context</param>
        /// <param name="statusCode">The status code of the error</param>
        /// <returns></returns>
        protected virtual ActionResult CreateActionResult(ExceptionContext filterContext, int statusCode)
        {
    
            // Create our context
            var context = new ControllerContext(filterContext.RequestContext, filterContext.Controller);
            var statusCodeName = ((HttpStatusCode)statusCode).ToString();
    
            // Create our route
            var controller = (string)filterContext.RouteData.Values["controller"];
            var action = (string)filterContext.RouteData.Values["action"];
            var model = new HandleErrorInfo(filterContext.Exception, controller, action);
    
            // Create our result
            var view = SelectFirstView(context, string.Format("~/Views/Error/{0}.cshtml", statusCodeName), "~/Views/Error/Index.cshtml", statusCodeName);
            var result = new ViewResult { ViewName = view, ViewData = new ViewDataDictionary<HandleErrorInfo>(model) };
    
            // Return our result
            return result;
        }
    
        /// <summary>
        /// Gets the first view name that matches the supplied names
        /// </summary>
        /// <param name="context">The current context</param>
        /// <param name="viewNames">A list of view names</param>
        /// <returns></returns>
        protected string SelectFirstView(ControllerContext context, params string[] viewNames)
        {
            return viewNames.First(view => ViewExists(context, view));
        }
    
        /// <summary>
        /// Checks to see if a view exists
        /// </summary>
        /// <param name="context">The current context</param>
        /// <param name="name">The name of the view to check</param>
        /// <returns></returns>
        protected bool ViewExists(ControllerContext context, string name)
        {
            var result = ViewEngines.Engines.FindView(context, name, null);
    
            return result.View != null;
        }
    }
    
    //
    ///用于处理api响应
    /// 
    ///HttpResponseMessage
    ///返回一个字符串
    专用异步任务句柄响应(HttpResponseMessage响应)
    {
    //阅读我们的回复内容
    var result=await response.Content.ReadAsStringAsync();
    //如果出现错误,则抛出HttpException
    if(response.StatusCode!=HttpStatusCode.OK)
    抛出新异常(结果);
    //如果没有错误,请返回我们的结果
    返回结果;
    }
    
    然后我创建了一个过滤器,并将其添加到过滤器配置中,如下所示:

    protected void Application_Error(object sender, EventArgs e)
    {
        var currentController = " ";
        var currentAction = " ";
        var currentRouteData = RouteTable.Routes.GetRouteData(new HttpContextWrapper(Context));
    
        if (currentRouteData != null)
        {
            if (currentRouteData.Values["controller"] != null && !String.IsNullOrEmpty(currentRouteData.Values["controller"].ToString()))
                currentController = currentRouteData.Values["controller"].ToString();
    
            if (currentRouteData.Values["action"] != null && !String.IsNullOrEmpty(currentRouteData.Values["action"].ToString()))
                currentAction = currentRouteData.Values["action"].ToString();
        }
    
        var ex = Server.GetLastError();
        var controller = new ErrorController();
        var routeData = new RouteData();
        var action = "Index";
    
        var code = (ex is HttpException) ? (ex as HttpException).GetHttpCode() : 500;
    
        switch (code)
        {
            case 400:
                action = "BadRequest";
                break;
            case 401:
                action = "Unauthorized";
                break;
            case 403:
                action = "Forbidden";
                break;
            case 404:
                action = "NotFound";
                break;
            case 500:
                action = "InternalServerError";
                break;
            default:
                action = "Index";
                break;
        }
    
        Server.ClearError();
        Response.Clear();
        Response.StatusCode = code;
        Response.TrySkipIisCustomErrors = true;
    
        routeData.Values["controller"] = "Error";
        routeData.Values["action"] = action;
    
        controller.ViewData.Model = new HandleErrorInfo(ex, currentController, currentAction);
        ((IController)controller).Execute(new RequestContext(new HttpContextWrapper(Context), routeData));
    }
    
    /// <summary>
    /// Used to make a Get request to a specified url
    /// </summary>
    /// <param name="url">The target url</param>
    /// <returns>Returns a string</returns>
    public async Task<string> MakeApiCall(string url)
    {
        return await MakeApiCall(url, HttpMethod.GET, null);
    }
    
    /// <summary>
    /// Used to make a Post request to a specified url
    /// </summary>
    /// <param name="url">The target url</param>
    /// <param name="method">The Http method</param>
    /// <param name="data">The object to send to the api</param>
    /// <returns>Returns a string</returns>
    public async Task<string> MakeApiCall(string url, HttpMethod method, object data)
    {
    
        // Create our local variables
        var client = new HttpClient();
        var user = Session["AccessToken"];
        var authenticating = user == null;
    
        // If we are not authenticating, set our auth token
        if (!authenticating)
            client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", Session["AccessToken"].ToString());
    
        // Check to see what HTTP method is being used
        switch (method)
        {
            case HttpMethod.POST:
    
                // If we are POSTing, then perform a post request
                return await PostRequest(client, url, data, authenticating);
            default:
    
                // If we are GETing, then perform a get request
                return await GetRequest(client, url);
        }
    }
    
    #region Helper methods
    
    /// <summary>
    /// Posts data to a specifed url
    /// </summary>
    /// <param name="client">The HttpClient used to make the api call</param>
    /// <param name="url">The target url</param>
    /// <param name="data">The object to send to the api</param>
    /// <param name="authenticating">Used to set the content type when authenticating</param>
    /// <returns>Returns a string</returns>
    private async Task<string> PostRequest(HttpClient client, string url, object data, bool authenticating)
    {
    
        // If the data is a string, then do a normal post, otherwise post as json
        var response = (data is string) ? await client.PostAsync(this.apiUrl + url, new StringContent(data.ToString())) : await client.PostAsJsonAsync(this.apiUrl + url, data);
    
        // If we are authenticating, set the content type header
        if (authenticating == true)
            response.Content.Headers.ContentType = new MediaTypeHeaderValue("application/x-www-form-urlencoded");
    
        // Handle our response
        return await HandleResponse(response);
    }
    
    /// <summary>
    /// Gets data from a specifed url
    /// </summary>
    /// <param name="client">The HttpClient used to make the api call</param>
    /// <param name="url">The target url</param>
    /// <returns>Returns a string</returns>
    private async Task<string> GetRequest(HttpClient client, string url)
    {
    
        // Perform the get request
        var response = await client.GetAsync(this.apiUrl + url);
    
        // Handle our response
        return await HandleResponse(response);
    }
    
    /// <summary>
    /// Used to handle the api response
    /// </summary>
    /// <param name="response">The HttpResponseMessage</param>
    /// <returns>Returns a string</returns>
    private async Task<string> HandleResponse(HttpResponseMessage response)
    {
    
        // Read our response content
        var result = await response.Content.ReadAsStringAsync();
    
        // If there was an error, throw an HttpException
        if (response.StatusCode != HttpStatusCode.OK)
            throw new HttpException((int)response.StatusCode, result);
    
        // Return our result if there are no errors
        return result;
    }
    
    #endregion
    
    /// <summary>
    /// Custom Api Exception
    /// </summary>
    public class ApiException : Exception
    {
    
        /// <summary>
        /// Default constructor
        /// </summary>
        public ApiException()
        {
        }
    
        /// <summary>
        /// Constructor with message
        /// </summary>
        /// <param name="message">The error message as a string</param>
        public ApiException(string message)
            : base(message)
        {
        }
    
        /// <summary>
        /// Constructor with message and inner exception
        /// </summary>
        /// <param name="message">The error message as a string</param>
        /// <param name="inner">The inner exception</param>
        public ApiException(string message, Exception inner)
            : base(message, inner)
        {
        }
    }
    
    /// <summary>
    /// Used to handle the api response
    /// </summary>
    /// <param name="response">The HttpResponseMessage</param>
    /// <returns>Returns a string</returns>
    private async Task<string> HandleResponse(HttpResponseMessage response)
    {
    
        // Read our response content
        var result = await response.Content.ReadAsStringAsync();
    
        // If there was an error, throw an HttpException
        if (response.StatusCode != HttpStatusCode.OK)
            throw new ApiException(result);
    
        // Return our result if there are no errors
        return result;
    }
    
    public class ExceptionAttribute : IExceptionFilter
    {
    
        /// <summary>
        /// Handles any exception
        /// </summary>
        /// <param name="filterContext">The current context</param>
        public void OnException(ExceptionContext filterContext)
        {
    
            // If our exception has been handled, exit the function
            if (filterContext.ExceptionHandled)
                return;
    
            // If our exception is not an ApiException
            if (!(filterContext.Exception is ApiException))
            {
    
                // Set our base status code
                var statusCode = (int)HttpStatusCode.InternalServerError;
    
                // If our exception is an http exception
                if (filterContext.Exception is HttpException)
                {
    
                    // Cast our exception as an HttpException
                    var exception = (HttpException)filterContext.Exception;
    
                    // Get our real status code
                    statusCode = exception.GetHttpCode();
                }
    
                // Set our context result
                var result = CreateActionResult(filterContext, statusCode);
    
                // Set our handled property to true
                filterContext.ExceptionHandled = true;
            }
        }
    
        /// <summary>
        /// Creats an action result from the status code
        /// </summary>
        /// <param name="filterContext">The current context</param>
        /// <param name="statusCode">The status code of the error</param>
        /// <returns></returns>
        protected virtual ActionResult CreateActionResult(ExceptionContext filterContext, int statusCode)
        {
    
            // Create our context
            var context = new ControllerContext(filterContext.RequestContext, filterContext.Controller);
            var statusCodeName = ((HttpStatusCode)statusCode).ToString();
    
            // Create our route
            var controller = (string)filterContext.RouteData.Values["controller"];
            var action = (string)filterContext.RouteData.Values["action"];
            var model = new HandleErrorInfo(filterContext.Exception, controller, action);
    
            // Create our result
            var view = SelectFirstView(context, string.Format("~/Views/Error/{0}.cshtml", statusCodeName), "~/Views/Error/Index.cshtml", statusCodeName);
            var result = new ViewResult { ViewName = view, ViewData = new ViewDataDictionary<HandleErrorInfo>(model) };
    
            // Return our result
            return result;
        }
    
        /// <summary>
        /// Gets the first view name that matches the supplied names
        /// </summary>
        /// <param name="context">The current context</param>
        /// <param name="viewNames">A list of view names</param>
        /// <returns></returns>
        protected string SelectFirstView(ControllerContext context, params string[] viewNames)
        {
            return viewNames.First(view => ViewExists(context, view));
        }
    
        /// <summary>
        /// Checks to see if a view exists
        /// </summary>
        /// <param name="context">The current context</param>
        /// <param name="name">The name of the view to check</param>
        /// <returns></returns>
        protected bool ViewExists(ControllerContext context, string name)
        {
            var result = ViewEngines.Engines.FindView(context, name, null);
    
            return result.View != null;
        }
    }
    
    公共类例外属性:IEExceptionFilter
    {
    /// 
    ///处理任何异常
    /// 
    ///当前环境
    公共无效OneException(例外上下文筛选器上下文)
    {
    //如果我们的异常已被处理,请退出该函数
    if(filterContext.ExceptionHandled)
    返回;
    //如果我们的例外不是一个例外
    如果(!(filterContext.Exception为ApiException))
    {
    //设置我们的基本状态代码
    var statusCode=(int)HttpStatusCode.InternalServerError;
    //如果我们的异常是http异常
    if(filterContext.Exception为HttpException)
    {
    //将我们的异常强制转换为HttpException
    var exception=(HttpException)filterContext.exception;
    //获取我们的真实状态代码
    statusCode=exception.GetHttpCode();
    }
    //设置上下文结果
    var result=CreateActionResult(filterContext,statusCode);
    //将我们处理的属性设置为true
    filterContext.ExceptionHandled=true;
    }
    }
    /// 
    ///根据状态代码创建操作结果
    /// 
    ///当前环境
    ///错误的状态代码
    /// 
    受保护的虚拟ActionResult CreateActionResult(上下文筛选器上下文、int状态代码除外)
    {
    //创造我们的环境
    var context=newcontrollercontext(filterContext.RequestContext,filterContext.Controller);
    var statusCodeName=((HttpStatusCode)statusCode).ToString();
    //创建我们的路线
    变量控制器=(字符串)filterContext.RouteData.Values[“controller”];
    var action=(字符串)filterContext.RouteData.Values[“action”];
    var模型=新HandleErrorInfo(filterContext.Exception,controller,action);
    //创造我们的结果
    var view=SelectFirstView(context,string.Format(“~/Views/Error/{0}.cshtml”,statusCodeName),“~/Views/Error/Index.cshtml”,statusCodeName);
    var result=newviewresult{ViewName=view,ViewData=newviewdatadictionary(model)};
    //返回我们的结果
    返回结果;
    }
    /// 
    ///获取与提供的名称匹配的第一个视图名称
    /// 
    ///当前环境
    ///视图名称的列表
    /// 
    受保护的字符串SelectFirstView(ControllerContext上下文,参数字符串[]视图名称)
    {
    返回viewNames.First(view=>ViewExists(context,view));
    }
    /// 
    ///检查视图是否存在
    /// 
    ///当前环境
    ///要检查的视图的名称
    /// 
    受保护的布尔视图存在(ControllerContext上下文、字符串名称)
    {
    var result=ViewEngines.Engines.FindView(上下文、名称、null);
    返回结果。视图!=null;
    }
    }
    
    最后,我从Global.asax中的应用程序错误方法中删除了逻辑,希望这能起作用。但事实并非如此。当出现ApiException时,我仍然会得到一个返回的文档

    有人能帮我吗

    我是否可以忽略Global.asax错误处理,只返回JSON,这样我的JavaScript就可以决定如何处理错误了

    由于
    Global.asax
    是ASP.NET管道的一部分,因此没有本机方法可以忽略它。您可能会求助于一些黑客,但是如果您使用MVC和WebApi框架来解决这个问题,而不是依赖过时的ASP.NET行为,那就更好了

    有更好的方法吗

    在这两种情况下都可以使用异常过滤器