ASP.NET Web API 中的异常处理Exception Handling in ASP.NET Web API

作者: Mike Wassonby Mike Wasson

本文介绍了 ASP.NET Web API 中的错误和异常处理。This article describes error and exception handling in ASP.NET Web API.

HttpResponseExceptionHttpResponseException

如果 Web API 控制器引发未捕获的异常,会发生什么情况?What happens if a Web API controller throws an uncaught exception? 默认情况下,大多数异常都转换为 HTTP 响应,状态代码为500,内部服务器错误。By default, most exceptions are translated into an HTTP response with status code 500, Internal Server Error.

带有 httpresponseexception类型是一种特殊情况。The HttpResponseException type is a special case. 此异常返回在异常构造函数中指定的任何 HTTP 状态代码。This exception returns any HTTP status code that you specify in the exception constructor. 例如,如果id参数无效,则以下方法返回404,但找不到。For example, the following method returns 404, Not Found, if the id parameter is not valid.

public Product GetProduct(int id)
{
    Product item = repository.Get(id);
    if (item == null)
    {
        throw new HttpResponseException(HttpStatusCode.NotFound);
    }
    return item;
}

为了更好地控制响应,你还可以构造整个响应消息,并将其包含在带有 httpresponseexception 中:For more control over the response, you can also construct the entire response message and include it with the 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;
}

异常筛选器Exception Filters

可以通过编写异常筛选器来自定义 Web API 处理异常的方式。You can customize how Web API handles exceptions by writing an exception filter. 当控制器方法引发带有 httpresponseexception异常的任何未经处理的异常时,将执行异常筛选器。An exception filter is executed when a controller method throws any unhandled exception that is not an HttpResponseException exception. 带有 httpresponseexception类型是一种特殊情况,因为它专用于返回 HTTP 响应。The HttpResponseException type is a special case, because it is designed specifically for returning an HTTP response.

异常筛选器实现了IExceptionFilter接口。Exception filters implement the System.Web.Http.Filters.IExceptionFilter interface. 编写异常筛选器的最简单方法是从ExceptionFilterAttribute类派生,并重写OnException方法。The simplest way to write an exception filter is to derive from the System.Web.Http.Filters.ExceptionFilterAttribute class and override the OnException method.

Note

ASP.NET Web API 中的异常筛选器类似于 ASP.NET MVC 中的筛选器。Exception filters in ASP.NET Web API are similar to those in ASP.NET MVC. 但是,它们分别在单独的命名空间和函数中声明。However, they are declared in a separate namespace and function separately. 特别是,MVC 中使用的HandleErrorAttribute类不处理 Web API 控制器引发的异常。In particular, the HandleErrorAttribute class used in MVC does not handle exceptions thrown by Web API controllers.

下面的筛选器将NotImplementedException异常转换为 HTTP 状态代码501,而未实现:Here is a filter that converts NotImplementedException exceptions into HTTP status code 501, Not Implemented:

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 响应消息。The Response property of the HttpActionExecutedContext object contains the HTTP response message that will be sent to the client.

注册异常筛选器Registering Exception Filters

可通过多种方法注册 Web API 异常筛选器:There are several ways to register a Web API exception filter:

  • 按操作By action
  • 按控制器By controller
  • 全局Globally

要将筛选器应用到特定的操作,请将筛选器作为特性添加到该操作:To apply the filter to a specific action, add the filter as an attribute to the action:

public class ProductsController : ApiController
{
    [NotImplExceptionFilter]
    public Contact GetContact(int id)
    {
        throw new NotImplementedException("This method is not implemented");
    }
}

若要将筛选器应用于控制器上的所有操作,请将筛选器作为属性添加到控制器类:To apply the filter to all of the actions on a controller, add the filter as an attribute to the controller class:

[NotImplExceptionFilter]
public class ProductsController : ApiController
{
    // ...
}

若要将筛选器全局应用到所有 Web API 控制器,请将筛选器的实例添加到GlobalConfiguration集合。To apply the filter globally to all Web API controllers, add an instance of the filter to the GlobalConfiguration.Configuration.Filters collection. 此集合中的异常筛选器将应用到任何 Web API 控制器操作。Exception filters in this collection apply to any Web API controller action.

GlobalConfiguration.Configuration.Filters.Add(
    new ProductStore.NotImplExceptionFilterAttribute());

如果使用 "ASP.NET MVC 4 Web 应用程序" 项目模板来创建项目,请将 Web API 配置代码置于 WebApiConfig 类中,该代码位于应用_启动文件夹中:If you use the "ASP.NET MVC 4 Web Application" project template to create your project, put your Web API configuration code inside the WebApiConfig class, which is located in the App_Start folder:

public static class WebApiConfig
{
    public static void Register(HttpConfiguration config)
    {
        config.Filters.Add(new ProductStore.NotImplExceptionFilterAttribute());

        // Other configuration code...
    }
}

HttpErrorHttpError

HttpError对象提供了一种一致的方式来在响应正文中返回错误信息。The HttpError object provides a consistent way to return error information in the response body. 下面的示例演示如何在响应正文中返回包含HttpError的 HTTP 状态代码404(未找到)。The following example shows how to return HTTP status code 404 (Not Found) with an HttpError in the response body.

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是在HttpRequestMessageExtensions类中定义的扩展方法。CreateErrorResponse is an extension method defined in the System.Net.Http.HttpRequestMessageExtensions class. 在内部, CreateErrorResponse创建一个HttpError实例,然后创建一个包含HttpErrorHttpResponseMessageInternally, CreateErrorResponse creates an HttpError instance and then creates an HttpResponseMessage that contains the HttpError.

在此示例中,如果方法成功,它将在 HTTP 响应中返回产品。In this example, if the method is successful, it returns the product in the HTTP response. 但如果找不到请求的产品,HTTP 响应会在请求正文中包含HttpErrorBut if the requested product is not found, the HTTP response contains an HttpError in the request body. 响应可能如下所示:The response might look like the following:

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。Notice that the HttpError was serialized to JSON in this example. 使用HttpError的一个优点是,它与任何其他强类型模型进行相同的内容协商和序列化过程。One advantage of using HttpError is that it goes through the same content-negotiation and serialization process as any other strongly-typed model.

HttpError 和模型验证HttpError and Model Validation

对于模型验证,可以将模型状态传递到CreateErrorResponse,以便在响应中包括验证错误:For model validation, you can pass the model state to CreateErrorResponse, to include the validation errors in the response:

public HttpResponseMessage PostProduct(Product item)
{
    if (!ModelState.IsValid)
    {
        return Request.CreateErrorResponse(HttpStatusCode.BadRequest, ModelState);
    }

    // Implementation not shown...
}

此示例可能返回以下响应:This example might return the following response:

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 APIFor more information about model validation, see Model Validation in ASP.NET Web API.

将 HttpError 与带有 httpresponseexception 配合使用Using HttpError with HttpResponseException

前面的示例从控制器操作返回HttpResponseMessage消息,但也可以使用带有 httpresponseexception返回HttpErrorThe previous examples return an HttpResponseMessage message from the controller action, but you can also use HttpResponseException to return an HttpError. 这使您可以在正常的成功情况下返回强类型化的模型,同时在出现错误时仍会返回HttpErrorThis lets you return a strongly-typed model in the normal success case, while still returning HttpError if there is an error:

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;
    }
}