Ajax 周辺技術のまとめ
[履歴] [最終更新] (2019/09/05 00:19:43)
最近の投稿
注目の記事

概要

Ajax (Asynchronous JavaScript + XML) は JavaScript でサーバと非同期通信を行うための仕組みです。非同期通信には JavaScript の仕様として標準で提供されている XMLHttpRequest オブジェクトを利用することが多いです。XMLHttpRequest は Ajax のためのオブジェクトではなく HTTP 通信を行うための汎用オブジェクトです。Ajax は応用先のひとつです。一部の古い Internet Explorer では XMLHttpRequest の実装が異なります。しかしながら最近のブラウザであれば IE を含め挙動に差異はなく、ブラウザ間の実装の違いを意識する必要も薄れてきています。XMLHttpRequest にはその名称からも予想できるように XML を扱うための専用のメソッドが実装されています。プレーンテキストを扱うメソッドを利用すれば JSON など任意の形式のデータを扱うこともできます。

WebSocket との違い

WebSocket は Ajax と同様にクライアントとサーバが非同期通信を行うための仕組みです。補助的に HTTP を利用する専用のプロトコルで通信します。Ajax と異なりコネクションを維持したままにするため双方向通信が可能です。具体的にはサーバからクライアントへの push 通知などが可能になります。類似の技術にロングポーリングを行う Comet があります。ロングポーリングにおいて、サーバは HTTP レスポンスをすぐには返さずにデータが準備できるまでクライアントを待たせます。

WebSocket は HTML5 の仕様の一部でしたが、現在は単独のプロトコルとして規格策定が進められています。非同期通信ではなく双方向通信という観点から有用であり WebSocket の通信プロトコルは JavaScript だけでなく様々な言語で利用できるように実装が進められています。

XMLHttpRequest オブジェクトによる Ajax 通信

// オブジェクトの作成
var req = new XMLHttpRequest();

// 状態 (req.readyState) が変化する度に呼び出される関数を登録
req.onreadystatechange = function(){
  console.log("req.onreadystatechanged: " + req.readyState
              + ", req.status: " + req.status
              + ", req.statusText: " + req.statusText); //=>
    // req.onreadystatechanged: 1, req.status: 0, req.statusText:
    // GET https://localhost/remote/1/ 200 OK 119ms
    // req.onreadystatechanged: 2, req.status: 200, req.statusText: OK
    // req.onreadystatechanged: 3, req.status: 200, req.statusText: OK
    // req.onreadystatechanged: 4, req.status: 200, req.statusText: OK

  if (req.readyState != 4) return; // 通信中
  // 0: 未初期化 (open 未実行)
  // 1: open でサーバとのコネクションが張られた (open 実行済, send 未実行)
  // 2: send でリクエストしたところ応答ステータスとヘッダを得たがボディはまだ
  // 3: レスポンスボディの受信が開始した
  // 4: すべての応答データを取得した

  if (req.status == 200) {
    console.log('正常');
    console.log(req.getResponseHeader('Date')); //=>
    console.log(req.getAllResponseHeaders()); //=>
      // Date: Sun, 08 Feb 2015 10:01:16 GMT
      // Server: ...
      // Content-Length: 1
      // Keep-Alive: timeout=5, max=91
      // Connection: Keep-Alive
      // Content-Type: text/html; charset=UTF-8
    console.log(req.responseText); //=> 3
    console.log(req.responseXML); //=> null
  }
  else {
    console.log('エラー');
  }
  // 200: OK
  // 304: Not Modified
  // 401: Unauthorized, 403: Forbidden, 404: Not Found
  // 500: Internal Server Error, 503: Service Unavailable
};


// コネクションの確立
//     クロスドメインを指定すると、サーバからの応答は得られますが
//     ブラウザによる制限で得られた応答を処理できません。
//     つまり、ドメイン指定 http://127.0.0.1/remote/1/ は許可されておらず
//     通常は /remote/1/ などの相対パスで指定します。
//     第三引数では非同期通信をするかどうかを指定します。
req.open('GET', '/remote/1/', true);
// req.open('POST', '/remote/1/', true); // ← POST の場合はヘッダを指定ましょう↓
// req.setRequestHeader('Content-Type',  // https://www.qoosky.io/techs/7670044f2d (参考)
//                      'application/x-www-form-urlencoded; charset=UTF-8');


// HTTP リクエストの実行
req.send(null);
// req.send('name=' + encodeURIComponent("日本語です")); // POST の場合は送信データを指定

// 発行済みのリクエストを中止したい場合
// req.abort();

