HttpClient を使って JSON データを POST する方法


Windows ストアアプリと Windows Phone アプリのサポートチームで公開している Blog のうち、ページビューが多いいくつかの記事を日本語に翻訳しています。

今回は HttpClient クラスを使って JSON (JavaScript Object Notation) を送信する方法についてまとめた記事を紹介します。

How to use HttpClient to post JSON data

これはフォーラムでよく訊かれるので、HttpClient クラスと DataContractJsonSerializer を使って JSON データをウェブサービスへ POST する方法を説明したいと思います。シリアル化した JSON オブジェクトをウェブのエンドポイントへ送るのはデータを HTTP や HTTPS を使って取得する一般的な方法で、これは WinJS の世界 (HTML5 / JavaScript の Windows ストアアプリを書く場合) では非常に簡単ではありますが、.NET アプリでHttpClient を使って行うやり方についてはあまりいい例が出回っていません。 

ステップ 1 – 送るデータを見極める

JSON データを WebService へ送る場合、サービスそのものがどんなデータを想定しているかの例を提供していることが望まれます。普通はどうやって POST リクエストを生成するかのサンプルコードが用意されています。まず、うまくいく POST 送信を HTTP または HTTPS トラフィックとしてキャプチャーしましょう。これにより、この POST をあらかじめ正しいとわかっているものと比較することができます。HTTP や HTTPS プロトコルを使うアプリを開発中に非常に役立つツールの一つに Fiddler があります。 今回のサンプルでは Windows Azure Mobile Services エンドポイントへの HTTP トラフィックをキャプチャーしました。

以下に、背後のデータベースに POST を送信したときに、Mobile Services が生成した HTTP トラフィックを Fiddler でキャプチャーしてみました。

POST https://sharemydata.azure-mobile.net/tables/TodoItem HTTP/1.1
Accept: application/json
X-ZUMO-INSTALLATION-ID: 8bc6aea9-864a-44fc-9b4b-87ec64e123bd
X-ZUMO-APPLICATION: OabcWgaGVdIXpqwbMTdBQcxyrOpeXa20
Content-Type: application/json
Host: sharemydata.azure-mobile.net

Content-Length: 31
Expect: 100-continue

{“text”:”ddd”,”complete”:false}

次のステップでは、このような MobileServices クラスの魔法を使わずに、上記と同じデータを HttpClient クラスを使って POST する方法を紹介します。ここから少し工夫すれば、皆さんも JSON を使ってデータを POST することができるようになると思います。繰り返しになりますが、ここでの目的は提供されている様々なライブラリを使わずに済ませることでなく、JSON ベースのウェブサービスをどう障害解析し、HttpClient とその関連クラスをつかって POST するかをお見せすることです。

ステップ2 – POSTされている内容を理解し複製する

それでは上記のトレースを一行ずつ見ていき、同じことをするコードを書いてみましょう。

POST https://sharemydata.azure-mobile.net/tables/TodoItem HTTP/1.1

// HttpClient を作り、Uri https://sharemydata.azure-mobile.net/tables/TodoItem に POST します。

// アプリで使用するための HttpClient インスタンスを作成
HttpClient aClient = new HttpClient();

// POST 送信先の Uri
Uri theUri = new Uri(“https://sharemydata.azure-mobile.net/tables/TodoItem“);

// この Http クライアントを使って何かデータを POST します ( ‘theContent’ はこれから定義します)
aClient.PostAsync(theUri, theContent);       

Accept: application/json

// サーバに対してJSONでの返答を受け付けている旨を知らせる必要があるので、このようにヘッダーに追加します。

aClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue(“application/json“));

X-ZUMO-INSTALLATION-ID: 8bc6aea9-864a-44fc-9b4b-87ec64e123bd

このウェブサービスのドキュメントによると、これは必須ではないので触らずに残しておくこともできます(が、含めます)。

aClient.DefaultRequestHeaders.Add(“X-ZUMO-INSTALLATION-ID“, “8bc6aea9-864a-44fc-9b4b-87ec64e123bd“);

X-ZUMO-APPLICATION: OabcWgaGVdIXpqwbMTdBQcxyrOpeXa20

これをヘッダーにセットして、サービスにデータ挿入をしようとしているアプリケーションを知らせます。

aClient.DefaultRequestHeaders.Add(“X-ZUMO-APPLICATION“,”OabcWgaGVdIXpqwbMTdBQcxyrOpeXa20“);

 

Host: sharemydata.azure-mobile.net
 

aClient.DefaultRequestHeaders.Host = theUri.Host;

Expect: 100-continue

