この記事では、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 メソッドをオーバーライドすることです。
Note
ASP.NET Web API の例外フィルターは、ASP.NET MVC の例外フィルターと似ています。 ただし、これらの例外フィルターは個別の名前空間で宣言され、別々に機能します。 特に、MVC で使用される HandleErrorAttribute クラスでは、Web API コントローラーによってスローされる例外は処理されません。
NotImplementedException 例外を HTTP 状態コード 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 応答メッセージが含まれています。
例外フィルターの登録
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 オブジェクトは、応答本文でエラー情報を返す一貫した方法を提供します。 次の例は、応答本文で HttpError を使用して 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 クラスで定義されている拡張メソッドです。 内部的には、CreateErrorResponse は HttpError インスタンスを作成し、その 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 を使用する利点の一つは、他の厳密に型指定されたモデルと同じコンテンツ ネゴシエーションとシリアル化プロセスを経ることです。
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;
}
}