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

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

HttpResponseException

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

HttpResponseException類型是特殊案例。 這個例外狀況會傳回您在例外狀況建構函式中指定的任何 HTTP 狀態碼。 例如,如果 id 參數無效,下列方法會傳回 404, Not Found。

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 組態程式碼放在 類別內 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);
    }
}

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