トップ 差分 一覧 ソース 検索 ヘルプ PDF RSS ログイン

JSON

JSONP

JSONは情報をやり取りする際のフォーマットとして頻繁に使われるようになりました。

ただ、外部サービスと通信する際に、AjaxでJSONをやり取りするには「クロスドメイン」という壁があります。つまり、他ドメインに属するファイルはAjaxでは基本的に取得出来ません。

そこでJSONPの出番です。

JSONPはJSON with Paddingの略称です。Paddingは(本来は不要なものの)付け足しという意味です。

-- Wikipediaより --

JSONP(JSON with padding)とは、scriptタグを使用してクロスドメインな(異なるドメインに存在する)データを取得する仕組みのことである。HTMLのscriptタグ、JavaScript(関数)、JSONを組み合わせて実現される。

仕組

ウェブブラウザなどに実装されている「同一生成元ポリシー」という制約により、Webページは通常、自分を生成したドメイン以外のドメインのサーバと通信することはできない。 しかし、HTMLのscriptタグのsrc属性には別ドメインのURLを指定して通信することができるという点を利用することによって別ドメインのサーバからデータを取得することが可能になる。JSONPでは、通常、上記src属性のレスポンスの内容はjavascript関数呼び出しの形式となるため、src属性に指定するURLにその関数の名前をクエリ文字列の形式で付加する。一般的な方法では、この時に指定する関数名はWebページ側ですでに定義されているコールバック用の関数の名前になる。関数名を渡すリクエストパラメータの名前はサーバとクライアント間で事前に取り決めておく必要がある。例えば(callbackというパラメータ名でparseResponseという関数名を渡す場合)

   <script type='text/javascript' 
    src='http://another.domain.example.com/getjson?callback=parseResponse'> 

通常は、上記リクエストのレスポンスとして、JSON形式のデータを引数とする関数の呼び出し文が返される。 この関数の呼び出し文がブラウザにより解釈・実行されることで、データの受信完了の検知とコールバック処理が可能になっている。 上記の例では、parseResponseという関数の呼び出し文が返される。

 parseResponse({"Name":"Smith","Rank":7})

注意点

JSONPでは、CSRF(cross-site request forgery)に対する脆弱性に注意が必要である。 このscriptタグを使う方法では同一生成元ポリシーが適用されず、またサーバのエンドポイントは外部に公開されているため、悪意のあるサイトが自分のページにscriptタグを埋め込み、別のサイトのJSONデータを取得するといったことが可能である。このため、機密情報や個人情報などのデータを取り扱うには不適切である。 また、scriptタグを埋め込む側においては、リモートサイトは任意の内容のデータをページに差し込むことが可能であるため、そのリモートサイトが悪意のあるサイトである場合やJavaScriptインジェクションに対する脆弱性がある場合は、その脆弱性を突かれることで、アカウント情報を盗まれたり、元のサイトも影響を受けたりする可能性がある。データを提供するサーバ側では、リクエストの正当性を検証するのが適切である。 但し、Cookieだけを使用した検証は、CSRFに対して脆弱であるため、不十分である。Dojo Toolkit、Google Web ToolkitなどのライブラリでJSONPがサポートされている。

サンプル

