読者です 読者をやめる 読者になる 読者になる

miso_soup3 Blog

主に ASP.NET 関連について書いています。

ASP.NET Web API 2 - IHttpActionResult

ASP.NET Web API 2 から、アクションの戻り値に、IHttpActionResult インターフェイスを指定できるようになりました。
ASP.NET MVC をご存じの方にはこう伝えたいです。
“ ActionResult が ASP.NET Web API にもやってきた!”と。

f:id:miso_soup3:20130722224014p:plain
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 にも対応してほしいです。