如何覆盖所有的WebAPI标准错误页面 [英] How to override all standard error pages in WebAPI

查看:691
本文介绍了如何覆盖所有的WebAPI标准错误页面的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我的美丽REST Web服务的伟大工程。但如果我访问像〜/ 页面,返回默认的IIS 403禁止页(甚至使用招,只指定接受:应用/ JSON )。我想无非是JSON或XML错误。有没有一种方法来覆盖一个自定义异常处理所有异常?或默认控制器来处理所有未知的请求?什么是最简单的,最正确的(如果不同),方法来处理这​​使客户只需要解析REST API友好的XML数据报或JSON的斑点?

示例请求:

  GET HTTP://本地主机:7414 / HTTP / 1.1
用户代理:提琴手
主机:本地主机:7414
接受:应用/ JSON,文本/ JSON,文本/ XML

响应:(我不喜欢,请注意,的text / html 不是公认的反应类型之一)

  HTTP / 1.1 403禁止
缓存控制:私人
内容类型:text / html的;字符集= UTF-8
服务器:Microsoft-IIS / 8.0
的X来源档案:????= UTF-的8B QzpcaWNhcm9sXENoYXJpdHlMb2dpYy5pQ2Fyb2wuQXBp =
的X已启动方式:ASP.NET
日期:星期五,2013年1月25日21时06分21秒GMT
内容长度:5396!< D​​OCTYPE HTML PUBLIC - // W3C // DTD XHTML 1.0 Strict标准// ENhttp://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">
< HTML的xmlns =htt​​p://www.w3.org/1999/xhtml>
< HEAD>
<标题> IIS 8.0的详细错误 - 403.14 - 禁止< /标题>
<风格类型=文/ CSS>
<! -
...

响应(我将preFER):

  HTTP / 1.1 403禁止
缓存控制:私人
内容类型:应用程序/ JSON的;字符集= UTF-8
日期:...
内容长度:....{
  错误:禁止,
  状态:403,
  ERROR_DESCRIPTION:目录列表不允许的。
}


解决方案

修改14年1月26日:微软刚刚加入的全局错误处理到最新的WebAPI 2.1更新。


好吧,我想我已经得到了它。有几件吧。

第一:创建你的错误的控制器。我根据HTTP错误codeS命名我的行动。

公共类ErrorController:ApiController {
    [使用AllowAnonymous]
    [ActionName(获取)]
    公众的Htt presponseMessage的get(){
        返回Request.CreateErrorInfoResponse(的HTTPStatus code.InternalServerError,标题:未知错误);
    }    [使用AllowAnonymous]
    [ActionName(404)]
    [HTTPGET]
    公众的Htt presponseMessage Status404(){
        返回Request.CreateErrorInfoResponse(的HTTPStatus code.NotFound,说明:没有资源指定的URL匹配。);
    }    [使用AllowAnonymous]
    [ActionName(400)]
    [HTTPGET]
    公众的Htt presponseMessage Status400(){
        返回Request.CreateErrorInfoResponse(的HTTPStatus code.BadRequest);
    }    [使用AllowAnonymous]
    [ActionName(500)]
    [HTTPGET]
    公众的Htt presponseMessage Status500(){
        返回Request.CreateErrorInfoResponse(的HTTPStatus code.InternalServerError);
    }
}

接下来,我创建了一个 GenericExceptionFilterAttribute 检查,看看是否HttpActionExecutedContext.Exception填充,如果反应仍然是空的。如果这两种情况属实,那么它会产生一个响应。

公共类GenericExceptionFilterAttribute:ExceptionFilterAttribute {
    公共GenericExceptionFilterAttribute()
        :碱(){
        的DefaultHandler =(背景下,EX)=> context.Request.CreateErrorInfoResponse(System.Net.HttpStatus code.InternalServerError,内部服务器错误,在服务器上occoured一个unepected错误,例外:EX);
    }    只读字典<类型,函数功能与LT; HttpActionExecutedContext,异常的Htt presponseMessage>>的ExceptionHandlers =新词典<类型,函数功能与LT; HttpActionExecutedContext,异常的Htt presponseMessage>>();    公共Func键< HttpActionExecutedContext,异常的Htt presponseMessage> {DefaultHandler的获取;组; }    公共无效AddExceptionHandler< T>(Func键< HttpActionExecutedContext,异常的Htt presponseMessage>处理)其中T:异常{
        exceptionHandlers.Add(typeof运算(T),处理程序);
    }    公共覆盖无效onException的(HttpActionExecutedContext上下文){
        如果(context.Exception == NULL)回报;        尝试{
            变种exType = context.Exception.GetType();
            如果(exceptionHandlers.ContainsKey(exType))
                context.Response =的ExceptionHandlers [exType](背景下,context.Exception);            如果(context.Response == NULL和放大器;&安培;!DefaultHandler的= NULL)
                context.Response =的DefaultHandler(背景下,context.Exception);
        }
        赶上(例外前){
            context.Response = context.Request.CreateErrorInfoResponse(的HTTPStatus code.InternalServerError,说明:在构建异常响应错误,例外:EX);
        }
    }
}

