本文介绍 ASP.NET Web API 中的错误和异常处理。
HttpResponseException
如果 Web API 控制器引发未捕获的异常,会发生什么情况? 默认情况下,大多数异常都转换为状态代码为 500“内部服务器错误”的 HTTP 响应。
HttpResponseException 类型是一种特殊情况。 此异常返回在异常构造函数中指定的任何 HTTP 状态代码。 例如,如果 ID 参数无效,则以下方法返回 404“找不到”。
public Product GetProduct(int id)
{
Product item = repository.Get(id);
if (item == null)
{
throw new HttpResponseException(HttpStatusCode.NotFound);
}
return item;
}
若要更好地控制响应,还可以构造整个响应消息并将其包含在 HttpResponseException 中:
public Product GetProduct(int id)
{
Product item = repository.Get(id);
if (item == null)
{
var resp = new HttpResponseMessage(HttpStatusCode.NotFound)
{
Content = new StringContent(string.Format("No product with ID = {0}", id)),
ReasonPhrase = "Product ID Not Found"
};
throw new HttpResponseException(resp);
}
return item;
}
异常筛选器
可以通过编写 异常筛选器来自定义 Web API 如何处理异常。 当控制器方法引发任何未经处理的异常,若不是HttpResponseException异常时,将执行异常筛选器。 HttpResponseException 类型是一种特殊情况,因为它专为返回 HTTP 响应而设计。
异常筛选器实现 System.Web.Http.Filters.IExceptionFilter 接口。 编写异常筛选器的最简单方法是从 System.Web.Http.Filters.ExceptionFilterAttribute 类派生并重写 OnException 方法。
注释
ASP.NET Web API 中的异常筛选器类似于 ASP.NET MVC 中的筛选器。 但是,它们分别在单独的命名空间和函数中声明。 具体而言,MVC 中使用的 HandleErrorAttribute 类不会处理 Web API 控制器引发的异常。
这是一个将 NotImplementedException 异常转换为 HTTP 状态代码 501(未实现)的筛选器。
namespace ProductStore.Filters
{
using System;
using System.Net;
using System.Net.Http;
using System.Web.Http.Filters;
public class NotImplExceptionFilterAttribute : ExceptionFilterAttribute
{
public override void OnException(HttpActionExecutedContext context)
{
if (context.Exception is NotImplementedException)
{
context.Response = new HttpResponseMessage(HttpStatusCode.NotImplemented);
}
}
}
}
HttpActionExecutedContext 对象的 Response 属性包含将发送到客户端的 HTTP 响应消息。
注册异常过滤器
可通过多种方式注册 Web API 异常筛选器:
- 按操作
- 通过控制器
- 全球
若要将筛选器应用于特定操作,请将筛选器添加为操作的属性:
public class ProductsController : ApiController
{
[NotImplExceptionFilter]
public Contact GetContact(int id)
{
throw new NotImplementedException("This method is not implemented");
}
}
若要将筛选器应用于控制器上的所有操作,请将筛选器作为属性添加到控制器类:
[NotImplExceptionFilter]
public class ProductsController : ApiController
{
// ...
}
若要将筛选器全局应用到所有 Web API 控制器,请将筛选器的实例添加到 GlobalConfiguration.Configuration.Filters 集合。 此集合中的异常筛选器适用于任何 Web API 控制器作。
GlobalConfiguration.Configuration.Filters.Add(
new ProductStore.NotImplExceptionFilterAttribute());
如果使用“ASP.NET MVC 4 Web 应用程序”项目模板创建项目,请将 Web API 配置代码放入位于App_Start文件夹中的类中 WebApiConfig :
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
config.Filters.Add(new ProductStore.NotImplExceptionFilterAttribute());
// Other configuration code...
}
}
HttpError
HttpError 对象提供一致的方式来返回响应正文中的错误信息。 以下示例演示如何在响应正文中使用 HttpError 返回 HTTP 状态代码 404(未找到)。
public HttpResponseMessage GetProduct(int id)
{
Product item = repository.Get(id);
if (item == null)
{
var message = string.Format("Product with id = {0} not found", id);
return Request.CreateErrorResponse(HttpStatusCode.NotFound, message);
}
else
{
return Request.CreateResponse(HttpStatusCode.OK, item);
}
}
CreateErrorResponse 是在 System.Net.Http.HttpRequestMessageExtensions 类中定义的扩展方法。 在内部,CreateErrorResponse 创建一个 HttpError 实例,然后创建一个包含该 HttpError 的 HttpResponseMessage。
在此示例中,如果方法成功,则会在 HTTP 响应中返回产品。 但如果找不到请求的产品,HTTP 响应在请求正文中包含 HttpError 。 响应可能如下所示:
HTTP/1.1 404 Not Found
Content-Type: application/json; charset=utf-8
Date: Thu, 09 Aug 2012 23:27:18 GMT
Content-Length: 51
{
"Message": "Product with id = 12 not found"
}
请注意,在此示例中, HttpError 已序列化为 JSON。 使用 HttpError 的一个优点是,它经历了与任何其他强类型模型相同的 内容协商 和序列化过程。
HttpError 和模型验证
对于模型验证,可以将模型状态传递给 CreateErrorResponse,以在响应中包含验证错误:
public HttpResponseMessage PostProduct(Product item)
{
if (!ModelState.IsValid)
{
return Request.CreateErrorResponse(HttpStatusCode.BadRequest, ModelState);
}
// Implementation not shown...
}
此示例可能返回以下响应:
HTTP/1.1 400 Bad Request
Content-Type: application/json; charset=utf-8
Content-Length: 320
{
"Message": "The request is invalid.",
"ModelState": {
"item": [
"Required property 'Name' not found in JSON. Path '', line 1, position 14."
],
"item.Name": [
"The Name field is required."
],
"item.Price": [
"The field Price must be between 0 and 999."
]
}
}
有关模型验证的详细信息,请参阅 ASP.NET Web API 中的模型验证。
将 HttpError 与 HttpResponseException 配合使用
前面的示例从控制器操作返回 HttpResponseMessage 消息,但也可以使用 HttpResponseException 返回 HttpError。 这样就可以在正常成功的情况下返回强类型模型,如果出现错误,仍返回 HttpError :
public Product GetProduct(int id)
{
Product item = repository.Get(id);
if (item == null)
{
var message = string.Format("Product with id = {0} not found", id);
throw new HttpResponseException(
Request.CreateErrorResponse(HttpStatusCode.NotFound, message));
}
else
{
return item;
}
}