第3回 APIコントローラの実装方法:連載:ASP.NET Web API 入門(2/3 ページ)
RESTfulなWeb APIを実装してみよう。ASP.NET Web API 2を使えば、実践的なHTTPサービスが容易に実装できる点に注目してほしい。
2-3 HTTPリクエストを取得する
アクション・メソッドにて、HTTPリクエストを取得するには、先述のとおり、アクション・メソッドに引数を定義する。大半はこの方法でよいが、実はもう1つの方法がある。それはAPIコントローラ・クラスの基底クラスであるApiControllerのRequestプロパティ(HttpRequestMessageクラス)を参照することだ。まずは、アクション・メソッドの引数よりHTTPリクエストの取得を試み、それでも不十分の場合に、ApiControllerのRequestプロパティを参照することを推奨する。
それでは、よくある以下の項目を、アクション・メソッドの引数から取得してみよう*10。
- URLのクエリ文字列
- URLの一部の値
- Body値
*10アクション・メソッドの引数からHTTPリクエストを取得できる仕組みについて簡単に説明する。アクション・メソッドに引数を定義すると、ASP.NET Web APIフレームワークにより、HTTPリクエストから適切な値を引数に代入した状態で、メソッドの呼び出しが行われる。このHTTPリクエストの値を引数に代入する仕組みのことを、モデル・バインド、代入することをバインドと呼称する。
○ URLのクエリ文字列を取得する
クエリ文字列を取得するには、クエリ文字列の名前と同じ名前である引数を、アクション・メソッドに定義する。例として、URL「~/api/cusomers/?category=name&number=1」のクエリ文字列の値を取得するには、下のリスト2のように引数を定義する。
public string Get(string category, int number) { }
引数の型は、プリミティブ型(参照:Type.IsPrimitive プロパティ)である必要がある。復合データ型(独自クラス)を定義したい場合は、属性クラスFromUriAttribute(System.Web.Http名前空間)を付与する。
下のリスト3は、MyParameterクラス(開発者が定義する独自クラス)の各プロパティに、先ほどのURL「~/api/cusomers/?category=name&number=1」のクエリ文字列をバインドさせたいときのコードである。
public class MyParemeter
{
public string Category { get; set; }
public string Number { get; set; }
}
public class CustomersController : ApiController
{
public string Get([FromUri]MyParemeter parameter) { }
}
このときの注意点として、MyParameterクラスの各プロパティの名前は、クエリ文字列の名前と同じにする必要がある(アクション・メソッドの引数の名前は何でもよい)。
○ URLの一部の値を取得する
場合によっては、「~/api/customers/japan/tokyo」といったURLを定義して、「japan」や「tokyo」に当てはまるURLの一部をパラメータとして受け取りたいこともあるだろう。これは、ルーティングの設定にてマッピング・ルールを追加することで実現可能だ。
例として、先ほどのURL「~/api/customers/japan/tokyo」の「japan」や「tokyo」を取得したい場合は、リスト4のようにあらかじめ記述されているルーティングの設定のコードの前に、マッピング・ルールを追加し、リスト5のようにメソッドの引数を定義する。
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
config.Routes.MapHttpRoute(
name: "AddressApi",
routeTemplate: "api/address/{country}/{prefecture}", // ……?
defaults: new { controller = "address" }
);
// ……省略(あらかじめ記述されているルーティングの設定)……
あらかじめ記述されているルーティングの設定の前に、上のようにMapHttpRouteメソッドを記述する。
public class AddressController : ApiController
{
public string Get(string country, string prefecture)
{
return country + prefecture;
}
}
注意点は、ルーティングの設定で指定しているパラメータの名前(リスト4の「{country}」や「{prefecture}」の部分)と、アクション・メソッドの引数の名前を同じにすることである。また、クエリ文字列と同様に属性クラスFromUriAttribute(System.Web.Http名前空間)を付与することで、アクション・メソッドの引数の型に複合データ型を指定することもできる。
○ Bodyの値を取得する
Bodyには、メッセージの本質を表す情報をある書式に変換して格納する。例えば、下の表6のように、顧客を追加するためのHTTPリクエストの場合は、追加したい顧客の情報をJSON形式などの書式で格納する。
HTTPリクエストの項目 | 値 |
---|---|
HTTPメソッド | POST |
HTTPヘッダ | Content-Type : application/json |
Body | { Name : “Taro”, Addres : “Tokyo” }(JSON形式) |
表6 Bodyの値を送信するHTTPリクエストの例 |
このBodyの値を取得するには、複合データ型(独自のクラス)をアクション・メソッドの引数に定義することで可能になる。
ただし、Bodyの書式(=Content-Typeヘッダで指定される値)が、ASP.NET Web APIフレームワークによってあらかじめサポートされていない場合は、別途、メディア・フォーマッタを実装するか、もしくは、アクション・メソッドの引数からではなくApiControllerクラスのRequestプロパティから参照する必要がある(メディア・フォーマッタの実装については、次回で解説予定)。
ASP.NET Web API フレームワークによってあらかじめサポートされている書式は、以下の3つである。
- application/x-www-form-urlencoded(フォームの送信時に使用される形式)
- application/json(JSON形式)
- application/xml(XML形式)
いわゆるJSONやXMLといった一般的な書式がサポートされている。ここでは、下のHTTPリクエストのように、JSON形式で表された(=ヘッダ「Content-Type」で指定される値が「application/json」である)Bodyの値を取得する例を取り上げる。
HTTPリクエストの項目 | 値 |
---|---|
HTTPメソッド | POST |
HTTPヘッダ | Content-Type : application/json |
Body | { Name : “Taro”, Addres : “Tokyo” }(JSON形式) |
表7 JSON形式のHTTPリクエストの例 |
まず、Bodyの値をバインドさせるためのクラス「Person」を用意し、アクション・メソッドでPerson型の引数を定義する(リスト6)。
public class Person
{
public string Name { get; set; }
public int Number { get; set; }
public DateTime Birthday { get; set; }
}
public class PersonController : ApiController
{
public void Post(Person person)
{
}
}
注意点としては、先ほどのURL取得時のように属性クラスFromUriAttributeを付与しないことと、Personクラスのプロパティ名を、Bodyの値のJSONプロパティの名前と同じにすることである。
○ ApiControllerクラスのRequestプロパティを参照する
上記で取り上げたHTTPリクエストの値以外を取得するには、ApiControllerクラスのRequestプロパティを参照する。この方法では、先ほどアクション・メソッドの引数で取得したURLやBodyの値も取得できる。リスト7にサンプル・コードを掲載するので、必要なときに参考にしてほしい。
public class PersonController : ApiController
{
public void Post()
{
// URLの絶対パスを取得
string absolutePath = this.Request.RequestUri.AbsolutePath;
// URLパラメータ「category」の値を取得
string routeCategory = this.Request.GetRouteData().Values["category"].ToString();
// クエリ文字列「number」の値の取得
string queryNumber = this.Request.GetQueryNameValuePairs().First(q => q.Key == "number").Value;
// ヘッダ「Accept」の値の取得
string acceptEncoding = this.Request.Headers.Accept.First().MediaType;
// Bodyに格納されたバイナリ値を(同期で)取得
Stream stream = ((StreamContent)this.Request.Content).ReadAsStreamAsync().Result;
}
}
各HTTPリクエストの値が設定されていると想定してコーディングしているため、実際に試す場合は、送信するHTTPリクエストに不備があると例外が発生するので注意。
HTTPリクエストの値の取得については以上だ。次は反対に、サーバからクライアントへ返すHTTPレスポンスの内容を指定する方法を解説する。
Copyright© Digital Advantage Corp. All Rights Reserved.