これについてはデフォルトで追加されるので何もする必要はありません。

コンテンツのヘッダーと情報

お気づきのように、 Content-Type: application/json, Content-Length: 31、および、{“text”:”ddd”,”complete”:false} については飛ばしました。

このコンテンツ関連のデータは上述の PostAsync 呼び出しの際に、’theContent’ を StringContent として渡すことで追加されます。もちろん、簡単なテキストをご自分でフォーマットし JSON 文字列として送ることも、単純な JSON の POST 送信であれば可能です。複雑なオブジェクトの場合は専門のクラスに任せてしまうほうがずっと簡単です!

  • POSTされたJSONデータに対応するクラスを定義

[DataContract]
   public class TodoItem2
   {
       public int Id { get; set; }

       [DataMember(Name = “text”)]
       public string Text { get; set; }

       [DataMember(Name = “complete”)]
       public bool Complete { get; set; } 
   }

ここに POST する JSON データに対応するクラスを定義しました。 ‘DataContract’ と ‘DataMember’ の属性は JSON シリアル化のクラスで必要とされます。  ‘DataMember’ ではさらに ‘text’ と ‘complete’ がクラスの public メンバーの名前を小文字にしたもので定義しています。ここで明らかなように、シリアル化した JSON データの既存のクラスの DataMember の Name を変更することができます。また、public int の ’Id’ は DataMember 属性を与えられていないことに注意しましょう。これによりこの attribute はシリアル化対象外となります。

  • クラスを作成し、データで埋めていきます

単純にクラスをインスタンス化しデータを割り当てていきます

// JSON としてシリアル化し POST するデータ

TodoItem2 todoItem2 = new TodoItem2();

// 値をセットします
todoItem2.Text = “ddd”;
todoItem2.Complete = false;

  • オブジェクトを文字列にシリアル化するコードを追加し、PostAsync 呼び出し時に渡す StringContent オブジェクトを作成

// 該当タイプ用のJsonSerializer を作成
DataContractJsonSerializer jsonSer = new
DataContractJsonSerializer(typeof(TodoItem2));

// Serializerを使ってオブジェクトをMemoryStream に書き込み
MemoryStream ms = new MemoryStream();

jsonSer.WriteObject(  ms, todoItem2);
ms.Position = 0;

// StreamReader で StringContent (Json) をコンストラクトします。
StreamReader sr = new StreamReader(ms);
StringContent theContent = new StringContent(sr.ReadToEnd(), System.Text.Encoding.UTF8,”application/json”);

ステップ3 – POST が正しいか確かめる

オブジェクトのコンテンツをストリームにしてウェブサービスに送るのに必要なのはこれだけです。このコードを実行し Fiddler でトレースすると、POST が 201 で成功し事実上先ほどの成功した POST と同一であることがわかります (ヘッダーの中の順序が異なるのと Content-Type にcharsetが追加されているのは問題ありません。)

POST https://sharemydata.azure-mobile.net/tables/TodoItem HTTP/1.1
Accept: application/json
X-ZUMO-INSTALLATION-ID: 8bc6aea9-864a-44fc-9b4b-87ec64e123bd
X-ZUMO-APPLICATION: OabcWgaGVdIXpqwbMTdBQcxyrOpeXa20
Content-Type: application/json;
charset=utf-8
Host: sharemydata.azure-mobile.net
Content-Length: 31

Expect: 100-continue
Connection: Keep-Alive

{“complete”:false,”text”:”ddd”}

まとめ

JSON データを POST 送信するというのは、エンドポイントに対し単純に正確な HTTP や HTTPS のトラフィックを提供することである、ということが理解できれば、皆さんも上述のコードを使って好きなエンドポイントと通信するためのコードを書くことができるでしょう。これは本当に正しいヘッダーをつかって文字列を送信するだけのことです。JSON データの受け取りの方も同様で、もしご要望が高ければ将来の記事で触れてみたいと思います(とはいえドキュメントはしっかり整っています)

ツイッターでフォロー
@wsdevsol!

注: 
この記事のデータは架空のエンドポイントに書き換えました。皆さんが試される場合は Windows Azure Mobile Services アプリケーションを作るか、JSON データを受け入れるご自身のエンドポイントを指定してください! 

完成したコード:

C#

[DataContract]
   public class TodoItem2
   {
       public int Id { get; set; }

       [DataMember(Name = “text”)]
       public string Text { get; set; }

       [DataMember(Name = “complete”)]
       public bool Complete { get; set; } 
   }