jQuery による Ajax 通信

先程 XMLHttpRequest による Ajax 通信の例で示した内容を jQuery で再現すると以下のようになります。jQuery.ajax() の公式ドキュメントはこちらです。jQuery.get(), jQuery.post(), .load() などへのリンクが記載された Ajax 関連ドキュメントのホームはこちら です。

$(function(){
  $.ajax({
    url: '/remote/1/',
    type: 'GET',
    success: function(data, textStatus, jqXHR){
      console.log('正常');
      console.log(jqXHR.getResponseHeader('Date'));
      console.log(jqXHR.getAllResponseHeaders());
      console.log(data);
    },
    error: function(){
      console.log('エラー');
    }
  });
});

JSONP による外部サイトとの Ajax 通信

JSONP (JSON with Padding) 自体は Ajax とは関係のない概念です。しかしながら外部サイトの JSONP の API は Ajax 通信で呼び出すことが多いため本ページに記載します。JSONP は、あるサイトで取得した JavaScript が外部サイトと HTTP 通信を行うための手法です。名称 JSONP の由来は、HTTP 通信によって、外部サイトが周囲にパディングされた JSON データを返すことが想定されているところにあります。具体的には例えば「padding({"key1": 1, "key2": 2})」が返されます。この場合 JSON は「{"key1": 1, "key2": 2}」でパディングは「padding(」と「)」です。仕組みの都合上、任意のサイトと通信できず外部サイトが JSONP 用の GET リクエスト API を提供している必要があります。

あるドメインから取得したページに記載された JavaScript は、同一のドメインのサーバに対しては、例えば上述の XMLHttpRequest オブジェクトによって同期または非同期に GET/POST などの HTTP リクエストを発行できます。しかしながら、異なるドメインに対して発行された HTTP リクエストのレスポンスは、ブラウザのセキュリティを考慮した制限によって取り扱うことができません。これを回避する手法が JSONP です。提供した JSONP API が悪意あるサイトで呼び出される場合、その悪意あるサイトにユーザが訪れた際、そのユーザの権限で JSONP の API が実行され JSON データが取得されることになります。JSONP の API では悪意あるサイトに取得されてもいいデータのみを提供しましょう。

CORS との違い

JSONP は仕様の隅をつついて実現されたような仕組みです。GET リクエストしか行えないなど不便な点も有しています。そこで「Cross-origin resource sharing (CORS)」とよばれる仕組みが標準化されました。JSONP が GET しかできないのに対して CORS では POST, PUT, DELETE なども発行できます。JSONP は任意の外部サイトから実行できてしまうのに対して CORS では許可する外部サイトを指定できます。ただし、古いブラウザがサポートしていない、サーバ側の設定が必要という欠点も有しています。

シンプルなサンプルコード

はてなブックマークは、あるサイトのブックマーク情報を JSONP で提供しています。これを呼び出してコンソール出力するサンプルを以下に示します。script タグを body タグの配下に自動生成して、生成された script タグの src 属性ではてなブックマークの JSONP API に GET リクエストを発行します。はてなブックマークの JSONP API は JSON を show() でパディングした結果を返します。これが script タグの性質上 JavaScript のソースコードとして評価され、show() 内でブックマーク情報が格納された JSON を利用できます。Ajax とは全く関係がないことが分かります。

index.html

<body>
</body>
<script type="text/javascript" src="sample.js"></script>

sample.js

var scr = document.createElement('script');
scr.type = 'text/javascript';
scr.src = 'http://b.hatena.ne.jp/entry/jsonlite/?callback=show&url='
  + encodeURIComponent('http://www.example.com/');
document.getElementsByTagName('body').item(0).appendChild(scr);

function show(data) {
  console.log(data.count); //=> 108
  for(var i = 0; i < data.count; ++i) {
    console.log(data.bookmarks[i].comment); //=>
      // 記念ブクマ
      // 例のどっとこむ。あんまり気にした事なかったけど...
      // なんだこれは
      // サンプルコメントです
      // very cool website
      // ...
  }
}

Ajax で JSONP を利用

先程の JSONP のサンプルコードを Ajax で再現すると以下のようになります。

index.html

<script type="text/javascript" src="jquery-1.11.2.min.js"></script>
<script type="text/javascript" src="sample.js"></script>

sample.js

function show(data) {
  console.log(data.count); //=> 108
  for(var i = 0; i < data.count; ++i) {
    console.log(data.bookmarks[i].comment); //=>
      // 記念ブクマ
      // 例のどっとこむ。あんまり気にした事なかったけど...
      // なんだこれは
      // サンプルコメントです
      // very cool website
      // ...
  }
}

