無精・短気・傲慢

perlの事 いろいろ

JSONP

JSONP

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

ただ、外部サービスと通信する際に、AjaxJSONをやり取りするには「クロスドメイン」という壁があります。つまり、他ドメインに属するファイルは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;