次の方法で共有


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 アプリケーション" プロジェクト テンプレートを使用してプロジェクトを作成する場合は、App_Start フォルダーにある WebApiConfig クラス内に Web API 構成コードを配置します。

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

        // Other configuration code...
    }
}

HttpError

HttpError オブジェクトは、応答本文でエラー情報を返す一貫した方法を提供します。 次の例は、HTTP エラー を含む HTTP 状態コード 404 (Not Found) を応答本文で返す方法を示しています。

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 クラスで定義されている拡張メソッドです。 内部的には、CreateErrorResponseHttpError インスタンスを作成し、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 を使用する利点の 1 つは、他の厳密に型指定されたモデルと同じコンテンツ ネゴシエーションとシリアル化プロセスを通過することです。

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