miso_soup3 Blog

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

ASP.NET WEB API モデルバインド その1 値の取得先

ASP.NET WEB API モデルバインドについてです。

モデルバインディングとは

モデルバインディングとは、ApiController のアクションメソッドの引数に定義した
パラメータの型(モデル)の値を、リクエストから生成し、メソッドに渡すことです。
ASP.NET MVC にもある概念です。

例:

public class ValuesController
{
  // GET ~/api/values/?id=32
  public string Get(int id)
  {
    // id には、QueryString "id" の値 32 が入っている。
  } 
}

ASP.NET WEB API のモデルバインドでは、
このモデルである引数の値を、どこから取ってくるのか、
その1は、この取得先についてです。

デフォルトでは

先日、ASP.NET WEB API パイプラインのポスターが公開されました。
ASP.NET Web API HTTP Message Lifecycle
日本語版もでています。ASP.NET Web API HTTP メッセージ ライフサイクル 日本語版

このPDFの左下に、モデルバインドについての図があります。
この図は、WEB API のモデルバインドのデフォルトの挙動を説明しており、
値の取得先が一目で分かるようになっています。

  • 型が ComplexType (自分で作成したクラスとか)だと、Body から値を生成します。
  • 型が SimpleType (int, datetime, guid 等の Primitive 型)だと URI から値を生成します。
  • 他、開発者が独自の処理で、値の取得先を変更することができます。(Header とか)

文字で見るよりコードで見たほうがわかりやすいです。

QueryString と Body から値を取得する例を挙げます。

例えばこんな HTTP リクエストを送ると… ↓

POST ~/api/values/?id=4
ContentType : application/json

{ Name : "Taro", Number : 1 }

Api Controller のアクションメソッドでは
このように値が取れます。 ↓

public void Post(int id, Person person)
{
  //id : 4
  //person.Name : "Taro"
  //person.Number : 1
  //と値がバインドされる。
}

id は、ルーティングでプレースホルダーを使って定義することもできます。

属性で制御する

引数に属性をつけると、上記のデフォルトとは違って、
値の取得先を変更することができます。

例えばこんな HTTP リクエストでは… ↓

POST ~/api/values/?name=Taro&number=1

Api Controller のアクションメソッドでは
このようにすると値が取れます。 ↓

public void Post([FromUri]Person person)
{
  //person.Name : "Taro"
  //person.Number : 1
  //と値がバインドされる。
}

アクションメソッドの引数に、[FromUri]
ついていることに注意です。
この属性を付けないと、例のように値がバインドされません。

※[FromBody]について
プリミティブ型に[FromBody]をつけて、Body から値を取得することはできますが、
かなり限定的になるのであまり使えません。
(json で { id : 4 } とかしてもバインドできません)

つまり

特に何もしなければ、int 型等のプリミティブ型の場合は、URI から、
自分で定義したクラスを指定すれば、Body から値が生成されます。
が、[FromBody] や [FromUri] などの属性を指定した場合は、
指定した場所から値が生成されます。

void Post(int id, Person person);
 // ↑ id は URI から、person は Body から

void Post([FromBody]int id, [FromUri]Person person);
// ↑ id は、Body から、person は URI から

WEB API のプロジェクトテンプレートのコードに、
[FromBody] が付いているのはこれが理由です。

この URL と Body の分け方、直感的だなと思うのですがどうでしょうか…。

ASP.NET MVC と比べて

もはや MVC と比べるのはナンセンスかもしれませんが…。
MVC のモデルバインドは、Key を指定すれば、ValueProvider さんが各所から値を用意してくれていました。
一方 WEB API のモデルバインドは、HTTP リクエストの値を直接指定するようになっています。

WEB API にも、MVC の ValueProvider や ModelBinder の仕組みはあり、
カスタマイズもできるのですが、やはり中身は、
HTTP リクエストの値をしっかり指定するような実装になります。

次はモデルを作っているところ

モデルの取得先を簡単に見てみましたが、先ほどのパイプラインのポスターの図には、
モデルを作る部分、MediaTypeFormatter・ModelBinder・ValueProvider も登場しています。
次はこの辺りを書ければいいなと思います。