J.T*_*lor 3 asp.net iis-7.5 asp.net-web-api
我正在使用 WebApi 2.1 创建一个面向公众的 API,有一个专用的 WebApi 项目(没有 MVC),在自己的服务器上托管在 IIS 7.5 中的 API,并且设计目标是只返回 JSON 或空内容,并且从不返回 HTML。
我很高兴使用 ExceptionFilterAttribute 来处理 WebApi 管道中出现的异常,如下所示:
public class GlobalExceptionHandler : ExceptionFilterAttribute
{
    public override void OnException(HttpActionExecutedContext context)
    {
        // Log the exception to Elmah
        Elmah.Error error = new Elmah.Error(context.Exception, HttpContext.Current);
        error.Detail = ActionState.GetRequestParameters(context) + error.Detail;
        Elmah.ErrorLog.GetDefault(HttpContext.Current).Log(error);
        if (context.Exception is NotImplementedException)
        {
            context.Response = context.Request.CreateErrorResponse(
                HttpStatusCode.NotImplemented
                , "The API method has not yet been implemented"
            );
        }
        else
        {
            context.Response = context.Request.CreateErrorResponse(
                HttpStatusCode.InternalServerError
                , "A " + context.Exception.GetType().ToString() + " was thrown"
            );
        }
        base.OnException(context);
    }
}
在 App_Start 中正确添加了过滤器:
config.Filters.Add(new SecureVideoApiGlobalExceptionHandler());
当出现 WebApi 管道之外的错误时,问题就出现了。例如,请求带有 Web 根的 URI https://mysite.com/,响应是 403.11,带有 HTML 正文,说明“Web 服务器配置为不列出此目录的内容”。另一种可能性是 global.asax 的 Application_Start 方法中调用的代码中存在错误,例如在 AutoMapper 代码中,该代码断言所有映射都有效。
我的问题是:我怎样才能做到当 API 服务器上发生任何错误时,只返回 JSON 错误消息,而不返回 HTML 错误消息?
我试过了
<modules runAllManagedModulesForAllRequests="true">
这允许我处理 global.asax 中 Application_Error() 中的任何错误,但我无法访问那里的 Response 对象以发出 JSON。
你可以做类似这个属性的事情
[AttributeUsageAttribute(AttributeTargets.All, Inherited = true, AllowMultiple = true)]
public class ExceptionActionFilter : ExceptionFilterAttribute
{
    private static Logger _log ;//= LogManager.GetLogger("mysite");
    public override void OnException(HttpActionExecutedContext contex)
    {
        if (_log == null)
            _log = LogManager.GetCurrentClassLogger();
        var ex = contex.Exception;
        _log.Error(ex);
        contex.Response = contex.Request.CreateResponse(HttpStatusCode.OK,
           new 
           {
               ErrorMessage = contex.Exception.Message,
               RealStatusCode = (int)(ex is NotImplementedException || ex is ArgumentNullException ? HttpStatusCode.NoContent : HttpStatusCode.BadRequest), 
               ReturnUrl = CommonContext.ErrorUrl
           },
           new JsonMediaTypeFormatter());
        base.OnException(contex);
    }      
}
然后将属性放在类 exc 和客户端上
或带过滤器
 public class ExceptionLoggerFilter : System.Web.Http.Filters.IExceptionFilter
{
    private static Logger _log;
    public ExceptionLoggerFilter()
    {
        if (_log == null)
            _log = LogManager.GetCurrentClassLogger();
    }
    public bool AllowMultiple { get { return true; } }
    public System.Threading.Tasks.Task ExecuteExceptionFilterAsync(
            System.Web.Http.Filters.HttpActionExecutedContext contex,
            System.Threading.CancellationToken cancellationToken)
    {
        return System.Threading.Tasks.Task.Factory.StartNew(() =>
        {
            _log.Error(contex.Exception);
            contex.Response = contex.Request.CreateResponse(HttpStatusCode.OK, 
                new { RealStatusCode = (int)HttpStatusCode.Forbidden, ReturnUrl = "#/error.html"},
                contex.ActionContext.ControllerContext.Configuration.Formatters.JsonFormatter);
        }, cancellationToken);
    }
}
然后在 Global.asax.cs
protected void Application_Start()
{
    GlobalConfiguration.Configure(WebApiConfig.Register);
    Database.SetInitializer<MySiteContext>(null);
}
和
public static class WebApiConfig
{
    public static void Register(HttpConfiguration config)//RouteCollection config)//
    {
        config.Formatters.JsonFormatter.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore;
        config.Filters.Add(new ExceptionLoggerFilter());
        // Web API routes
        config.MapHttpAttributeRoutes();
        config.Routes.MapHttpRoute(
            name: "DefaultApi",
            routeTemplate: "api/{controller}/{action}/{id}",
            defaults: new { id = RouteParameter.Optional });
    }
}
在客户端,也许有点像
$.controller.ajax.promise = controller.ajax.promise = function ( obj )
{
    var deferred = q.defer();
    $.ajax( {
        type: obj.type || "GET",
        url: obj.url,
        context: obj.context || null,
        data: obj.data || null,
        contentType: obj.contentType || "application/json; charset=utf-8",
        dataType: obj.dataType || "json",
        success: function ( res, textStatus, jqXHR )
        {
            if ( res.RealStatusCode )
            {
                switch ( res.RealStatusCode )
                {
                    case 400://x error
                        res.ClientMessage = res.ErrorMessage;
                        deferred.reject(res);
                        break;
                    case 408://y errors
                        location.href = res.ReturnUrl;
                        return false;
                    case 403://ext
                        msgbox.alert( {
                            message: 'Ma belle msg',
                            title: "Error"
                        } );
                        deferred.reject();
                        location.href = res.ReturnUrl;
                        return false;
                    default:
                        deferred.reject();
                        location.href = res.ReturnUrl;
                        break;
                }
            }
            deferred.resolve( res );
            return true;
        },
        error: function ( jqXHR, textStatus, errorThrown )
        {
            deferred.reject( { msg: jqXHR.statusText, jqXHR: jqXHR, textStatus:textStatus, errorThrown:errorThrown } );
        }
    } );
    return deferred.promise;
};
我希望这可以帮助其他谷歌员工!(就像@Thomas CG de Vilhena 说的 =)
| 归档时间: | 
 | 
| 查看次数: | 2497 次 | 
| 最近记录: |