[翻译] ASP.NET WebAPI 中的异常处理
原文链接:https://docs.microsoft.com/en-us/aspnet/web-api/overview/error-handling/exception-handling
本文介绍了在 ASP.NET Web API 中的错误和异常处理
- HttpResponseException
- Exception Filters
- Registering Exception Filters
- HttpError
HttpResponseMessage
如果一个 Web API 控制器抛出一个未处理的异常会发生什么?默认情况,大多数异常会被转换为 HTTP 响应,状态码为500(服务器内部错误)。
HttpResponseMessage 类型是一种特殊情况。该异常返回异常构造函数中指定的任何 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 响应。
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 **属性包含将被发送给客户端的响应消息。
注册异常过滤器
有集中方法注册 Web API 异常过滤器:
- 通过 Action
- 通过 Controller
- 全局
为指定的 Action 应用过滤器:
public class ProductsController : ApiController
{
[NotImplExceptionFilter]
public Contact GetContact(int id)
{
throw new NotImplementedException("This method is not implemented");
}
}
为 Controller 的所有 Action应用过滤器:
[NotImplExceptionFilter]
public class ProductsController : ApiController
{
// ...
}
为全局所有的 Web API Controller 应用过滤器,要在 GlobalConfiguration.Configuration.Filters 集合中添加一个过滤器实例。 在这个集合中的异常过滤器应用到任何 Web API Controller 的 Action:
GlobalConfiguration.Configuration.Filters.Add(
new ProductStore.NotImplExceptionFilterAttribute());
如果你是用 "ASP.NET MVC 4 Web Application" 项目模版创建的项目,把 Web API 配置代码放在 WebApiConfig
类中,该类位于 App_Start 文件夹里:
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 and Model Validation
对模型验证来说,你可以把模型状态传给 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."
]
}
}
更多关于模型绑定的信息,请参见Model Validation in ASP.NET Web API。
和 HttpResponseException 一起使用 HttpError
前面的示例,从控制器的 Action 返回一个 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;
}
}