[http://togawa.qee.jp/json02.html]

<head>
<meta charset="UTF-8">
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.7/jquery.min.js"></script> 
<script type="text/javascript"> 
function execute() { 
  $.ajax({
    type: 'GET',
      url: 'http://www21051ue.sakura.ne.jp/api/json/get',
      dataType: 'jsonp',
      success: function (data) {
        $("#container").html("<pre>" + JSON.stringify(data,null,"  ") + "</pre>");
      }
  });
} 
</script>
</head> 
<body> 
<H1>JSON</H1>
<button onclick="execute()">サンプル実行</button> 
<hr><div id="jsondata">content</div>
<hr><div id="container">JSON DATA</div>
</body> 
</html> 

package Tool::mmt::Controller::Json;
use Mojo::Base 'Tool::mmt::Controller::Mmt';

my $perl_object =  {head => 'Json Test Data',array=>[1,2,3,4],lang=>['perl','ruby','php'],
           日本語=>['漢字','ひらがな','カタカナ']};

sub json_post{
    my $s = shift;
    use Mojo::UserAgent;
    my $ua = Mojo::UserAgent->new;
    my $data = $ua->post('http://www21051ue.sakura.ne.jp:8888/index.cgi' =>
                               {Accept=> '*/*'} => json => $perl_object);
    if (my $res = $data->success){
        $s->render(data => $res->body ,format=>'html');
    }else{
        my ($err, $code) = $data->error;
        $s->render(data => $code ? "$code response: $err\n" : "Connection error: $err\n",
            format => 'text');
    }
}

sub json_test01{
    my $s = shift;
    $s->json_or_jsonp( $s->render(json => $perl_object, partial => 1)
        );
}

sub json{
    my $s = shift;
    $s->render(json => $s->req->json);
}
sub json_or_jsonp{
    my $s = shift;
    my $json = shift;
    my $callback = $s->param('callback');
    if($callback ne ""){
        $s->render(data => "$callback($json)",format => 'js');
    } else {
        $s->render(data => $json, format=>'json');
    }
}
sub get{
    my $s = shift;
    my $sql = "select name,chat from test.chatdata order by rand() limit 10";
    my $dbh = $s->app->model->webdb->dbh;
    my $data = $dbh->selectall_arrayref($sql,+{Slice => +{}});
    $s->json_or_jsonp( $s->render(json => $data, partial => 1));
}

1;

mojolicious JSON POST

mojoliciousでJSONをPOSTで送信と受信

デモ画面を作ってみた。

[toolmmt/templates/json/json.html.ep]

% layout 'default';
% title 'mojplicious JSON DEMO';
<h2>mojolicious JSON Demo</h2>
<hr><h4>JSON</h4>
<ul>
  <li><a href="http://www21051ue.sakura.ne.jp:3003/api/json/json_test01">json_test001</a></li>
</ul>
<h4>JSON POST</h4>
<script type="text/javascript">
    $(function(){
        $("#response").html("Response Values");
        $("#button").click( function(){
            var url = $("#url_post").val();
                var JSONdata = {
                    value1: $("#value1").val(),
                    value2: $("#value2").val()      
                };
            // alert(JSON.stringify(JSONdata));
            $.ajax({
                type : 'post',
                url : url,
                data : JSON.stringify(JSONdata),
                contentType: 'application/JSON',
                dataType : 'JSON',
                scriptCharset: 'utf-8',
                success : function(data) {
                    // Success
                    // alert("success");
                    // alert(JSON.stringify(data));
                    $("#response").html(JSON.stringify(data,null,"  ")); //JSONを整形
                },
                error : function(data) {
                    // Error
                    alert("error");
                    alert(JSON.stringify(data));
                    $("#response").html(JSON.stringify(data));
                }
            });
        })
    })
</script>

    <h1>HTMLファイルからPOSTでJSONデータを送信する</h1>
    <p>URL: <input type="text" id="url_post" name="url" size="100" value="<%== url_for %>"></p>
    <p>value1: <input type="text" id="value1" size="30" value="値1"></p>
    <p>value2: <input type="text" id="value2" size="30" value="値2"></p>
    <p><button id="button" type="button">submit</button></p>
    <textarea id="response" cols=120 rows=10 disabled></textarea>

[toolmmt/lib/Tool/mmt/Controller/Json.pm]

package Tool::mmt::Controller::Json;
use Mojo::Base 'Tool::mmt::Controller::Mmt';

my $perl_object =  {head => 'Json Test Data',array=>[1,2,3,4],lang=>['perl','ruby','php'],
           日本語=>['漢字','ひらがな','カタカナ']};

sub json_post{
    my $s = shift;
    use Mojo::UserAgent;
    my $ua = Mojo::UserAgent->new;
    my $data = $ua->post('http://www21051ue.sakura.ne.jp:8888/index.cgi' =>
                               {Accept=> '*/*'} => json => $perl_object);
    if (my $res = $data->success){
        $s->render(data => $res->body ,format=>'html');
    }else{
        my ($err, $code) = $data->error;
        $s->render(data => $code ? "$code response: $err\n" : "Connection error: $err\n",
            format => 'text');
    }
}

sub json_test01{
    my $s = shift;
    $s->json_or_jsonp( $s->render(json => $perl_object, partial => 1)
        );
}

sub json{
    my $s = shift;
    $s->render(json => $s->req->json);
}
sub json_or_jsonp{
    my $s = shift;
    my $json = shift;
    my $callback = $s->param('callback');
    if($callback ne ""){
        $s->render(data => "$callback($json)",format => 'js');
    } else {
        $s->render(data => $json, format=>'json');
    }
}

1;

perl6も使って見よう

JSONシリアライズとデシリアライズ

$ perl6 -MJSON::Tiny -e 'say from-json(q{ { "a": 42 } }).perl; say to-json { a => [1, 2, <b>] };'
{:a(42)}
{ "a" : [ 1, 2, "b" ] }
$

JSON:いつの間にか簡単になっていた

mojoliciousとjQueryを使えば何も考えずにJSONデータのやりとりが出来てします。

・サーバーからJSONを送信

sub flick{
  my $self = shift;
  my $json = $self->app->album->flick($self);
  $self->render(json => $json);
}

・サーバーよりJSONを受信

                           $.ajax({
                               url: '/flick',
                               type: 'GET',
                               dataType: 'json',
                               data: {
                                   _action: 'flick',
                                   _name: document.img.src,
                                   _count: '1',
                               },
                               success: function( data,textStatus,jqXHR){
                                   document.img.src=data.name;
                                   $("#bigimg").attr("href",data.name);
                                   $("<img>").attr("src",data.preload);
                               }
                           })

photo_albumより抜粋)

