読者です 読者をやめる 読者になる 読者になる

感謝のプログラミング 10000時間

たどり着いた結果(さき)は、感謝でした。

jQuery & AjaxでサーバからのJSONレスポンスを読み込んでブラウザに表示させる。

Ajax jQuery
<スポンサーリンク>

Ajaxとは

AjaxというのはAsynchronous JavaScript and XMLの略で、Webブラウザで実行されるJavaScriptプログラムとWebサーバとの間でデータを送受信する仕組みのこと。
Ajaxの処理を始めるために必要な設定項目は以下の通り。

  • 処理を開始するきっかけ
  • Webサーバへ送信する時に必要なURL
  • パラメータ

Cross-Origin Resource Sharing

これはすごく勉強になった。
Cross-Origin Resource Sharingという仕様に対応しているWebブラウザとWebサーバであれば、アクセス制限の可否を制御できるというもの。
しかし、GETやPOSTなど、URLを直接指定して通信できるのは、WebサーバやWebブラウザによって許可されていない限り、同じドメインのWebサーバのみに制限される。
前にGoogleのWebページをAjaxで取得しようとして失敗したのはたぶんこの製薬のせいだったはず。

JSONとは

JSONとはJavaScript Object Nationの略で、JavaScriptのオブジェクトの表記方法に基づく、データ記述言語の一種である。

JSONPとは

JSONPという言葉をよく見る。
JSONPというのは、JSON with Paddingの略で、受信したデータをすぐにJavaScriptの関数で実行する方法らしい。
XMLやJSONを受信する場合は、Webページと異なるドメインへのアクセスは制限されるものの、JSONPではその制限がない。
まだちょっとよくわかっていないけれど、
サーバ側の方でJavaScriptを生成し、それをブラウザに送りつける。
で、その送られてきたJavaScriptをブラウザ側のスクリプトにに組み込んで実行するイメージのようだ。

そうか!送られてきたパラメータを元に、サーバ側がJavaScriptを生成する。
で、JavaScriptを埋め込んで(padding!)ブラウザに返す。
ブラウザはそのJavaScriptを実行する仕組み、それがJSONPのようだ。たぶん。

jQuery AjaxでサーバからJSONを受け取るサンプルを作ってみる。

まずはサーバ側

package controller;

import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import bean.Person;

import com.google.gson.Gson;

public class JsonTestServlet extends HttpServlet {
    public void doGet(HttpServletRequest req, HttpServletResponse res) 
                throws ServletException, IOException {
        
        res.setContentType("application/json; charset=utf-8");
        PrintWriter out = res.getWriter();
        
        Gson gson = new Gson();
        Person person = new Person();
        person.setAge(28);
        person.setName("sho322");
        person.setJob("Programmer");
        out.println(gson.toJson(person));
    }
}

このサーブレットにリクエストを飛ばすと、以下のように返ってくる。

→(リクエスト)http://127.0.0.1:8082/rest/jsontest
{"name":"sho322","job":"Programmer","age":28}

ポートが[8082]なことに注目。
それをApache(80番ポート)上のAjaxで読み込もうとすると・・・

<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8">
<title> サーバから送られてきたJSONを読み込んでみる </title>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js"></script>
<script>
$(document).ready(function() {
	$('#load').click(function(e) {
		$.getJSON('http://127.0.0.1:8082/rest/jsontest',
			{},
			function(data, textStatus) {
				console.log(textStatus);
				console.log(data);
				$('#result').text(data);
			});
	});
});
</script>
<body>
<form>
<input type="button" id="load" value="Load">
</form>
<div id="result"></div>
</body>
</html>

localhostの8082ポート(もしTomcatデフォルトだったら8080だね)にAjaxでリクエストを投げたら、こんなエラーが出る。

http://localhost is not allowed by Access-Control-Allow-Origin. 

このエラーの原因はStockoverflowに書いてある。
http://stackoverflow.com/questions/12683530/origin-http-localhost-is-not-allowed-by-access-control-allow-origin
このエラーはいわゆるXML HttpRequest same origin policyによるものだ。

ajaxのリクエストは異なるポートに対して制限されている。
つまり、Apacheで動いている80番ポートから、8082ポートに対してはリクエストができない。
この制限はクロスサイトスクリプティング(XSS)を防ぐためのものである。

で、ここでやっとわかったんだけど、こういう制限をなんとかするために、JSONPがあるのかと。
JSONPは、異なるドメインのサーバや、異なるポートで動いているサーバからJSONを受け取るための仕組みなんだと。