在我的情况,我就用一个通用的处理程序,我可以支持注册为每个主要的例外类型和地图的每一个异常类型特定的HTTP响应codeS的。现在全球范围内注册您的异常类型和处理此过滤器在你的的global.asax.cs

//这些过滤器覆盖默认ASP.NET异常处理创建REST友好的错误响应。
VAR exceptionFormatter =新GenericExceptionFilterAttribute();
exceptionFormatter.AddExceptionHandler< NotImplementedException>((背景下,EX)=方式> context.Request.CreateErrorInfoResponse(System.Net.HttpStatus code.InternalServerError,没有实现,这个方法还没有得到落实,请你试试在稍后的日期再次请求,例外:EX));
exceptionFormatter.AddExceptionHandler< ArgumentException的>((背景下,EX)=> context.Request.CreateErrorInfoResponse(System.Net.HttpStatus code.BadRequest,例外:EX));
exceptionFormatter.AddExceptionHandler< ArgumentNullException>((背景下,EX)=> context.Request.CreateErrorInfoResponse(System.Net.HttpStatus code.BadRequest,例外:EX));
exceptionFormatter.AddExceptionHandler< ArgumentOutOfRangeException>((背景下,EX)=> context.Request.CreateErrorInfoResponse(System.Net.HttpStatus code.BadRequest,例外:EX));
exceptionFormatter.AddExceptionHandler< FormatException>((背景下,EX)=> context.Request.CreateErrorInfoResponse(System.Net.HttpStatus code.BadRequest,例外:EX));
exceptionFormatter.AddExceptionHandler<&NotSupportedException异常GT;((背景下,EX)=> context.Request.CreateErrorInfoResponse(System.Net.HttpStatus code.BadRequest,不支持,例外:EX));
exceptionFormatter.AddExceptionHandler<&InvalidOperationException异常GT;((背景下,EX)=> context.Request.CreateErrorInfoResponse(System.Net.HttpStatus code.BadRequest,无效的操作,例外:EX));
GlobalConfiguration.Filters.Add(exceptionFormatter)

接下来,创建一个包罗万象的路线所有未知请求发送到您的新错误处理程序:

config.Routes.MapHttpRoute(
    名称:DefaultCatchall
    routeTemplate:{* URL},
    默认:新{
        控制器=错误
        行动=404
    }
);

和,包了这一切,让IIS进程通过ASP.NET所有请求加入给你的的web.config

<结构>
    < system.webServer>
        <模块runAllManagedModulesForAllRequests =真/>
    < /system.webServer>
< /结构>

另外,您还可以使用的web.config 的customErrors 部分所有的错误重定向到新错误处理程序。

My beautiful REST webservice works great. Except if I visit pages like ~/, which returns the default IIS 403 Forbidden page (even using Fiddler and specifying only Accept: application/json). I want nothing but JSON or XML errors. Is there a way to override ALL exceptions with a custom exception handler? or a default controller to handle all unknown requests? What's the simplest, and the most correct (if different), way to handle this so that clients need only parse REST API-friendly XML datagrams or JSON blobs?

Example Request:

GET http://localhost:7414/ HTTP/1.1
User-Agent: Fiddler
Host: localhost:7414
Accept: application/json, text/json, text/xml

Response: (that I don't like, notice that text/html wasn't one of the accepted response types)

HTTP/1.1 403 Forbidden
Cache-Control: private
Content-Type: text/html; charset=utf-8
Server: Microsoft-IIS/8.0
X-SourceFiles: =?UTF-8?B?QzpcaWNhcm9sXENoYXJpdHlMb2dpYy5pQ2Fyb2wuQXBp?=
X-Powered-By: ASP.NET
Date: Fri, 25 Jan 2013 21:06:21 GMT
Content-Length: 5396

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> 
<html xmlns="http://www.w3.org/1999/xhtml"> 
<head> 
<title>IIS 8.0 Detailed Error - 403.14 - Forbidden</title> 
<style type="text/css"> 
<!-- 
...

Response (that I would prefer):

HTTP/1.1 403 Forbidden
Cache-Control: private
Content-Type: application/json; charset=utf-8
Date: ...
Content-Length: ....

{
  "error":"forbidden",
  "status":403,
  "error_description":"Directory listing not allowed."
}

解决方案

Edit 1/26/14: Microsoft just added "Global Error Handling" to the latest WebAPI 2.1 update.


Ok, I think I've got it. There's a few parts to it.

First: Create a controller for your errors. I named my actions according to the HTTP error codes.

public class ErrorController : ApiController {
    [AllowAnonymous]
    [ActionName("Get")]
    public HttpResponseMessage Get() {
        return Request.CreateErrorInfoResponse(HttpStatusCode.InternalServerError, title: "Unknown Error");
    }