・実際にJSONを受信してみる

$ wget -S http://www21051ue.sakura.ne.jp:3000/flick
--2015-03-03 22:51:47--  http://www21051ue.sakura.ne.jp:3000/flick
www21051ue.sakura.ne.jp (www21051ue.sakura.ne.jp) をDNSに問いあわせています...  49.212.186.65
www21051ue.sakura.ne.jp (www21051ue.sakura.ne.jp)|49.212.186.65|:3000 に接続しています... 接続しました。
HTTP による接続要求を送信しました、応答を待っています... 
 HTTP/1.1 200 OK
 Connection: keep-alive
 Content-Type: application/json
 Date: Tue, 03 Mar 2015 13:51:47 GMT
 Content-Length: 68
 Server: Mojolicious (Perl)
長さ: 68 [application/json]
`flick.1' に保存中

100%[======================================>] 68          --.-K/s   時間 0s    

2015-03-03 22:51:47 (4.18 MB/s) - `flick.1' へ保存完了 [68/68]

$ cat flick.1
{"preload":"img\/1379065566324.jpg","name":"img\/1379065566324.jpg"}
$

JSONメモ

JSON日の金曜日 に行ってきた

ギークハウス新潟JSONの勉強会に参加してきた。いつもながら楽しかったヨ!!! ギー潟のリビングで開催された勉強会、開始の挨拶の後に5人の方が発表し、その後飛び入りで3人が発表しました。その3人目に私も軽いLT(このページ)をやらせてもらいました。私は、PCを持っていないので@NkznさんのPCをお借りしました。(ありがとう!)ギー潟リビングではMAC比率高かった。ほぼMACでMAC9台?、Win1台、手ぶら1人(私)って感じでした。発表内容は早くもsasa+1さんが発表内容をまとめてくれています。---

私の発表内容

2012年7月13日の金曜日にジェーソン(JSON)の勉強会が新潟で行われるのでJSONについて調べてみた。AJAXでサーバーからデータを受けとるのにXMLよりJSONの方がそのままevalで処理出きるので便利(危険)だ。perlでは作成も簡単で、人間にも見やすいので人間にも機械にもやさしいデータフォーマットだと思う。調べるだけでは何なので実際にWebでサンプルページを作ってみた。

JSONとは

