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 中使用的system.web.mvc.handleerrorattribute class類別不會處理 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. 下列範例示範如何在回應主體中傳回 HTTP 狀態碼404(找不到)和HttpErrorThe 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 API 中的模型驗證For more information about model validation, see Model Validation in ASP.NET Web API.

搭配使用 HttpError 與 HttpResponseExceptionUsing 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;
    }
}