我漂亮的REST网络服务很棒。除非我访问~/
之类的页面,它返回默认的IIS 403 Forbidden页面(即使使用Fiddler并仅指定Accept: application/json
)。我只想要JSON或XML错误。有没有办法用自定义异常处理程序覆盖所有异常?或者一个默认的控制器来处理所有未知请求?什么是最简单,最正确(如果不同)的方法来处理这个问题,以便客户端只需要解析REST API友好的XML数据报或JSON blob?
示例请求:
GET http://localhost:7414/ HTTP/1.1
User-Agent: Fiddler
Host: localhost:7414
Accept: application/json, text/json, text/xml
回复:(我不喜欢,请注意text/html
不是可接受的响应类型之一)
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">
<!--
...
回复(我更喜欢):
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."
}
答案 0 :(得分:11)
修改1/26/14:微软刚刚添加了&#34; Global Error Handling&#34;到最新的WebAPI 2.1更新。
好的,我想我已经知道了。它有几个部分。
首先:为您的错误创建一个控制器。我根据HTTP错误代码命名了我的操作。
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);
}
}
接下来,我创建了一个GenericExceptionFilterAttribute
,它检查是否填充了HttpActionExecutedContext.Exception以及响应是否仍为空。如果两种情况都属实,则会生成响应。
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);
}
}
}
就我而言,我选择了一个通用处理程序,我可以为每个主要的异常类型注册支持,并将每个异常类型映射到特定的HTTP响应代码。现在,在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)
接下来,创建一个catchall路由,将所有未知请求发送到新的Error处理程序:
config.Routes.MapHttpRoute(
name: "DefaultCatchall",
routeTemplate: "{*url}",
defaults: new {
controller = "Error",
action = "404"
}
);
并且,为了将其全部包装起来,让IIS通过将其添加到您的web.config
来处理所有请求:
<configuration>
<system.webServer>
<modules runAllManagedModulesForAllRequests="true" />
</system.webServer>
</configuration>
或者,您也可以使用customErrors
的{{1}}部分将所有错误重定向到新的错误处理程序。
答案 1 :(得分:0)
在IIS管理器中,您可以edit the custom errors:
打开IIS管理器并导航到要管理的级别。对于 有关打开IIS管理器的信息,请参阅打开IIS管理器(IIS 7)。 有关在UI中导航到位置的信息,请参阅 在IIS管理器(IIS 7)中导航。
在“功能视图”中,双击“错误页面”。
在“错误页面”页面上,单击以选择要更改的错误。
在“操作”窗格中,单击“编辑”。
在“编辑自定义错误页面”对话框中,选择以下选项之一: 如果您的错误,请将静态文件中的内容插入错误响应中 内容是静态的,例如.html文件。
如果您的错误内容是动态的,请在此网站上执行网址,例如 一个.asp文件。
如果要重定向客户端浏览器,则使用302重定向进行响应 到不同的网址。
在“文件路径”文本框中,键入自定义错误页面的路径 将静态文件中的内容插入到错误响应中 路径类型。如果使用此站点上的“执行URL”或“响应” 使用302重定向路径类型,键入自定义的URL 错误页面。单击“确定”。
另请参阅Rick Strahl's writeup,其中包含一些屏幕截图。
但是,我不认为这会解决响应中的内容类型标题 - 它只是概述了如何更改内容部分,您可以将其更改为JSON格式。我不知道如何在不做更多自定义的情况下改变它,但是如果内容仍然是JSON,一些客户端将能够处理错误的内容类型,所以这可能就足够了(如果是这样,可能是最简单的选项)。所以这不是一个完整的答案,但它可能对你有帮助。
还有其他更多代码密集型选项 - 如自定义HTTP模块或使用服务器端代码/配置(如.Net)来处理所有请求并构建正确的响应+标头(请参阅ASP.NET rewritten custom errors do not send content-type header或http://www.iis.net/learn/develop/runtime-extensibility/developing-iis-modules-and-handlers-with-the-net-framework)。
答案 2 :(得分:0)