共用方式為


ASP.NET Web API 中的例外狀況處理

本文說明 ASP.NET Web API 中的錯誤和例外狀況處理。

HttpResponseException

如果 Web API 控制器擲回未攔截到的例外狀況,會發生什麼事? 根據預設,大部分例外狀況都會轉譯為 HTTP 回應,狀態碼為 500,內部伺服器錯誤。

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 物件的回應屬性包含將傳送到客戶端的 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 設定程式碼放入 WebApiConfig 類別中,該類別位於 App_Start 資料夾中:

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

        // Other configuration code...
    }
}

HttpError

HttpError 物件提供一致的方式來傳回回應本文中的錯誤資訊。 以下範例示範如何在回應本文中傳回 HTTP 狀態碼 404 (找不到) 和 HttpError

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

CreateErrorResponseSystem.Net.Http.HttpRequestMessageExtensions 類別中定義的擴充方法。 在內部,CreateErrorResponse 會建立一個 HttpError 執行個體,然後建立一個包含 HttpErrorHttpResponseMessage

在此範例中,如果方法成功,它會在 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 中的模型驗證

搭配 HttpResponseException 使用 HttpError

前面的範例從控制器動作傳回 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;
    }
}