ASP.NET Web API 2 - IHttpActionResult
ASP.NET Web API 2 から、アクションの戻り値に、IHttpActionResult インターフェイスを指定できるようになりました。
ASP.NET MVC をご存じの方にはこう伝えたいです。
“ ActionResult が ASP.NET Web API にもやってきた!”と。
(ASP.NET Web API HTTP メッセージ ライフサイクル ポスター でいうとこの右下の部分。図は少し手を加えたもの)
public class ValuesController : ApiController { public IHttpActionResult Post() {…} }
(戻り値に IHttpActionResult を指定できる。)
今までは、アクションの戻り値は上記の図の通り、
- HttpResponseMessage
- void (ステータスコード 204 を返す)
- オブジェクトの型(パイプラインにより、HttpResponseMessage に変換される)
の3つを指定できましたが、今回の追加で4つめの
- IHttpActionResult
を指定できるようになりました。
(ポスター、新しくなるかな?)
このエントリでは、この IHttpActionResult が何であるかと、使い方の例を書きます。
IHttpActionResult とは
IHttpActionResult は、HttpResponseMessage を組み立てる処理をカプセル化したものです。
インターフェイスであり、HttpResponseMessage を返すメソッドのみ定義されています。
namespace System.Web.Http { public interface IHttpActionResult { Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken); } }
開発者は、この IHttpActionResult インターフェイスを実装したクラスを用意し(※1)、
そのクラスの中で、HttpResponseMessage を組み立てる処理を記述します。
そして、Apiコントローラのアクションにて、その実装したクラスのインスタンスを返します。
パイプラインはそのインスタンスの ExecuteAsync メソッドを実行し、HttpResponseMessage を取得します。
※1…ASP.NET Web API にすでに用意されるクラスを使うこともできます。
どんな時に使うか
- HttpResponseMessage を組み立てる処理が複雑になった時
- HttpResponseMessage を組み立てる処理が、コントローラやアクションをまたいで複数の場所で記述する時
に使えると思います。
アクションメソッドの単体テストの検証も簡潔になるかと。
例
IHttpActionResult – new way of creating responses in ASP.NET Web API 2
こちらの記事では、2つの実装例を紹介されています。
- CreatedContentActionResult
- ステータスコード 201 Created と、ヘッダー Location を指定した HttpResponseMessage を返す。
- HtmlActionResult
- Viewファイル名とモデルを指定して、Html を Body にもつ HttpResponseMessage を返す。(RazorEngine を使用)
後者は、ASP.NET MVC でいうところの ViewResult と似た役割を持っています。
ここでは
ここでは、例えば同じ内容を投稿した時に使われるであろう、以下の HttpResponseMessage を返す API を例にして、IHttpActionResult の使い方について書きます。
- ステータスコード
- 403 Forbidden
- Body
- “リソースが重複しています”
(Twitterでも同じツイートを投稿したときに、似たようなエラーレスポンスを返しています。)
コードで言うと、以下の記述を IHttpActionResult を使って実装します。
public class HttpResponseMessage : ApiController { public IHttpActionResult Post() { if (true)//重複していたら { return Request.CreateResponse(HttpStatusCode.Forbidden, "リソースが重複しています"); }
IHttpActionResult の実装クラスを用意する
(※以下のコードは、ASP.NET Web API 2 で記述できるコードです。)
System.Web.Http 名前空間にある IHttpActionResult インターフェイスを実装します。
クラス名は、「DupulicationErrorActionResult」にし、コンストラクタの引数に HttpRequestMessage を定義します。
(HttpResponseMessage の組み立てには、HttpRequestMessage が必要だからです。←そういえばなぜ必要なのだろう?)
ExecuteAsync メソッドにて、ステータスコードとメッセージを指定した HttpResponeMessage を返します。
/// <summary> /// 結果が重複エラーであることを表します /// </summary> public class DupulicationErrorActionResult : IHttpActionResult { public DupulicationErrorActionResult(HttpRequestMessage request) { _request = request; } private readonly HttpRequestMessage _request; public Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken) { var response = _request.CreateResponse(HttpStatusCode.Forbidden, "追加リソースが重複しています"); return Task.FromResult(response); } }
Api コントローラにて
Api コントローラでは、アクションの戻り値を IHttpActionResult を指定し、
DupulicationErrorActionResult クラスのインスタンスを返します。
public class ValuesController : ApiController { public IHttpActionResult Post() { if (true)//重複していたら { return new DupulicationErrorActionResult(this.Request); } throw new NotImplementedException(); }
ヘルパーメソッドを使おう!!
以上で終わりにしても良いのですが、
ASP.NET MVC でもよく使われていたヘルパーメソッドを、Apiコントローラに用意すると…
// ValuesControllerにて /// <summary> /// 重複エラーを表す結果を返します /// </summary> /// <returns></returns> private IHttpActionResult DupulicationError() { var result = new DupulicationErrorActionResult(this.Request); return result; }
先ほどのアクションではこんな風に記述できます。
public IHttpActionResult Post() { if (true)//重複していたら { return DupulicationError(); } throw new NotImplementedException(); }
以上の様に、IHttpActionResult で HttpResponseMessage の組み立てをカプセル化してしまえば、Api コントローラの至る所で汎用できるというわけです。
今後
先ほどの例で問題になるのは、重複していない時(if文の後)HttpResponseMessage の組み立てをどうするかです。
戻り値を IHttpActionResult としているので、その実装クラスを返さなければなりません。
また実装クラスを用意するのは面倒・・・です。
が、きっと ASP.NET Web API 2 がリリースされる頃には、たくさんの IHttpActionResult 実装クラスが用意されるはずです。
(GitHub で master ブランチ を見ると、System.Web.Http に Results 名前空間が追加されていて、すでに実装クラスがいくつかあります! JsonResultクラスがあるのがなんとも・・・)
また、先ほどの例で用意したヘルパーメソッドも、ApiController クラスに多く用意されると思います。
→ 2013/10/17 リリース版にて追加されてます!
return OK(); とか、return BadRequest(); とか return Conflict(); とか書けるわけです。
ASP.NET MVC の return View(); とか return Content(“”); みたいに!
個人的には、throw にも対応してほしいです。