JSON の紹介より(http://www.json.org/)

JSON (JavaScript Object Notation)は、軽量のデータ交換フォーマットです。人間にとって読み書きが容易で、マシンにとっても簡単にパースや生成を行なえる形式です。 JavaScriptプログラミング言語 (ECMA-262標準第3版 1999年12月)の一部をベースに作られています。 JSONは完全に言語から独立したテキスト形式ですが、C、C++、C#、Java、JavaScript、Perl、Python、その他多くのCファミリーの言語を使用するプログラマにとっては、馴染み深い規約が使われています。これらの性質が、 JSONを理想的なデータ交換言語にしています。

JSONは2つの構造を基にしています。

  • 名前/値のペアの集まり。様々な言語で、これはオブジェクト、レコード、構造体、ディクショナリ、ハッシュテーブル、キーのあるリスト、連想配列として実現されています。
  • 値の順序付きリスト。ほとんどの言語で、これは配列として実現されています。

データフォーマット

  • 連想配列
{"name":"John Smith","arg":50}
  • 配列
["perl","ruby","JavaScript","JSON"]
  • 連想配列と配列の組み合わせ
{"languages":["Perl","Ruby","Python"],
  "DATA":["JSON","XML","CSV"]
}

サンプルを作ってみる

サーバーのデータを参照しJSON形式でレコードを返しJavaScriptでTABLE形式にフォーマットし表示するだけのサンプルを作ってみた。

・フロー
+-----------------+ request +----------------+      +-----------+
|                 |-------->|                |      |           |
|  json01.cgi     |         |    get.cgi     |<---->|  MySQL    |
|                 |<--------|                |      |           |
+-----------------+  JSON   +----------------+      +-----------+
  • json01.cgi
#!/usr/local/bin/perl

use mmt;
my $mmt = mmt->new();
print make_html();
undef $mmt;

sub make_html{
	$sql=$mmt->{dbh}->urlencode("select * from 機能一覧 order by rand() limit 10");
	my $res = <<END_HTML;
@{[$mmt->{in}->header(-type=>"text/html; charset=euc-jp")]}
<head>
<script type="text/javascript" src="js/prototype.js"></script> 
<script type="text/javascript"> 
function execute() { 
    var a = new Ajax.Request( 
        "get.cgi", 
        { 
            "method": "get", 
            "parameters": "act=JSON&sql=$sql", 
            onComplete: function(request) { 
                \$('container').innerHTML = request.responseText; 
                var json = eval("("+request.responseText+")");
                var text = "<table border=1>"; 
                for(var i=0;i< json.item.length;i++){
             text += "<tr>";
                    for (var  keyString in json.item[i]) {
                        text += "<td>" + json.item[i][keyString] + "</td>";
                    }
                    text += "</tr>";
                }
                text +=  "</table>"; 
                \$('jsondata').innerHTML = text; 
            }, 
            onFailure: function(request) { 
                alert('読み込みに失敗しました'); 
            } 
        } 
    ); 
} 
</script>
</head> 
<body> 
<H1>JSON</H1>
<button onclick="execute()">サンプル実行</button> 
<hr><div id="container">JSON DATA</div>
<hr><div id="jsondata">content</div>
</body> 
</html> 
END_HTML

return $res;
}
  • get.cgi
#!/usr/local/bin/perl

=pod

=head1 NAME

get.cgi - データ照会

=head1 概要


=cut

use strict;
use webdb;
use CGI qw/:standard :html3/;

use vars qw($in $dbh $sql);
$in = new CGI;
$dbh = new webdb;
$sql = $in->param('sql') || "select 取引先名 from 取引先マスタ where 取引先コード =  ?";
print header(-type=>"text/html; charset=euc-jp");
exit if($sql !~ /^\s*(select|show)/i);
if($in->param('act') eq 'JSON'){
	JSON();
}else{
	my $sth = $dbh->{'dbh'}->prepare($sql);
	$sth->execute($in->param('code'));
	while(my $ref = $sth->fetchrow_arrayref()){
		print $ref->[0];
	}
}
sub JSON{
	my $sth = $dbh->{dbh}->prepare($sql);
	$sth->execute($in->param('code'));
	print '{"item":[' . "\n";
	my @a = ();
	my @b = ();
	while(my $ref = $sth->fetchrow_hashref()){
		@b = ();
		for(@{$sth->{NAME}}){
			$ref->{$_} =~ s/\x00//g;
			push @b,qq{"$_":"$ref->{$_}"};
		}
		push @a,"{\n" . join(",\n",@b) . "}\n";
	}
	print join(',',@a);
	print "]\n}\n";
}

メモ

use mmt;
my $mmt = mmt->new();
$sql=$mmt->{dbh}->urlencode("select 分類No,機能名 from 機能一覧 limit 10");
print qq{<a href=get.cgi?act=JSON&sql=$sql >JSON</a>}
{"item":
 [
   { "分類No":"1", "機能名":"ホストデータ振り分け処理"} ,
   { "分類No":"1", "機能名":"ホストデータの取得(現物集中システムサーバ)"} ,
   { "分類No":"1", "機能名":"ホストデータのジェネレーション処理"} ,
   { "分類No":"1", "機能名":"ホストデータ受信更新処理"} ,
   { "分類No":"1", "機能名":"不動産担保データ受信更新処理"} ,
   { "分類No":"2", "機能名":"自店保管管理用データ抽出処理(日次)"} ,
   { "分類No":"2", "機能名":"不備返却管理用データ抽出処理(月次)"} ,
   { "分類No":"2", "機能名":"遅延管理用データ抽出処理(日次)"} ,
   { "分類No":"2", "機能名":"事務量集計データ抽出処理(日次)"} ,
   { "分類No":"2", "機能名":"事務量集計データ抽出処理(月次)"} 
 ] 
}