このように、サイトをまたがってAjaxを使えない問題の解決策はいくつかあって、

  • サーバ側でヘッダーに「Access-Control-Allow-Origin: *」を加える
  • JSONPを使う

などがあるようだ。
今回は簡単にサーバ側に「Access-Control-Allow-Origin: *」を加えてみた。
そして、受け取ったJSONをStringに変換する

var jsStr = JSON.stringify(json);

というメソッドも使ってみた。
今回は、サーバから受け取ったJSONオブジェクトを使って、ブラウザに結果を表示している。
まずはサーバ側のJavaを見てみる。
Gsonを使って、JavaでJSONを返している。

package controller;

import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import bean.Person;

import com.google.gson.Gson;

public class JsonTestServlet extends HttpServlet {
    public void doGet(HttpServletRequest req, HttpServletResponse res) 
                throws ServletException, IOException {
        
        //res.setContentType("application/json; charset=utf-8");
        //「jsonp1372612532406」みたいな文字が格納される
        String callback = req.getParameter("callback");
        System.out.println(callback);
        res.setContentType("text/javascript; charset=utf-8");
        res.setHeader("Access-Control-Allow-Origin", "*");
        PrintWriter out = res.getWriter();
        
        Gson gson = new Gson();
        Person person = new Person();
        person.setAge(28);
        person.setName("sho322");
        person.setJob("Programmer");
        //このcallbackを付けないと、JavaScript側でデータを受け取れないみたい
        out.println( callback + "(" + gson.toJson(person) + ")");
        //out.println("(" + gson.toJson(person) + ")");
    }
}

ここのcallbackをつけて返すところが鬼門というか、ハマリどころで、これにずいぶん悩まされた。
コメントアウトしてあるけど、リクエストにくっついてきたcallbackの文字列を一緒に渡さないとダメみたい。
理由とか背景はあとで調べる。

で、これにリクエストを飛ばすと、

jsonp1372612532406(Object {name: "sho322", job: "Programmer", age: 28} )

みたいな感じでレスポンスが戻ると思うんだけど、これをAjax & jQueryで処理する。

<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8">
<title> サーバから送られてきたJSONを読み込んでみる </title>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js"></script>
<script>
$(function() {
	//.ajaxでJSONPを実行する方法
	$.ajax({
		url :"http://localhost:8082/rest/jsontest",
		dataType : "jsonp",
		success : function(json) {
			console.log(json); //★1
			//$('#result').text(json);
			setResult(json);
		}
	});
});

function setResult(json) {
	var jsStr = JSON.stringify(json);
	console.log(jsStr);
	var jsObject = JSON.parse(jsStr);
	console.log(jsObject);
	$("#name").text("name:" + jsObject["name"]);
	$("#job").text("job:" + jsObject["job"]);
	$("#age").text("age:" + jsObject["age"]);
}
</script>
<body>
<form>
<input type="button" id="load" value="Load">
</form>
<div id="name"></div>
<div id="job"></div>
<div id="age"></div>
</body>
</html>

★1のところのconsole.logでは見事に

Object {name: "sho322", job: "Programmer", age: 28} 

と受け取っていて、それをsetResult関数に渡している。
ちなみに今回は.ajaxを使う例だけど、他にもscriptタグをヘッダに埋め込む方法もある。

で、このブラウザを開いた結果・・・
ttp://localhost/jQuery/jsontest2.html
ブラウザに表示されるのは・・・

name:sho322
job:Programmer
age:28

という結果!
やっとサーバから返ってきたJSONを読み込んで表示させることができた。
かなり時間がかかった!

参考文献

jQuery ポケットリファレンス (POCKET REFERENCE)

jQuery ポケットリファレンス (POCKET REFERENCE)


ちょこちょこっと調べるのに重宝した。
まさにポケットリファレンスだ。

jQuery本格入門 ?JavaScript開発・デザイン効率化の基礎から Ajax・QUnitまで

jQuery本格入門 ?JavaScript開発・デザイン効率化の基礎から Ajax・QUnitまで


ちょっとだけ参考にした。

感謝のプログラミング

今回で感謝のプログラミングは【492時間目】
10000時間まで、あと【9508時間】

他にもJMSの勉強もしたんだけど、うまくまとめれなかったから記事にしなかった。来週はJBossの復習をしたい。