jQuery の ajax で JavaScript のオブジェクトをポストした時に送信されるデータは?

jQuery の ajax ライブラリを利用すると、AJAX を利用した動的なウェブページを簡単に作成できます。

AJAX を使うメソッドは $.ajax メソッドです。

これについては、AJAX で説明しました。

Ajax では様々なデータを背後で送受信可能です。

ここでは JavaScript のオブジェクトをサーバーにポストしたときに、実際にどのような形式でデータがポストされているのか確認します。

ajax によるデータのポスト

HTTP には主なメソッドとして GET とか POST 等があります。POST というのは、データが HTTP リクエストのボディー部に設定されるタイプのものを指します。

GET リクエストの場合は、URL の後ろに ?a=1&b=2 などと付きますが、POST リクエストはボディー部に同じような形式で入るので URL などには見えてこないというわけです。

GET リクエストでは URL のクエリーストリングとして値を設定してパラメータを渡しますが、POST ではボディー部にいれます。これによって、 POST ではサーバーに Content-Length をヘッダー情報として渡す機会があるので、サイズの大きいデータも送信できます。逆に言うと、 サイズの大きなデータは POST にするべきです。

JavaScript のオブジェクトをポストしたときに送信されるデータは?

さて、ここからやっと本題に入ります。

今回のテーマは JavaScript のオブジェクトを $.ajax でポストしたときに、背後でどんな形式でそれが送信されているか、ということです。 実際にネットワークで送信される情報をみてみましょう。

jQuery はバージョン 1.8.3 を使います。

次のコード中の変数 x を、data1 という名前で POST しましょう。

var x = {
  M: [1, 2, 3],
  N: {
    X: 'FOO',
    Y: 'BAR'
  }
};

$.ajax({
  url: "...",
  type: "POST",
  data: {
    data1: x
  },
  dataType: "html",
  cache: false,
  success: function(data, textStatus){
    ...
  },
  error: function(xhr, textStatus, errorThrown){
    ...
  }
});

早速実行してみます。

キャプチャされたデータは次の通り(見やすいように & にて改行しています)。

data1%5BM%5D%5B%5D=1
&data1%5BM%5D%5B%5D=2
&data1%5BM%5D%5B%5D=3
&data1%5BN%5D%5BX%5D=FOO
&data1%5BN%5D%5BY%5D=BAR

なにやらゴチャゴチャしていますね。

ここで、5B, 5D という値はそれぞれ [ と ] に相当することを踏まえて書き直すと、次のようになります。

data1[M][]=1
&data1[M][]=2
&data1[M][]=3
&data1[N][X]=FOO
&data1[N][Y]=BAR

もとのデータと見比べます。

var x = {
  M: [1, 2, 3],
  N: {
    X: 'FOO',
    Y: 'BAR'
  }
};

基本的に名前は [名前] として設定され、要素に名前のない配列データは [] に値が順番に設定されていることがわかります。

このように jQuery では既定の設定で、データを URL エンコードしてオブジェクトをシリアライズするのです。

traditional = true の場合

jQuery ではデータ送信時のオプションとして、traditional というものがあります。 これを true に設定すると、.serialize() メソッドでのシリアライズ化を行いません。

それによって実際にどのように値が変わるか。さっそく、試してみましょう。

$.ajax({
  url: "...",
  type: "POST",
  traditional: true,

  data: {
    data1: x
  },
  dataType: "html",
  ...

試してみると、なんと残念ながら次のようなゴミしか送られていないことがわかります。

data1=%5Bobject+Object%5D

明らかに 1, 2, 3 とか FOO、BAR といったデータは送られていませんね。

このままだと、「じゃぁ、なんでこんなオプションあるの?要らないじゃん」ということになってしまいますので、もうひとつ実験を。

先ほどの変数 x を次のように、単純な配列オブジェクトに置き換えます。

var x = [1, 2, 3];

データの送信方法は同じです。デフォルト (traditional = false) では次のようになります。

data1%5B%5D=1&data1%5B%5D=2&data1%5B%5D=3

%5B%5D を [] に直すと、次のようになります。

data1[]=1&data1[]=2&data1[]=3

ここで traditional = true にすると、送信されるデータは次のように変わります。

data1=1&data1=2&data1=3

今回はちゃんと値が送られていますね。そして、[] がなくなっていることがわかります。このように、複雑なオブジェクトでなければちゃんとデータが送られます。

そして、こうしたオプションがなぜ必要かというと、環境によっては[] 付きのデータを受け取れないからです。