ASP.NET Web API 2.1 での BSON サポート

このトピックでは、Web API コントローラー (サーバー側) と .NET クライアント アプリで BSON を使用する方法について説明します。 Web API 2.1 では、BSON のサポートが導入されています。

BSON とは

BSON はバイナリ シリアル化形式です。 "BSON" は "バイナリ JSON" を表しますが、BSON と JSON は非常に異なる方法でシリアル化されます。 オブジェクトは JSON と同様に名前と値のペアとして表されるため、BSON は "JSON に似ています"。 JSON とは異なり、数値データ型は文字列ではなくバイトとして格納されます

BSON は、軽量でスキャンが簡単で、エンコード/デコードが高速に行われるよう設計されています。

  • BSON のサイズは JSON に相当します。 データによっては、BSON ペイロードのサイズが、JSON ペイロードより増減する可能性があります。 イメージ ファイルなどのバイナリ データをシリアル化する場合、バイナリ データは base64 でエンコードされていないため、BSON は JSON よりも小さくなります。
  • BSON ドキュメントは、要素の前に長さフィールドが付いているため、スキャンが簡単であるため、パーサーは要素をデコードせずにスキップできます。
  • 数値データ型は文字列ではなく数値として格納されるため、エンコードとデコードは効率的です。

.NET クライアント アプリなどのネイティブ クライアントでは、JSON や XML などのテキスト ベースの形式の代わりに BSON を使用するとメリットがあります。 JavaScript は JSON ペイロードを直接変換できるため、ブラウザー クライアントの場合は、JSON を使用する必要があります。

幸いなことに、Web API はコンテンツ ネゴシエーションを使用するため、API は両方の形式をサポートし、クライアントが選択できるようにします。

サーバーでの BSON の有効化

Web API 構成で、BsonMediaTypeFormatter をフォーマッタ コレクションに追加します。

public static class WebApiConfig
{
    public static void Register(HttpConfiguration config)
    {
        config.Formatters.Add(new BsonMediaTypeFormatter());

        // Other Web API configuration not shown...
    }
}

クライアントが "application/bson" を要求すると、Web API は BSON フォーマッタを使用します。

BSON を他のメディアの種類に関連付けるには、それらを SupportedMediaTypes コレクションに追加します。 次のコードは、サポートされているメディアの種類に "application/vnd.contoso" を追加します。

var bson = new BsonMediaTypeFormatter();
bson.SupportedMediaTypes.Add(new MediaTypeHeaderValue("application/vnd.contoso"));
config.Formatters.Add(bson);

HTTP セッションの例

この例では、次のモデル クラスと単純な Web API コントローラーを使用します。

public class Book
{
    public int Id { get; set; }
    public string Title { get; set; }
    public string Author { get; set; }
    public decimal Price { get; set; }
    public DateTime PublicationDate { get; set; }
}

public class BooksController : ApiController
{
    public IHttpActionResult GetBook(int id)
    {
        var book = new Book()
        {
            Id = id,
            Author = "Charles Dickens",
            Title = "Great Expectations",
            Price = 9.95M,
            PublicationDate = new DateTime(2014, 1, 20)
        };

        return Ok(book);
    }
}

クライアントは、次の HTTP 要求を送信する場合があります。

GET http://localhost:15192/api/books/1 HTTP/1.1
User-Agent: Fiddler
Host: localhost:15192
Accept: application/bson

応答を次に示します。

HTTP/1.1 200 OK
Content-Type: application/bson; charset=utf-8
Date: Fri, 17 Jan 2014 01:05:40 GMT
Content-Length: 111

.....Id......Title.....Great Expectations..Author.....Charles Dickens..Price..........PublicationDate.........

ここでは、バイナリ データを "." 文字に置き換えます。 Fiddler の次のスクリーン ショットは、生の 16 進値を示しています。

Screenshot of a window pane showing the binary data's raw hex values in the colors green on the top and middle, and black at the bottom.

HttpClient での BSON の使用

.NET クライアント アプリでは、HttpClientで BSON フォーマッタを使用できます。 HttpClient の詳細については、「.NET クライアントからの Web API の呼び出し」を参照してください。

次のコードは、BSON を受け入れる GET 要求を送信し、応答で BSON ペイロードを逆シリアル化します。

static async Task RunAsync()
{
    using (HttpClient client = new HttpClient())
    {
        client.BaseAddress = new Uri("http://localhost");

        // Set the Accept header for BSON.
        client.DefaultRequestHeaders.Accept.Clear();
        client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/bson"));

        // Send GET request.
        result = await client.GetAsync("api/books/1");
        result.EnsureSuccessStatusCode();

        // Use BSON formatter to deserialize the result.
        MediaTypeFormatter[] formatters = new MediaTypeFormatter[] {
            new BsonMediaTypeFormatter()
        };

        var book = await result.Content.ReadAsAsync<Book>(formatters);
    }
}

サーバーから BSON を要求するには、Accept ヘッダーを "application/bson" に設定します。

client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new  
    MediaTypeWithQualityHeaderValue("application/bson"));

応答本文を逆シリアル化するには、BsonMediaTypeFormatter を使用します。 このフォーマッタは既定のフォーマッタ コレクションに含まれていないため、応答本文を読み取るときに指定する必要があります。

MediaTypeFormatter[] formatters = new MediaTypeFormatter[] {
    new BsonMediaTypeFormatter()
};

var book = await result.Content.ReadAsAsync<Book>(formatters);

次の例では、BSON を含む POST 要求を送信する方法を示します。

static async Task RunAsync()
{
    using (HttpClient client = new HttpClient())
    {
        client.BaseAddress = new Uri("http://localhost:15192");

        // Set the Accept header for BSON.
        client.DefaultRequestHeaders.Accept.Clear();
        client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/bson"));

        var book = new Book()
        {
            Author = "Jane Austen",
            Title = "Emma",
            Price = 9.95M,
            PublicationDate = new DateTime(1815, 1, 1)
        };

        // POST using the BSON formatter.
        MediaTypeFormatter bsonFormatter = new BsonMediaTypeFormatter();
        var result = await client.PostAsync("api/books", book, bsonFormatter);
        result.EnsureSuccessStatusCode();
    }
}

このコードの多くは、前の例と同じです。 ただし、PostAsync メソッドでは、フォーマッタとして BsonMediaTypeFormatter を指定します。

MediaTypeFormatter bsonFormatter = new BsonMediaTypeFormatter();
var result = await client.PostAsync("api/books", book, bsonFormatter);

最上位プリミティブ型のシリアル化

すべての BSON ドキュメントは、キーと値のペアの一覧です。BSON 仕様では、整数や文字列などの 1 つの生の値をシリアル化するための構文は定義されていません。

この制限を回避するために、BsonMediaTypeFormatter はプリミティブ型を特殊なケースとして扱います。 シリアル化する前に、値をキー "Value" を使用してキーと値のペアに変換します。 たとえば、API コントローラーから整数が返されたとします。

public class ValuesController : ApiController
{
    public IHttpActionResult Get()
    {
        return Ok(42);
    }
}

シリアル化する前に、BSON フォーマッタはこれを次のキーと値のペアに変換します。

{ "Value": 42 }

逆シリアル化すると、フォーマッタはデータを元の値に戻します。 ただし、Web API が生の値を返す場合は、別の BSON パーサーを使用するクライアントがこのケースを処理する必要があります。 一般に、生の値ではなく、構造化データを返すことを検討する必要があります。

その他のリソース

Web API BSON サンプル

メディア フォーマッタ