$(function(){
  $.ajax({
    type: 'GET',
    url: 'http://b.hatena.ne.jp/entry/jsonlite/?callback=show&url='
      + encodeURIComponent('http://www.example.com/'),
    dataType: 'script'
  });
});

スマートに記述

dataType に jsonp を指定すると success に登録した関数を利用できます。

index.html

<script type="text/javascript" src="jquery-1.11.2.min.js"></script>
<script type="text/javascript" src="sample.js"></script>

sample.js

$(function(){
  $.ajax({
    type: 'GET',
    url: 'http://b.hatena.ne.jp/entry/jsonlite/?callback=?&url='
      + encodeURIComponent('http://www.example.com/'),  // ↑ show ではなく '?'
    dataType: 'jsonp', // ← script ではなく jsonp
    success: function(data){
      console.log(data.count);
      for(var i = 0; i < data.count; ++i) {
        console.log(data.bookmarks[i].comment);
      }
    }
  });
});

更にスマートに記述

jQuery.getJSON() を利用すると更にスマートな記述ができます。

index.html

<script type="text/javascript" src="jquery-1.11.2.min.js"></script>
<script type="text/javascript" src="sample.js"></script>

sample.js

$(function(){
  $.getJSON(
    'http://b.hatena.ne.jp/entry/jsonlite/?callback=?&url='
      + encodeURIComponent('http://www.example.com/'),
    function(data){
      console.log(data.count);
      for(var i = 0; i < data.count; ++i) {
        console.log(data.bookmarks[i].comment);
      }
    }
  );
});

複数の非同期処理が完了したら実行する処理を登録

Ajax は Deferred オブジェクトの一種です。jQuery.when() に複数の Ajax を登録しておくことで、それらがすべて完了したタイミングで実行される処理を記述できます。Ajax 処理が成功 (resolved) したか失敗 (rejected) したかによって処理を分岐できます。

  • deferred.done(): すべてが resolved の処理
  • deferred.fail(): 少なくとも一つが rejected の処理
  • deferred.then(): 「すべてが resolved」と「少なくとも一つが rejected」それぞれの処理
  • deferred.always(): すべてが完了したら resolved/rejected に依らず実行する処理

上述の「jQuery による Ajax 通信」を二つ登録する例を以下に示します。

var mySuccess = function(a1,a2){
  // 引数 a1,a2 は d1,d2 が返す配列:
  // [ data, statusText, jqXHR ]
  console.log('正常');
  console.log(a1[2].getResponseHeader('Date'));
  console.log(a2[2].getResponseHeader('Date'));
  console.log(a1[2].getAllResponseHeaders());
  console.log(a2[2].getAllResponseHeaders());
  console.log(a1[0]);
  console.log(a2[0]);
};

var myFailure = function(a1,a2){
  console.log('エラー');
};

var myFunc = function(a1,a2){
  console.log(a1);
  console.log(a2);
};

$(function(){
  // success/error は $.when 以下に登録できるため URI で指定
  // するタイプの $.ajax を利用するとシンプルにまとまります。
  var d1 = $.ajax('/remote/1/'); // http://api.jquery.com/jQuery.ajax/
  var d2 = $.ajax('/remote/2/');

  $.when(d1, d2).done(mySuccess).fail(myFailure);
  // $.when(d1, d2).then(mySuccess, myFailure); // としても同じ意味

  // success/error に依らない汎用関数を登録したい場合:
  // $.when(d1, d2).always(myFunc);
});
関連ページ
    概要 AngularJS における HTTP 通信では、こちらのページに記載した XMLHttpRequest オブジェクトが利用されています。AngularJS で HTTP 通信を行うためのサンプルコードを以下に示します。公式ページはこちらです。 index.html <!DOCTYPE html> <html lang="ja" ng-app="myApp"> <head> <met
    概要 データをもとにして DOM を操作する D3.js (Data-Driven Documents) の基本的な使い方を記載します。特にバージョンは v5 を対象とします。 Gallery D3 API Reference Hello world HTTP サーバ 外部からデータを読み込むために CORS
    概要 よく使う python ライブラリのサンプルコード集です。 JSON #!/usr/bin/python # -*- coding: utf-8 -*- import json arr = [1, 2, {'xxx': 3}] # オブジェクト ←→ JSON 文字列 jsonStr = json.dumps(arr) arr2 = json.loads(jsonStr) # オ