    [AllowAnonymous]
    [ActionName("404")]
    [HttpGet]
    public HttpResponseMessage Status404() {
        return Request.CreateErrorInfoResponse(HttpStatusCode.NotFound, description: "No resource matches the URL specified.");
    }

    [AllowAnonymous]
    [ActionName("400")]
    [HttpGet]
    public HttpResponseMessage Status400() {
        return Request.CreateErrorInfoResponse(HttpStatusCode.BadRequest);
    }

    [AllowAnonymous]
    [ActionName("500")]
    [HttpGet]
    public HttpResponseMessage Status500() {
        return Request.CreateErrorInfoResponse(HttpStatusCode.InternalServerError);
    }
}

Next, I created a GenericExceptionFilterAttribute that checks to see if the HttpActionExecutedContext.Exception is populated and if the response is still empty. If both cases are true, then it generates a response.

public class GenericExceptionFilterAttribute : ExceptionFilterAttribute {
    public GenericExceptionFilterAttribute()
        : base() {
        DefaultHandler = (context, ex) => context.Request.CreateErrorInfoResponse(System.Net.HttpStatusCode.InternalServerError, "Internal Server Error", "An unepected error occoured on the server.", exception: ex);
    }

    readonly Dictionary<Type, Func<HttpActionExecutedContext, Exception, HttpResponseMessage>> exceptionHandlers = new Dictionary<Type, Func<HttpActionExecutedContext, Exception, HttpResponseMessage>>();

    public Func<HttpActionExecutedContext, Exception, HttpResponseMessage> DefaultHandler { get; set; }

    public void AddExceptionHandler<T>(Func<HttpActionExecutedContext, Exception, HttpResponseMessage> handler) where T : Exception {
        exceptionHandlers.Add(typeof(T), handler);
    }

    public override void OnException(HttpActionExecutedContext context) {
        if (context.Exception == null) return;

        try {
            var exType = context.Exception.GetType();
            if (exceptionHandlers.ContainsKey(exType))
                context.Response = exceptionHandlers[exType](context, context.Exception);

            if(context.Response == null && DefaultHandler != null)
                context.Response = DefaultHandler(context, context.Exception);
        }
        catch (Exception ex) {
            context.Response = context.Request.CreateErrorInfoResponse(HttpStatusCode.InternalServerError, description: "Error while building the exception response.", exception: ex);
        }
    }
}

In my case, I went with a single generic handler that I could register support for each of the main exception types and map each of those exception types to specific HTTP response codes. Now register your exception types and handlers this filter globally in your global.asax.cs:

// These filters override the default ASP.NET exception handling to create REST-Friendly error responses.
var exceptionFormatter = new GenericExceptionFilterAttribute();
exceptionFormatter.AddExceptionHandler<NotImplementedException>((context, ex) => context.Request.CreateErrorInfoResponse(System.Net.HttpStatusCode.InternalServerError, "Not Implemented", "This method has not yet been implemented. Please try your request again at a later date.", exception: ex));
exceptionFormatter.AddExceptionHandler<ArgumentException>((context, ex) => context.Request.CreateErrorInfoResponse(System.Net.HttpStatusCode.BadRequest, exception: ex));
exceptionFormatter.AddExceptionHandler<ArgumentNullException>((context, ex) => context.Request.CreateErrorInfoResponse(System.Net.HttpStatusCode.BadRequest, exception: ex));
exceptionFormatter.AddExceptionHandler<ArgumentOutOfRangeException>((context, ex) => context.Request.CreateErrorInfoResponse(System.Net.HttpStatusCode.BadRequest, exception: ex));
exceptionFormatter.AddExceptionHandler<FormatException>((context, ex) => context.Request.CreateErrorInfoResponse(System.Net.HttpStatusCode.BadRequest, exception: ex));
exceptionFormatter.AddExceptionHandler<NotSupportedException>((context, ex) => context.Request.CreateErrorInfoResponse(System.Net.HttpStatusCode.BadRequest, "Not Supported", exception: ex));
exceptionFormatter.AddExceptionHandler<InvalidOperationException>((context, ex) => context.Request.CreateErrorInfoResponse(System.Net.HttpStatusCode.BadRequest, "Invalid Operation", exception: ex));
GlobalConfiguration.Filters.Add(exceptionFormatter)

Next, create a catchall route to send all unknown requests to your new Error handler:

config.Routes.MapHttpRoute(
    name: "DefaultCatchall",
    routeTemplate: "{*url}",
    defaults: new {
        controller = "Error",
        action = "404"
    }
);

And, to wrap it all up, let IIS process all requests through ASP.NET by adding this to your web.config:

<configuration>
    <system.webServer>
        <modules runAllManagedModulesForAllRequests="true" />
    </system.webServer>
</configuration>

Optionally, you could also use the customErrors section of the web.config to redirect all errors to your new error handler.

这篇关于如何覆盖所有的WebAPI标准错误页面的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