XMLHttpRequest について
XMLHttpRequest とは?
■Ajax について
JavaScript を利用すると、「非同期なデータの送受信」を行なう事ができます。
また、JavaScript は「HTML の内容を動的に変更」する事もできます。
「非同期通信」と「ダイナミックHTML」の2つの技術を組み合わせたものを Ajax といいます。
(Asynchronous JavaScript + XML)
Ajax を利用すると、「非同期通信」を行い外部から最新のデータを取得しながら、「ダイナミックHTML」で HTML の内容をリアルタイムに更新する事ができます。
ページを再読込する事無く最新の情報をインタラクティブに供給する事ができます。
このページでは、「非同期通信」の方法について解説しています。
「ダイナミックHTML」については、「ドキュメントオブジェクトモデル(DOM)について」で解説しています。
■XMLHttpRequest とは?
XMLHttpRequest (XHR) は、非同期なデータの通信を実現するための API です。
元々は、「Internet Explorer 5」 で「ActiveX オブジェクト」として実装されましたが、他のブラウザでも互換性のある同等の機能が実装されはじめ、 XMLHttpRequest として利用できるようになりました。
Internet Explorer は 7.0 以降であれば、XMLHttpRequest 側の API が利用できます。
また、W3C により仕様が勧告されています。
http://www.w3.org/TR/XMLHttpRequest/
■XMLHttpRequest オブジェクトを作成する
Internet Explorer 6 以前では、「ActiveX オブジェクト」を利用する必要があるためブラウザによって作成方法が異なります。
そこで、XMLHttpRequest オブジェクトを作成するためのラッピング関数を用意します。
XMLHttpRequest オブジェクトを作成する関数です。
XMLHttpRequest オブジェクトを作成する
function XMLHttpRequestCreate(){
// XMLHttpRequest に対応している
if(XMLHttpRequest){
// XMLHttpRequest オブジェクトを作成
return new XMLHttpRequest();
}else{
// Internet Explorer 用
try{
return new ActiveXObject('MSXML2.XMLHTTP.6.0');
}catch(e){}
try{
return new ActiveXObject('MSXML2.XMLHTTP.3.0');
}catch(e){}
try{
return new ActiveXObject('MSXML2.XMLHTTP');
}catch(e){}
}
// 未対応
return null;
}
JavaScript 側のデータ送受信処理について
■「アクセス先 URL」と「HTTP メソッド」を指定する
XMLHttpRequest オブジェクトを使って送信先を指定するには、open() メソッドを使用します。
このメソッドを呼び出した時点では、まだ接続は行われません。
XMLHttpRequest.open(HTTPメソッド, アクセス先のURL , 非同期実行か? , ユーザー名 , パスワード);
| 第01引数 | HTTP メソッドを指定、"GET"、"POST"、"HEAD"、"PUT"、"DELETE"、"OPTIONS" など |
| 第02引数 | アクセス先 の URL を指定 |
| 第03引数(略可) | 非同期実行なら ture、同期実行なら false、デフォルトは true |
| 第04引数(略可) | ユーザー名 |
| 第05引数(略可) | パスワード |
| 戻り値 | なし |
■第01引数 HTTP メソッドを指定する
"GET" や "POST" などを指定します。
GET メソッドは、URL 文字列にパラメータを付加する事で情報を送ります。送信できるサイズに限界があるので、パラメータの総量が少ない時に利用します。
POST メソッドは、大容量のデータを送る事ができます。
■第02引数 アクセスするURLとパラメータを指定
アクセスしたい URL を指定します。
ウェブコンテンツとして使用する場合、「JavaScript を実行しているURL」 と 「アクセス先の URL」 のドメインが異なる場合、
サンドボックスの制限によりアクセスする事ができません。
ブラウザが「XMLHttpRequest Level 2」に対応していればサンドボックス制限を解除する事もできます。
ブラウザの拡張機能で実行する場合など、クロスドメイン通信の制限が無い環境もあります。
第01引数で GET メソッドを選択した場合、送信したいデータを URL に含めます。
■第03引数 非同期実行であるか
true の場合、非同期実行となります。JavaScript を動作させつつ、裏で通信を行うことができます。
false の場合、同期実行となります。通信処理が完了するまで JavaScript の動作が停止します。
デフォルトは true です。
■第04~05引数 ユーザー名とパスワード
認証が必要な場合、第04引数にユーザー名、第05引数にパスワードを指定します。
引数を省略した状態で、アクセス先で認証が必要だった場合、その場で認証入力用のダイアログボックスが表示されます。
使用例です。
GET メソッドで "http://hakuhin.jp/test.cgi" を指定する
// XMLHttpRequest オブジェクトを作成
var xhr = XMLHttpRequestCreate();
// GET メソッドで接続先 URL を指定
var url = "http://hakuhin.jp/test.cgi";
xhr.open("GET",url);
// XMLHttpRequest オブジェクトを作成する関数
function XMLHttpRequestCreate(){
if(XMLHttpRequest){
return new XMLHttpRequest();
}else{
try{
return new ActiveXObject('MSXML2.XMLHTTP.6.0');
}catch(e){}
try{
return new ActiveXObject('MSXML2.XMLHTTP.3.0');
}catch(e){}
try{
return new ActiveXObject('MSXML2.XMLHTTP');
}catch(e){}
}
return null;
}
POST メソッドで "./cgi-bin/test.cgi" を指定する。認証用にユーザー名:"myname" パスワード:"mypassword" を指定する。
// XMLHttpRequest オブジェクトを作成
var xhr = XMLHttpRequestCreate();
// POST メソッドで接続先 URL を指定
var url = "./cgi-bin/test.cgi";
xhr.open("POST",url,true,"myname","mypassword");
// XMLHttpRequest オブジェクトを作成する関数
function XMLHttpRequestCreate(){
if(XMLHttpRequest){
return new XMLHttpRequest();
}else{
try{
return new ActiveXObject('MSXML2.XMLHTTP.6.0');
}catch(e){}
try{
return new ActiveXObject('MSXML2.XMLHTTP.3.0');
}catch(e){}
try{
return new ActiveXObject('MSXML2.XMLHTTP');
}catch(e){}
}
return null;
}
■送信するデータのコンテンツタイプを指定する
送信したいデータのコンテンツタイプを設定するには、setRequestHeader() メソッドを使用します。
第01引数に、"Content-Type" という文字列を指定します。
XMLHttpRequest.setRequestHeader("Content-Type","コンテンツタイプの種類");
| 第01引数 | "Content-Type" という文字列を指定 |
| 第02引数 | コンテンツタイプの種類を指定 |
| 戻り値 | なし |
■第02引数 コンテンツタイプの種類を指定
コンテンツタイプを指定します。
種類の一例です。
| 文字列 | 説明 |
| text/plain | テキスト文書 |
| text/html | HTML 文書 |
| application/xml | XML 文書 |
| application/json | JSON 文書 |
| application/x-www-form-urlencoded | 『変数名=値&変数名=値&変数名=値&変数名=値』の形式 例えば「user=taro」「age=18」「blood=b」という3つのパラメータを送信したい場合 『user=taro&age=18&blood=b』 |
■アクセスを開始する
データの送信を開始したい、もしくはデータの受信を開始したい場合は、send() メソッドを使用します。
送信したいデータがある場合、引数にコンテンツタイプにあったデータを文字列で指定します。特に送信したいデータが無く、受信だけしたい場合は、null を指定します。
GET メソッドを使用している場合は、send() メソッドの引数には null を指定して、 URL に引数データを指定します。
XMLHttpRequest.send("データ");
| 第01引数(略可) | 送信したいデータを文字列で指定します。特にない場合は null を指定します。 |
| 戻り値 | なし |
GET メソッドで "http://hakuhin.jp/test.cgi" に "HelloWorld" という文字列を送信する
// 送信したい文字列
var str = "HelloWorld";
// 送信したい URL 先
var url = "http://hakuhin.jp/test.cgi";
// XMLHttpRequest オブジェクトを作成
var xhr = XMLHttpRequestCreate();
// GET メソッドで接続先 URL を指定(送信したいデータを URL に含める)
xhr.open("GET",url + "?" + str);
// コンテンツタイプにテキスト文書を指定
xhr.setRequestHeader("Content-Type","text/plain");
// 送信を開始する
xhr.send(null);
// XMLHttpRequest オブジェクトを作成する関数
function XMLHttpRequestCreate(){
if(XMLHttpRequest){
return new XMLHttpRequest();
}else{
try{
return new ActiveXObject('MSXML2.XMLHTTP.6.0');
}catch(e){}
try{
return new ActiveXObject('MSXML2.XMLHTTP.3.0');
}catch(e){}
try{
return new ActiveXObject('MSXML2.XMLHTTP');
}catch(e){}
}
return null;
}
POST メソッドで "http://hakuhin.jp/test.cgi" に "HelloWorld" という文字列を送信する
// 送信したい文字列
var str = "HelloWorld";
// 送信したい URL 先
var url = "http://hakuhin.jp/test.cgi";
// XMLHttpRequest オブジェクトを作成
var xhr = XMLHttpRequestCreate();
// POST メソッドで接続先 URL を指定
xhr.open("POST",url);
// コンテンツタイプにテキスト文書を指定
xhr.setRequestHeader("Content-Type","text/plain");
// 送信を開始する(送信したいデータを引数にセット)
xhr.send(str);
// XMLHttpRequest オブジェクトを作成する関数
function XMLHttpRequestCreate(){
if(XMLHttpRequest){
return new XMLHttpRequest();
}else{
try{
return new ActiveXObject('MSXML2.XMLHTTP.6.0');
}catch(e){}
try{
return new ActiveXObject('MSXML2.XMLHTTP.3.0');
}catch(e){}
try{
return new ActiveXObject('MSXML2.XMLHTTP');
}catch(e){}
}
return null;
}
■ファイルをアップロードする
こちらで解説しています。
■アクセスが成功したか調べる
XMLHttpRequest のアクセス状態を取得するには、onreadystatechange イベントハンドラを使用します。
このイベントは、send() メソッドを呼び出す前に設定しておく必要があります。
send() メソッドの処理中は、イベントハンドラに登録したコールバック関数が、定期的に繰り返し呼び出されます。
onreadystatechange イベントハンドラの関数内では、readyState プロパティを調べます。
取得できる数値により以下の意味があります。
| 定数 | 値 | 解説 |
| UNSENT | 0 | XMLHttpRequest オブジェクトを作成した状態です。 |
| OPENED | 1 | open() メソッドの呼び出しが完了しています。 |
| HEADERS_RECEIVED | 2 | ヘッダ部の受信は完了しています。 |
| LOADING | 3 | ボディ部を受信中です。 |
| DONE | 4 | send() メソッドの処理が完了しました。(成功失敗に関わらず) |
readyState プロパティで 4 が得られれば、send() メソッドが完了したことがわかります。次に status プロパティとstatusText プロパティを使用します。
status プロパティからは、HTTPステータスコードを取得することができます。
statusText プロパティからは、HTTPステータスコードの意味を文字列で取得することができます。
status プロパティが 0 であれば、エラーとなります。
status プロパティが 200 であれば、通常は受信に成功しています。
送受信が成功したか調べる
// 送信したい文字列
var str = "HelloWorld";
// 送信したい URL 先
var url = "http://hakuhin.jp/test.cgi";
// XMLHttpRequest オブジェクトを作成
var xhr = XMLHttpRequestCreate();
// 送受信状態に変更がある場合に実行されるイベント
xhr.onreadystatechange = function(){
console.log("readyState:" + this.readyState);
switch(this.readyState){
case 0:
console.log("XMLHttpRequest オブジェクトを作成した状態");
break;
case 1:
console.log("open() メソッドの呼び出しが完了している");
break;
case 2:
console.log("ヘッダ部の受信は完了している");
break;
case 3:
console.log("ボディ部を受信中");
break;
case 4:
console.log("send() メソッドの処理が完了");
console.log("status:" + this.status + ":" + this.statusText);
switch(this.status){
case 0:
console.log("エラー");
break;
case 200:
console.log("成功");
break;
default:
console.log("その他の応答");
break;
};
break;
}
}
// POST メソッドで接続先 URL を指定
xhr.open("POST",url);
// コンテンツタイプにテキスト文書を指定
xhr.setRequestHeader("Content-Type","text/plain");
// 送信を開始する(送信したいデータを引数にセット)
xhr.send(str);
// XMLHttpRequest オブジェクトを作成する関数
function XMLHttpRequestCreate(){
if(XMLHttpRequest){
return new XMLHttpRequest();
}else{
try{
return new ActiveXObject('MSXML2.XMLHTTP.6.0');
}catch(e){}
try{
return new ActiveXObject('MSXML2.XMLHTTP.3.0');
}catch(e){}
try{
return new ActiveXObject('MSXML2.XMLHTTP');
}catch(e){}
}
return null;
}
■受信したデータを取得する
readyState プロパティから 4、status プロパティから 200 が得られた場合データの受信に成功していることがわかります。次に responseText プロパティとresponseXML プロパティを使用します。
responseText プロパティからは、受信したデータを文字列として取得することができます。
受信したデータが XML ファイルであれば、responseXML プロパティから、Document オブジェクトを取得する事ができます。XML ファイルではない場合 null となります。
受信したデータを文字列として取得する
// 受信したい URL 先
var url = "http://hakuhin.jp/js.html";
// XMLHttpRequest オブジェクトを作成
var xhr = XMLHttpRequestCreate();
// 送受信状態に変更がある場合に実行されるイベント
xhr.onreadystatechange = function(){
if(this.readyState == 4){
if(this.status == 200){
// 受信した文字列を出力
console.log(this.responseText);
}
}
}
// GET メソッドで接続先 URL を指定
xhr.open("GET",url);
// コンテンツタイプにテキスト文書を指定
xhr.setRequestHeader("Content-Type","text/plain");
// 受信を開始する
xhr.send(null);
// XMLHttpRequest オブジェクトを作成する関数
function XMLHttpRequestCreate(){
if(XMLHttpRequest){
return new XMLHttpRequest();
}else{
try{
return new ActiveXObject('MSXML2.XMLHTTP.6.0');
}catch(e){}
try{
return new ActiveXObject('MSXML2.XMLHTTP.3.0');
}catch(e){}
try{
return new ActiveXObject('MSXML2.XMLHTTP');
}catch(e){}
}
return null;
}
受信したデータを XML として取得する
// 受信したい URL 先
var url = "http://hakuhin.jp/atom.xml";
// XMLHttpRequest オブジェクトを作成
var xhr = XMLHttpRequestCreate();
// 送受信状態に変更がある場合に実行されるイベント
xhr.onreadystatechange = function(){
if(this.readyState == 4){
if(this.status == 200){
if(this.responseXML){
// すべてのノードテキストを出力
NodeTraceTextRecursive(this.responseXML);
}
}
}
}
// GET メソッドで接続先 URL を指定
xhr.open("GET",url);
// コンテンツタイプにテキスト文書を指定
xhr.setRequestHeader("Content-Type","text/plain");
// 受信を開始する
xhr.send(null);
// XMLHttpRequest オブジェクトを作成する関数
function XMLHttpRequestCreate(){
if(XMLHttpRequest){
return new XMLHttpRequest();
}else{
try{
return new ActiveXObject('MSXML2.XMLHTTP.6.0');
}catch(e){}
try{
return new ActiveXObject('MSXML2.XMLHTTP.3.0');
}catch(e){}
try{
return new ActiveXObject('MSXML2.XMLHTTP');
}catch(e){}
}
return null;
}
// ノードの値を出力する再帰関数
function NodeTraceTextRecursive(node){
var str = "";
// 階層パス
str += "name:" + NodeGetPath(node) + " value:" + node.nodeValue;
// コンソール出力
console.log(str);
// 子ノードを検索
var child_nodes = node.childNodes;
var i;
var num = child_nodes.length;
for(i=0;i < num;i++){
var child_node = child_nodes[i];
NodeTraceTextRecursive(child_node);
}
}
// 階層のパスを調べる関数
function NodeGetPath(node){
var str = node.nodeName;
while(true){
var parent = node.parentNode;
if(!parent) break;
str = parent.nodeName + "." + str;
node = parent;
}
return str;
}
■アクセスを中断する
send() メソッドの処理を中断するには、abort() メソッドを使用します。
送受信処理を中断する
// 受信したい URL 先
var url = "http://hakuhin.jp/js.html";
// XMLHttpRequest オブジェクトを作成
var xhr = XMLHttpRequestCreate();
// 送受信状態に変更がある場合に実行されるイベント
xhr.onreadystatechange = function(){
console.log("readyState:" + this.readyState);
if(this.readyState == 4){
console.log("status:" + this.status);
}
}
// GET メソッドで接続先 URL を指定
xhr.open("GET",url);
// コンテンツタイプにテキスト文書を指定
xhr.setRequestHeader("Content-Type","text/plain");
// 受信を開始する
xhr.send(null);
// アクセスを中断する
xhr.abort();
// XMLHttpRequest オブジェクトを作成する関数
function XMLHttpRequestCreate(){
if(XMLHttpRequest){
return new XMLHttpRequest();
}else{
try{
return new ActiveXObject('MSXML2.XMLHTTP.6.0');
}catch(e){}
try{
return new ActiveXObject('MSXML2.XMLHTTP.3.0');
}catch(e){}
try{
return new ActiveXObject('MSXML2.XMLHTTP');
}catch(e){}
}
return null;
}
CGI 側のデータ送受信処理について
■CGI 側のデータ送受信処理について
CGI 側でデータを受け取って結果を出力する方法として、以下のスクリプトを使用してみます。
■Perl を使用する
Perl を利用して、パラメータの受信とパラメータの出力処理を行ってみます。
■GET メソッドを使った受信について
GET メソッドであるか調べるには、$ENV{'REQUEST_METHOD'} が "GET" であるか比較します。
リクエストメソッドを調べる
#!/usr/local/bin/perl
# GETメソッドであるか調べる
if ($ENV{'REQUEST_METHOD'} eq "GET") {
}
GET メソッドであれば、$ENV{'QUERY_STRING'} を取得します。URL 文字列の "?" 以降の文字列を取得することができます。
コンテンツタイプが「application/x-www-form-urlencoded」であれば、
さらに "&" で区切って、"変数名=値" を取り出します。
さらに "=" で区切って、変数名と値を分離し、連想配列に格納します。
受信したパラメータから変数を作成する
#!/usr/local/bin/perl
# パラメータを格納する為の連想配列
%request_param;
# GETメソッドであるか調べる
if ($ENV{'REQUEST_METHOD'} eq "GET") {
# URL の ? 以降の文字列を取得する
$query = $ENV{'QUERY_STRING'};
# "&" で区切って、配列に格納する
@list = split(/&/,$query);
foreach(@list) {
# "=" で区切る
($k,$v) = split(/=/,$_);
# URL デコードを行い値を連想配列に格納する
$request_param{"$k"} = UrlDecode($v);
}
}
#// -----------------------------------------------------
#// URLデコード
#// -----------------------------------------------------
sub UrlDecode {
my $str = @_[0];
$str =~ tr/+/ /;
$str =~ s/%([0-9A-Fa-f][0-9A-Fa-f])/pack('H2', $1)/eg;
return $str;
}
■POST メソッドを使った受信について
POST メソッドであるか調べるには、$ENV{'REQUEST_METHOD'} が "POST" であるか比較します。
リクエストメソッドを調べる
#!/usr/local/bin/perl
# POSTメソッドであるか調べる
if ($ENV{'REQUEST_METHOD'} eq "POST") {
}
POST メソッドであれば、標準入力を使用してパラメータを取得します。
コンテンツタイプが「application/x-www-form-urlencoded」であれば、
さらに "&" で区切って、"変数名=値" を取り出します。
さらに "=" で区切って、変数名と値を分離し、連想配列に格納します。
受信したパラメータから変数を作成する
#!/usr/local/bin/perl
# パラメータを格納する為の連想配列
%request_param;
# POSTメソッドであるか調べる
if ($ENV{'REQUEST_METHOD'} eq "POST") {
# 標準入力からパラメータを取得する
read (STDIN, $query, $ENV{'CONTENT_LENGTH'});
# "&" で区切って、配列に格納する
@list = split(/&/,$query);
foreach(@list) {
# "=" で区切る
($k,$v) = split(/=/,$_);
# URL デコードを行い値を連想配列に格納する
$request_param{"$k"} = UrlDecode($v);
}
}
#// -----------------------------------------------------
#// URLデコード
#// -----------------------------------------------------
sub UrlDecode {
my $str = @_[0];
$str =~ tr/+/ /;
$str =~ s/%([0-9A-Fa-f][0-9A-Fa-f])/pack('H2', $1)/eg;
return $str;
}
■JavaScript 側で読み込めるように文字列を出力する
文字列を出力するには、print 命令を使用します。
HTTP ヘッダには、出力したいデータのコンテンツタイプを指定します。
コンテンツタイプとデータの出力例です。
文字列を出力する
#!/usr/local/bin/perl
# Content-type を出力
print "Content-type:text/plain\n";
# 改行のみでヘッダの終了
print "\n";
# 文字列を出力
print "Hallo World!!";
HTML 文字列を出力する
#!/usr/local/bin/perl
# Content-type を出力
print "Content-type:text/html\n";
# 改行のみでヘッダの終了
print "\n";
# HTML 文字列をヒアドキュメントで出力
print << "EOF";
<html>
<body>
<b>Hallo World!!<b>
</body>
</html>
EOF
# ---
application/x-www-form-urlencoded を出力する
#!/usr/local/bin/perl
# Content-type を出力
print "Content-type:application/x-www-form-urlencoded\n";
# 改行のみでヘッダの終了
print "\n";
# 変数
$user = "hanako";
$age = 16;
$blood = "o";
# パラメータを出力
print "user=" . "$user";
print "&";
print "age=" . "$age";
print "&";
print "blood=" . "$blood";
JSON文字列を出力する
#!/usr/local/bin/perl
# Content-type を出力
print "Content-type:application/json\n";
# 改行のみでヘッダの終了
print "\n";
# 変数
$user = "hanako";
$age = 16;
$blood = "o";
# パラメータを出力
print "{";
print "\"user\":\"" . "$user" . "\",";
print "\"age\":" . "$age" . ",";
print "\"blood\":\"" . "blood" . "\"";
print "}";
■PHP を使用する
PHP を利用して、パラメータの受信とパラメータの出力処理を行ってみます。
■GET メソッドを使った受信について
GET メソッドであるか調べるには、$_SERVER['REQUEST_METHOD'] が "GET" であるか比較します。
リクエストメソッドを調べる
<?php
# GETメソッドであるか調べる
if ($_SERVER['REQUEST_METHOD'] == "GET") {
}
?>
クライアントから GET メソッドで送信されたパラメータを取得するには、$_GETを使用します。
コンテンツタイプが「application/x-www-form-urlencoded」であれば、
中身は連想配列となっています。
GETメソッド のパラメータを取得する
<?php
# パラメータを格納する為の連想配列
$request_param;
# GETメソッドであるか調べる
if ($_SERVER['REQUEST_METHOD'] == "GET") {
foreach ( $_GET as $k => $v){
# URL デコードを行い値を連想配列に格納する
$request_param{"$k"} = urldecode($v);
}
}
?>
■POST メソッドを使った受信について
POST メソッドであるかを取得するには、$_SERVER['REQUEST_METHOD'] が "POST" であるかを調べます。
リクエストメソッドを調べる
<?php
# POSTメソッドであるか調べる
if ($_SERVER['REQUEST_METHOD'] == "POST") {
}
?>
クライアントから POST メソッドで送信されたパラメータを取得するには、$_POSTを使用します。
コンテンツタイプが「application/x-www-form-urlencoded」であれば、
中身は連想配列となっています。
POSTメソッド のパラメータを取得する
<?php
# パラメータを格納する為の連想配列
$request_param;
# POSTメソッドであるか調べる
if ($_SERVER['REQUEST_METHOD'] == "POST") {
foreach ( $_POST as $k => $v){
# URL デコードを行い値を連想配列に格納する
$request_param{"$k"} = urldecode($v);
}
}
?>
■JavaScript 側で読み込めるように文字列を出力する
文字列を出力するには、echo 命令を使用します。
HTTP ヘッダには、出力したいデータのコンテンツタイプを指定します。
コンテンツタイプとデータの出力例です。
文字列を出力する
<?php
# Content-type を出力
header("Content-type:text/plain");
# 文字列を出力
echo "Hallo World!!";
?>
HTML 文字列を出力する
<?php
# Content-type を出力
header("Content-type:text/html");
# HTML 文字列をヒアドキュメントで出力
print <<<EOF
<html>
<body>
<b>Hallo World!!<b>
</body>
</html>
EOF;
?>
application/x-www-form-urlencoded を出力する
<?php
# Content-type を出力
header("Content-type:application/x-www-form-urlencoded");
# 変数
$user = "hanako";
$age = 16;
$blood = "o";
# パラメータを出力
echo "user=" . $user;
echo "&";
echo "age=" . $age;
echo "&";
echo "blood=" . $blood;
?>
JSON文字列を出力する
<?php
# Content-type を出力
header("Content-type:application/json");
# 変数
$user = "hanako";
$age = 16;
$blood = "o";
# パラメータを出力
echo "{";
echo "\"user\":\"" . $user . "\",";
echo "\"age\":" . $age . ",";
echo "\"blood\":\"" . $blood . "\"";
echo "}";
?>
■Ruby を使用する
Ruby を利用して、パラメータの受信とパラメータの出力処理を行ってみます。
■GET メソッドを使った受信について
GET メソッドであるか調べるには、ENV['REQUEST_METHOD'] が "GET" であるか比較します。
リクエストメソッドを調べる
#!/usr/local/bin/ruby
# GETメソッドであるか調べる
if ENV['REQUEST_METHOD'] == "GET" then
end
GET メソッドであれば、ENV['QUERY_STRING'] を取得します。URL 文字列の "?" 以降の文字列を取得することができます。
コンテンツタイプが「application/x-www-form-urlencoded」であれば、
さらに "&" で区切って、"変数名=値" を取り出します。
さらに "=" で区切って、変数名と値を分離し、連想配列に格納します。
受信したパラメータを連想配列に格納する
#!/usr/local/bin/ruby
# ライブラリをロード
require 'cgi'
# パラメータを格納する為の連想配列
request_param = Hash.new
# GETメソッドであるか調べる
if ENV['REQUEST_METHOD'] == "GET" then
# URL の ? 以降の文字列を取得する
query = ENV['QUERY_STRING']
# "&" で区切る
for q in query.split("&") do
# "=" で区切る
k, v = q.split("=",2)
# URL デコードを行い連想配列に格納
request_param[k] = CGI.unescape(v);
end
end
■POST メソッドを使った受信について
POST メソッドであるか調べるには、ENV['REQUEST_METHOD'] が "POST" であるか比較します。
リクエストメソッドを調べる
#!/usr/local/bin/ruby
# POSTメソッドであるか調べる
if (ENV['REQUEST_METHOD'] == "POST") {
}
POST メソッドであれば、標準入力を使用してパラメータを取得します。
コンテンツタイプが「application/x-www-form-urlencoded」であれば、
さらに "&" で区切って、"変数名=値" を取り出します。
さらに "=" で区切って、変数名と値を分離し、連想配列に格納します。
受信したパラメータを連想配列に格納する
#!/usr/local/bin/ruby
# ライブラリをロード
require 'cgi'
# パラメータを格納する為の連想配列
request_param = Hash.new
# POSTメソッドであるか調べる
if ENV['REQUEST_METHOD'] == "POST" then
# 標準入力からパラメータを取得する
query = STDIN.read(ENV['CONTENT_LENGTH'].to_i)
# "&" で区切る
for q in query.split("&") do
# "=" で区切る
k, v = q.split("=",2)
# URL デコードを行い連想配列に格納
request_param[k] = CGI.escape(v);
end
end
■JavaScript 側で読み込めるように文字列を出力する
文字列を出力するには、print 命令を使用します。
HTTP ヘッダには、出力したいデータのコンテンツタイプを指定します。
コンテンツタイプとデータの出力例です。
文字列を出力する
#!/usr/local/bin/ruby
# Content-type を出力
print "Content-Type: text/plain\n"
# 改行のみでヘッダの終了
print "\n"
# パラメータを出力
print "Hallo World!!"
HTML 文字列を出力する
#!/usr/local/bin/ruby
# Content-type を出力
print "Content-Type: text/html\n"
# 改行のみでヘッダの終了
print "\n"
# HTML 文字列をヒアドキュメントで出力
print <<EOF
<html>
<body>
<b>Hallo World!!<b>
</body>
</html>
EOF
application/x-www-form-urlencoded を出力する
#!/usr/local/bin/ruby
# Content-type を出力
print "Content-Type: application/x-www-form-urlencoded\n"
# 改行のみでヘッダの終了
print "\n"
# 変数
user = "hanako"
age = 16
blood = "o"
# パラメータを出力
print "user=" + user
print "&"
print "age=" + age.to_s
print "&"
print "blood=" + blood
JSON文字列を出力する
#!/usr/local/bin/ruby
# Content-type を出力
print "Content-Type: application/json\n"
# 改行のみでヘッダの終了
print "\n"
# 変数
user = "hanako"
age = 16
blood = "o"
# パラメータを出力
print "{"
print "\"user\":\"" + user + "\","
print "\"age\":" + age.to_s + ","
print "\"blood\":\"" + blood + "\""
print "}"
■Python を使用する
Python を利用して、パラメータの受信とパラメータの出力処理を行ってみます。
■GET メソッドを使った受信について
GET メソッドであるか調べるには、os.getenv('REQUEST_METHOD') が "GET" であるか比較します。
リクエストメソッドを調べる
#!/usr/local/bin/python
# coding: utf-8
# ライブラリをロード
import os
# GETメソッドであるか調べる
if os.getenv('REQUEST_METHOD') == "GET" :
print "GET METHOD"
GET メソッドであれば、os.getenv('QUERY_STRING') を取得します。URL 文字列の "?" 以降の文字列を取得することができます。
コンテンツタイプが「application/x-www-form-urlencoded」であれば、
さらに "&" で区切って、"変数名=値" を取り出します。
さらに "=" で区切って、変数名と値を分離し、連想配列に格納します。
受信したパラメータを連想配列に格納する
#!/usr/local/bin/python
# coding: utf-8
# ライブラリをロード
import os
import urllib
# パラメータを格納する為の連想配列
request_param = dict()
# GETメソッドであるか調べる
if os.getenv('REQUEST_METHOD') == "GET" :
# URL の ? 以降の文字列を取得する
query = os.getenv('QUERY_STRING')
# "&" で区切る
for q in query.split('&') :
# "=" で区切る
k, v = q.split("=",2)
# URL デコードを行い連想配列に格納
request_param[k] = urllib.unquote(v)
■POST メソッドを使った受信について
POST メソッドであるか調べるには、os.getenv('REQUEST_METHOD') が "POST" であるか比較します。
リクエストメソッドを調べる
#!/usr/local/bin/python
# coding: utf-8
# ライブラリをロード
import os
# POSTメソッドであるか調べる
if os.getenv('REQUEST_METHOD') == "POST" :
print "POST METHOD"
POST メソッドであれば、標準入力を使用してパラメータを取得します。
コンテンツタイプが「application/x-www-form-urlencoded」であれば、
さらに "&" で区切って、"変数名=値" を取り出します。
さらに "=" で区切って、変数名と値を分離し、連想配列に格納します。
受信したパラメータを連想配列に格納する
#!/usr/local/bin/python
# coding: utf-8
# ライブラリをロード
import os
import sys
import urllib
# パラメータを格納する為の連想配列
request_param = dict()
# POSTメソッドであるか調べる
if os.getenv('REQUEST_METHOD') == "POST" :
# 標準入力からパラメータを取得する
query = sys.stdin.read(int(os.getenv('CONTENT_LENGTH')))
# "&" で区切る
for q in query.split('&') :
# "=" で区切る
k, v = q.split("=",2)
# URL デコードを行い連想配列に格納
request_param[k] = urllib.unquote(v)
■JavaScript 側で読み込めるように文字列を出力する
文字列を出力するには、「sys.stdout.write() メソッド」を使用します。
HTTP ヘッダには、出力したいデータのコンテンツタイプを指定します。
コンテンツタイプとデータの出力例です。
文字列を出力する(文字コードを UTF-8 とする)
#!/usr/local/bin/python
# coding: utf-8
# ライブラリをロード
import sys
# Content-type を出力
sys.stdout.write("Content-Type: text/plain\n\n")
# パラメータを出力
sys.stdout.write("Hallo World!!")
HTML 文字列を出力する(文字コードを UTF-8 とする)
#!/usr/local/bin/python
# coding: utf-8
# ライブラリをロード
import sys
# Content-type を出力
sys.stdout.write("Content-Type: text/html\n\n")
# HTML 文字列をトリプルクォートで囲んで出力
sys.stdout.write("""
<html>
<body>
<b>Hallo World!!<b>
</body>
</html>
""")
application/x-www-form-urlencoded を出力する(文字コードを UTF-8 とする)
#!/usr/local/bin/python
# coding: utf-8
# ライブラリをロード
import sys
# Content-type を出力
sys.stdout.write("Content-Type: application/x-www-form-urlencoded\n\n")
# 変数
user = "hanako"
age = 16
blood = "o"
# パラメータを出力
sys.stdout.write("user=" + user)
sys.stdout.write("&")
sys.stdout.write("age=" + str(age))
sys.stdout.write("&")
sys.stdout.write("blood=" + blood)
JSON文字列を出力する(文字コードを UTF-8 とする)
#!/usr/local/bin/python
# coding: utf-8
# ライブラリをロード
import sys
# Content-type を出力
sys.stdout.write("Content-Type: application/json\n\n")
# 変数
user = "hanako"
age = 16
blood = "o"
# パラメータを出力
sys.stdout.write("{")
sys.stdout.write("\"user\":\"" + user + "\",")
sys.stdout.write("\"age\":" + str(age) + ",")
sys.stdout.write("\"blood\":\"" + blood + "\"")
sys.stdout.write("}")
クロスドメイン通信について
■クロスドメイン通信について
JavaScript でクロスドメイン通信を実現するためには以下の方法があります。
■JSONP を使用する
<script> タグは、どのドメインに存在する JavaScript ファイルであっても読み込んで実行する事ができます。
そこで、<script> タグで指定するリンク先を CGI ページにしておいて、CGI 側は好きな JavaScript コードを返すようにすれば、クロスドメイン通信が実現できます。
ほとんどのブラウザで動作させる事ができます。ソーシャルサイトの API サービスなど幅広く利用されています。
JSONPは 「JSON with padding」の略です。
■クライアント側の処理について
HTMLScriptElement を動的に生成します。
スクリプトエレメントを動的に作成する(クライアント側)
// スクリプトエレメントを作成する
var element = document.createElement("script");
src プロパティに CGI のアドレスを設定します。
CGI にパラメータを送信したい場合は、URL の最後に "?" を付けてGET メソッドとしてパラメータを付加します。
src プロパティにアドレスを指定する(クライアント側)
// スクリプトエレメントを作成する
var element = document.createElement("script");
// CGI のアドレスをセット
element.src = "http://hakuhin.jp/test.cgi";
「user=taro」「age=18」「blood=b」という3つのパラメータを渡しつつ src プロパティにアドレスを指定する(クライアント側)
// スクリプトエレメントを作成する
var element = document.createElement("script");
// CGI のアドレスをセット
element.src = "http://hakuhin.jp/test.cgi?user=taro&age=18&blood=b";
body のノードに登録して HTMLScriptElement を動作させる
// スクリプトエレメントを作成する
var element = document.createElement("script");
// CGI のアドレスをセット
element.src = "http://hakuhin.jp/test.cgi?user=taro&age=18&blood=b";
// body のノードに登録する
document.body.appendChild(element);
■サーバ側の処理について
クライアント側の JavaScript でグローバルな関数を用意します。
グローバルなコールバック関数を用意する(クライアント側)
function ScriptLoadCompleteFunc(param){
console.log("通信が完了した:" + param);
}
サーバ側の JavaScirpt からグローバル関数を実行する事で、通信完了を知りつつデータを取得することができます。
引数からデータを渡します。
コールバック関数を実行し、引数からは Object 形式のデータを渡す(サーバ側)
// コールバック関数を実行する
ScriptLoadCompleteFunc({a:0,b:1,c:2});
コールバック関数を実行し、引数からは JSON 形式のデータを渡す(サーバ側)
// コールバック関数を実行する
ScriptLoadCompleteFunc({"a":0,"b":1,"c":2});
送信パラメータに「実行したいコールバック関数"名"」を渡し、サーバー側で関数名を受け取り、その関数を実行します。
これは、様々なサービスで利用されている手法です。
以下の URL にアクセスしてみると JavaScript ファイルが取得できるのがわかります。
URL から「callback=test_function」というパラメータを渡します。するとサーバ側で 「"callback" に格納された名前の関数を実行する」 JavaScript コードを返します。
■ はてなブックマークエントリー情報を取得する API
http://b.hatena.ne.jp/entry/jsonlite/?url=http%3A%2F%2Fwww.hatena.ne.jp%2F&callback=test_function
■ Twitter にて "JavaScript" で検索した際の情報を取得する API
http://search.twitter.com/search.json&callback=test_function&q=JavaScript
クライアント側からサーバ側のコールバック関数名を指定できるという事は、複数の通信処理を同時に行うことができるようになるという事になります。
twitter の API を使って検索要求を複数同時に発行する例です。
twitter の検索を複数同時に要求する
// ユニークID生成用変数
var unique_id = 0;
// 検索したい文字
var query = [
"html5",
"JavaScript",
"CSS3.0"
];
var i;
for(i=0;i < query.length;i++){
// twitter で検索する
TwitterSearch(query[i],function(param){
console.log(param);
});
}
// twitter で検索する関数(第01引数:検索したい文字,第02引数:コールバック関数)
function TwitterSearch(q,func){
// ユニークなコールバック関数用の名前を生成
var callback_name = "callback_function_" + unique_id;
unique_id ++;
// スクリプトエレメントを作成する
var element = document.createElement("script");
// window 直下に受信用コールバック用関数を生成
window[callback_name] = function(param){
// スクリプトエレメントをノードから外す
element.parentNode.removeChild(element);
// 受信用コールバック関数を格納しているプロパティを破棄
delete window[callback_name];
// コールバック関数を実行
func(param);
}
// CGI のアドレスをセット
var url = "http://search.twitter.com/search.json?callback=" + callback_name + "&q=" + q;
element.src = url;
console.log("URL:" + url);
// body のノードに登録する(通信開始)
document.body.appendChild(element);
}
■クライアント側の処理の注意点
外部に存在する JavaScript を実行できてしまうので、不特定のアドレス先にアクセスできるような処理になっていると危険です。
信頼できるアドレスのみアクセスするよう注意します。
■XMLHttpRequest Level2 を使用する
XMLHttpRequest Level2 に対応しているブラウザであれば、XMLHttpRequest を利用してクロスドメイン通信を実現することができます。
Opera は 11.50 の時点ではまだ未対応のようです。
また、W3C により「XMLHttpRequest Level2」の仕様が勧告されています。
http://www.w3.org/TR/XMLHttpRequest2/
■クロスオリジンリソースシェアリング(CORS)について
「プロトコル、ドメイン、ポート番号」の3つからなる識別情報をオリジンといいます。
ブラウザが、「現在閲覧しているページのオリジン」から、「別のオリジンに存在するリソース」にアクセスした際に、
「別のオリジン」であるサーバー側で、「アクセスを許可するかどうか」を設定することができます。
この仕様を、クロスオリジンリソースシェアリングといいます。
W3C により「Cross-Origin Resource Sharing」の仕様が勧告されています。
http://www.w3.org/TR/cors/
■サーバー側の処理について
サーバ側の HTTP ヘッダにポリシー情報を追加することで、クロスオリジン通信が実現できます。
"Access-Control-Allow-Origin:" にオリジンを指定すると、そのオリジンからのアクセスを許可します。
入力例です。
すべてのオリジンからのアクセスを許可する
Access-Control-Allow-Origin: *
hakuhin.jp からのアクセスを許可する
Access-Control-Allow-Origin: http://hakuhin.jp
.htaccess ファイルからまとめてポリシー情報を付加する例です。
.htaccess ファイルにてクロスオリジン通信を許可する
Header append Access-Control-Allow-Origin: *
CGI から HTTP ヘッダにポリシー情報を付加する例です。
すべてのクロスオリジン通信を許可する(Perl)
#!/usr/local/bin/perl
print "Access-Control-Allow-Origin: *\n";
print "Content-type: text/plain\n";
print "\n";
print "Hallo World!!";
すべてのクロスオリジン通信を許可する(PHP)
<?php
header("Access-Control-Allow-Origin: *");
header("Content-type: text/plain");
echo "Hallo World!!";
?>
すべてのクロスオリジン通信を許可する(Ruby)
#!/usr/local/bin/ruby
print "Access-Control-Allow-Origin: *\n"
print "Content-type: text/plain\n"
print "\n"
print "Hallo World!!"
すべてのクロスオリジン通信を許可する(Python)
#!/usr/local/bin/python
# coding: utf-8
# ライブラリをロード
import sys
# Content-type を出力
sys.stdout.write("Access-Control-Allow-Origin: *\n")
sys.stdout.write("Content-type: text/plain\n")
sys.stdout.write("\n")
sys.stdout.write("Hallo World!!")
■サーバ側の処理の注意点
クロスオリジンリソースシェアリングは、アクセスの遮断はできません。
アクセス制限は、ブラウザが自主的にデータを受信できないようにしているだけのレベルであり、ソケット接続などからアクセスを試みるとデータの受信は可能です。
よって、公開する機密情報の取り扱いに注意します。