// POST 先の
Uri theUri = new Uri(“https://sharemydata.azure-mobile.net/tables/TodoItem%22);

//HttpClient を作成しヘッダーを設定します
HttpClient aClient = new HttpClient();
aClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue(“application/json”));
aClient.DefaultRequestHeaders.Add(“X-ZUMO-INSTALLATION-ID”, “8bc6aea9-864a-44fc-9b4b-87ec64e123bd”);
aClient.DefaultRequestHeaders.Add(“X-ZUMO-APPLICATION”, “OabcWgaGVdIXpqwbMTdBQcxyrOpeXa20”);
aClient.DefaultRequestHeaders.Host = theUri.Host;

// JSON としてシリアライズし POST するデータ
TodoItem2 todoItem2 = new TodoItem2();

//値をセットします
todoItem2.Text = “ddd”;
todoItem2.Complete = false;

// 該当タイプ用の JsonSerializer を作成
DataContractJsonSerializer jsonSer = new
DataContractJsonSerializer(typeof(TodoItem2));

// Serializer を使ってオブジェクトを MemoryStream に書き込み
MemoryStream ms = new MemoryStream();
jsonSer.WriteObject(ms, todoItem2);
ms.Position = 0;

// StreamReader で StringContent (Json) をコンストラクトします。
StreamReader sr = new StreamReader(ms);

// JSON が簡単なものであれば、シリアル化のための上記 5 行は無視して自分でシリアル化し、
// これを StringContent コンストラクタの最初の引数として渡すこともできることに注意
StringContent theContent = new StringContent(sr.ReadToEnd(), System.Text.Encoding.UTF8, “application/json”);            

// データを送信します
HttpResponseMessage aResponse = await aClient.PostAsync(theUri, theContent);

if (aResponse.IsSuccessStatusCode)
{

}
else
{
    // 応答ステータスコードを表示します。 
    String failureMsg = “HTTP Status: ” + aResponse.StatusCode.ToString() + ” – Reason: ” + aResponse.ReasonPhrase;
}

VB

Public Class TodoItem2 
    Private Property Id() As Integer
        Get
            Return m_Id 
        End Get
        Set(value As Integer)
            m_Id = value
        End Set
    End Property 
    Private m_Id As Integer

 

    Public Property Complete() As Boolean
        Get
            Return m_Complete 
        End Get
        Set(value As Boolean)
            m_Complete = value
        End Set
    End Property
    Private m_Complete As Boolean
End Class

 

Private Async Sub SendJsonPost() 
    ‘ POST 送信先
    Dim theUri As New Uri(“https://sharemydata.azure-mobile.net/tables/TodoItem%22)

    ‘ HttpClient を作成しヘッダーを設定します
    Dim aClient As New HttpClient()
    aClient.DefaultRequestHeaders.Accept.Add(New MediaTypeWithQualityHeaderValue(“application/json”)) 
    aClient.DefaultRequestHeaders.Add(“X-ZUMO-INSTALLATION-ID”, “8bc6aea9-864a-44fc-9b4b-87ec64e123bd”) 
    aClient.DefaultRequestHeaders.Add(“X-ZUMO-APPLICATION”, “OabcWgaGVdIXpqwbMTdBQcxyrOpeXa20”)
    aClient.DefaultRequestHeaders.Host = theUri.Host

    ‘ JSON としてシリアル化し POST するデータ
    Dim todoItem2 As New TodoItem2()

    ‘値をセットします
    todoItem2.Text = “ddd”
    todoItem2.Complete = False

    ‘該当タイプ用の JsonSerializer を作成
    Dim jsonSer As New DataContractJsonSerializer(GetType(TodoItem2))

    ‘ Serializerを使ってオブジェクトをMemoryStream に書き込み
    Dim ms As New MemoryStream() 
     jsonSer.WriteObject(ms, todoItem2)
    ms.Position = 0
    Dim sr As New StreamReader(ms)

    ‘ JSONが簡単なものであれば、シリアル化のための上記5行は無視して自分でシリアル化し、
    ‘ これを StringContent コンストラクタの最初の引数として渡すこともできることに注意
    Dim theContent As New StringContent(sr.ReadToEnd(), System.Text.Encoding.UTF8, “application/json”)

    ‘データをポスト送信します。  
    Dim aResponse As HttpResponseMessage = Await aClient.PostAsync(theUri, theContent)

    If (aResponse.IsSuccessStatusCode) Then

    Else

        ‘応答ステータスコードを表示します。
        Dim failureMsg = “HTTP Status: ” + aResponse.StatusCode.ToString() + ” – Reason: ” + aResponse.ReasonPhrase

    End If
End Sub

Skip to main content