マッシュアップ

目標

JavaScriptの醍醐味の1つは、他人が開発したモジュールを自分のプログラムに組み込んで、大規模なソフトウェアや見栄えのするソフトウェアを開発することである。
ここでは、他人が提供しているサービスを自分のプログラムに組み込んで使うことを学ぶ。

  • マッシュアップの概念を理解する。
  • 他人が用意したサービスを利用する仕組みについて理解する。
  • マッシュアップができるようになる。

Webサービス

この授業で学習する言語としてJavaScriptを選んだ理由の1つには、世の中の人々がWebを介して提供しているサービスを活用できるという点がある。
ライブラリもこの一種であるが、Webサービスはサーバ側で処理をしたり、サーバ側に用意されているデータを使ったりすると言う意味で、ライブラリより高度である。
今回は、Webサービスを使ったマッシュアップについて解説する。
マッシュアップとは、複数の異なる提供元の技術やコンテンツを混ぜあわせて新しいサービスを形作ることである。
すべてを自分で作ると1年以上かかるようなシステムを、短期間で作れてしまうなど、注目されている技術である。
マッシュアップでは、素材となるサービスのプログラム詳細を知る必要がなく、呼び出す手順と、送り返されるデータの形式だけに注目すれば良いため、
自分が作れる以上に複雑なサービスや自分が持っていないデータを使ったサービスを実現できる。

Webサービスの概念

これまで学習してきたように、主としてブラウザ上で動作をする言語である。
このため、JavaScriptのプログラムをブラウザに読み込む事さえ出来れば、プログラムを実行することができる。
もう少し言うと、別の人が作った関数でもブラウザ上に読み込む事ができれば、それを利用することができる。
マッシュアップの場合、読み込んだプログラムはWebサーバ上で提供されるインターフェイスを介して、様々なサービスを提供します。

WebServiceの仕組み

Webサービスを利用するにあたって

Webサービスを利用するにあたってはいくつかの注意が必要である。
ここではそれらの注意点についてみていく。

プログラミング

Webサービスを使うということは、他人のサイトにアクセスすることにほかならない。
このため、無限ループの中でWebサービスを使っていたりすると、他人のサイトに高速に大量のアクセスをすることになる。
たとえプログラムを書いた本人に悪意はなくとも、このような行為はWebサービスを提供している側から見るとDoS攻撃(Denial of Service attack)と見えてしまう。
Webサービスを利用する場合は、Webサービスを提供している側に迷惑をかけないよう、細心の注意を払ってプログラムを作成するように心がけてほしい。

セキュリティ

自分のウェブページに他者のWebサービスを埋め込む場合は、その品質を十分に吟味する必要がある。
悪意を持った他人のWebサービスを埋め込んでしまうと、悪事に加担したことになり、共犯者になってしまう可能性がある。

例えば、見ず知らずの他人から教えてもらったWebサービスを安易に利用してしまうと、表向きは便利な機能でも、
そのWebサービスが個人情報を流出するなど悪意を持った動作をする可能性もある。

このようなトラブルを回避するには、多くの人が長期に渡って利用しているなどの安全性が十分確認されているようなWebサービスを選ぶと良い。

使用条件

Webサービスを利用する場合、「使用条件」を確認し、それを守る必要がある。
たとえば、提供元を明示したり、特定のアイコンを表示することを求められている場合も多くある。
場合によってはユーザ登録が必要なこともある。
近年は多くのサービスがユーザ登録を必要としている。

例えば、友人のホームページのソースの一部をコピーして利用する場合などに、特に注意する必要がある。
サービス呼び出し部分だけでなく、クレジット部分もコピーする必要があるかも知れないし、URLが変ることになるので改めてユーザ登録を行う必要がある場合もある。
対象Webサービス提供者のホームページなどで、使用条件を確認することが重要である。

XMLとJSON

多くのWebサービスでは、XML形式やJSON形式でデータを取得することができる。
ここでは、XMLやJSONについて説明する。

XML

XMLとはExtensible Markup Languageのことで、HTMLのようなMarkup Languageの1つである。
HTML同様に、World Wide Web Consortium (W3C) により策定されている。
XMLは、その名前のとおり、ユーザがタグ群を定義することによって要素を拡張することができ、このため、メタ言語と呼ばれることもある。
基本的には「<タグ名>」で要素の始まりを示し、「</タグ名>」で要素の終わりを示す構造をしている。
現在では、XHTMLKML等、多くのフォーマットがXMLベースで開発されている。

JSON

XMLは柔軟にできており、多くのデータがこの形式で規定されるようになった。
しかし一方で、ファイルサイズが大きくなる、可読性が悪いなどの批判も多くある。
そこで、JavaScriptと親和性のよいJSON (JavaScript Object Notation)というデータ記述方法が登場した。

JSONは、JavaScriptにおけるオブジェクトリテラルをベースとしたデータ記述方法でRFC4627で規定されている。
基本的には、JavaScriptのオブジェクトリテラルを踏襲しているが、数値は10進法表記でなければならない等の制限がある。
また、オブジェクトのキーも文字列で「”」で囲まなければならないことになっている。
以下にJSONで表現されたデータの例を示す。
この例では見やすいように改行を入れてあるが、改行は必ずしも必要ではない。

{
    "name": {
        "familyName": "慶應",
        "firstName": "太郎"
    },
    "age": 19,
    "gender": "male"
}

Webサービスの利用: Google Maps

ここでは、Webサービスの利用の例として、Google Mapsの使い方を学ぶ。

Webサービス

世の中では複雑なWebサービスが提供されている。
これらの多くは、その裏で様々なコンテンツを提供している。
これは、Webサービスが用意している関数(Application Program Interface、APIなどと呼ばれます)を自分が作ったプログラムから呼び出すと、
その関数が必要なサーバと通信を行い、コンテンツを提供する仕組みを取り入れることによって実現している。
下記にその概念を示す。

Webサービスのアーキテクチャ

では実際にコンテンツを提供しているようなWebサービスを利用してみよう。

はじめてのGoogle Maps

下記は良くみかけるGoogle社が提供するGoogle Mapsを貼付けたものである。
このプログラムは実は簡単に実現する事ができる。


上記を実現するプログラムは下記のようになっている。
これまでのプログラムに比べれば幾らかややこしそうに見えるかもしれないが、
1つ1つみていけば、そんなに難しくはない。

HTML部

<div id="map" style="width:300px;height:300px"></div>

<script async defer src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY&callback=initMap" type="text/javascript"></script>
<script type="text/javascript" src="mymap.js"></script>

JavaScript部

function initMap() {
  // 地図の中心を決める
  var latlng = new google.maps.LatLng(35.388276, 139.427348);

  // 地図を表示するためのオプションを設定する
  var opt = {
    zoom: 15,
    center: latlng,
    mapTypeId: google.maps.MapTypeId.ROADMAP
  };

  // 地図を表示する。
  var map = new google.maps.Map(document.getElementById('map'), opt);
}

まず、HTML部の1行目はdivタグを使って地図を表示するエリアを確保している。
ここでは、idとstyleの2つの属性が指定されており、id属性によってIDを「map」に、style属性によってエリアのサイズを幅300px(ピクセル)、高さ300px(ピクセル)に設定している。

3行目はGoogle Maps用のAPIの読み込みである。
Google MapsのAPIはhttps://maps.googleapis.com/maps/api/jsにある。
そこで、Google Maps APIを利用するためにこのプログラムを読み込む必要がある。
この行はHTMLのヘッダにあっても構わない。
むしろヘッダに有る方が普通かもしれない。
ここでは、本来API_KEYを設定しなければならない。
API_KEYはGoogleの「キーの取得、認証」ページから取得できる。
現在のところは、以前のAPIがAPI_KEYを必須としていなかったため、後方互換性に鑑みてAPI_KEYを設定しなくとも動作するようである。
API_KEYを省略するためには、
「<script async defer src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY&callback=initMap" type="text/javascript"></script>」の部分から
API_KEYに関する部分をまるごと削除して、
「<script async defer src="https://maps.googleapis.com/maps/api/js?callback=initMap" type="text/javascript"></script>」のようにする。

また、Google Maps API v3には幾つかのオプションが存在する。
詳しくはGoogle Maps JavaScript API V3 Referenceのページで調べてみてほしい。

HTML部の4行目は、自分が書くべきプログラムファイルであるmymap.jsを読み込んでいる部分である。
Google Maps APIを読み込む際に initMap() をコールバック関数として指定しているため、このプログラムはinitMap()関数を含んでいる必要がある。

では、JavaScriptのプログラムを見てみよう。
まず、3行目では、表示する地図の中心点を決めている。
google.maps.LatLng()で新しいオブジェクトを作っているが、あまり細かいことは気にせず、ここでは引数に緯度と経度を指定して、その結果をlatlngという変数に格納していると思ってほしい。
6行目から10行目は実際に地図を表示する時のパラメータを設定している。
zoomが地図の縮尺を示すもので15に指定されている。
同様に、地図の中心を示すcenterが先ほど生成した変数であるlatlng、地図のタイプを示すmapTypeIdが道路地図(google.maps.MapTypeId.ROADMAP)に設定されている。
そして、それら全体がoptという変数に格納されている。
optがオブジェクトになっていることに気をつけてほしい。
そして13行目がいよいよ地図を表示している部分である。
document.getElementById(‘map’)を使ってWebページにおいて地図を表示すべき場所を特定し、optに設定されたパラメータを使って地図を表示している。

Google Maps APIのより詳しい使い方については、上にもあるが、
Google Maps JavaScript API V3 Referenceのページ
チュートリアルを参照してほしい。

Google Mapsへのイベントリスナ(コールバック関数)の設定

地図を表示すると、その地図を操作した時に何らかの動作をさせたくなる。
これを実現するために、Google Mapsにはイベントリスナを設定する機能がある。
イベントリスナとは、イベントが発生したときに呼び出される関数、コールバック関数のことである。

次のプログラムを見て欲しい。
これは、上の地図を表示させる関数にイベントリスナを追加したものである。
21行目で、clickイベントに対して、click_callback()関数が呼び出されるようにイベントリスナを設定している。
また、16行目から始まるclick_callback()関数がイベントリスナの本体である。
click_callback()は、1つの仮引数を持ち、この変数にイベント発生時の状態が設定されている。
このプログラムでは、17行目で、イベントが発生した場所、つまりクリックされた場所の緯度経度をalert()を使って表示している。
引数にどのような値が返ってくるかは、Google Maps JavaScript API V3 Referenceのページを参照してほしい。
このページの、Eventsの表の”click”のところをみると「Arguments: MouseEvent|IconMouseEvent」と記載されている。
これは、MouseEventオブジェクトかIconMouseEventオブジェクトが設定されて呼び出されるということを意味している。
IconMouseEventオブジェクトは、MouseEventオブジェクトにplaceIdという場所のIDが追加されたものである。
実際、clickイベントから呼び出されるイベントリスナに渡される引数は、通常の地図上をクリックされた時はIconMouseEvent、
ランドマークをクリックされた時はIconMouseEventとなる。

function initMap() {
  // 地図の中心を決める
  var latlng = new google.maps.LatLng(35.388276, 139.427348);

  // 地図を表示するためのオプションを設定する
  var opt = {
    zoom: 15,
    center: latlng,
    mapTypeId: google.maps.MapTypeId.ROADMAP
  };

  // 地図を表示する。
  var map = new google.maps.Map(document.getElementById('map'), opt);

  // イベントリスナ(コールバック関数)
  function click_callback(e) {
    alert(e.latLng.toString());
  }

  // イベントリスナの設定
  map.addListener('click', click_callback);
}

マーカー

google.maps.Markerクラスを使うことによって、マーカーを設定することができる。
次のプログラムを見て欲しい。
このプログラムでは、16行目でマーカーを作成している。
17行目では、マーカーを置く場所を、18行目ではマーカーを置く地図を指定している。
positionに適当な値を設定することによって、好きな所にマーカーを設定することができる。

function initMap() {
  // 地図の中心を決める
  var latlng = new google.maps.LatLng(35.388276, 139.427348);

  // 地図を表示するためのオプションを設定する
  var opt = {
    zoom: 15,
    center: latlng,
    mapTypeId: google.maps.MapTypeId.ROADMAP
  };

  // 地図を表示する。
  var map = new google.maps.Map(document.getElementById('map'), opt);

  // マーカーの追加
  var marker = new google.maps.Marker({
                     position: latlng,
                     map: map
		});
}

マーカーにもいろいろなオプションがあるのので、MarkerOptions object specificationを参照してほしい。

JSONPと同一生成元ポリシー

同一生成元ポリシー

JavaScriptには同一生成元ポリシーと呼ばれる制限があり、JavaScriptプログラムをダウンロードしてきたサイト以外のサイトとは通信できないようになっている。
これは、自分が表示したページに置かれていたプログラムが他のサイトに通信を行い、情報が漏洩したりするのを防ぐために存在している制約である。
同じコンテンツでもhttpでアクセスした際とhttpsでアクセスした際で違うドメインと判断されたりするので、注意が必要である。

しかし、プログラムをダウンロードしてきたサイト以外のサイトとは通信ができないという制約は非常に厳しく、JavaScriptの利便性を下げることにつながる。
諸刃の剣である。

同一生成元ポリシーの回避: JSONP

同一生成元ポリシーは、JavaScriptプログラムからはプログラムを持ってきたサイトのみにしかアクセスできないというものである。
逆に言えば、アクセスしたいサイト、つまりデータがあるサイトにアクセスのためのJavaScriptプログラムがあればアクセスに何も問題はないということになる。
このことを利用して、JSONを処理する関数を予め自分で用意しておき、データを持っているサイト側から用意した関数を呼び出してもらうことで同一生成元ポリシーを回避することができる。

例えば次のようなプログラムがあったとする。
ここで9行目以降が別のサイトにあって、そのサイトにあるデータを引数にしてメソッドを呼び出してくれると考えてほしい。
この例では、2行目が実行されると、10行目の関数process()が呼び出される。
すると、関数process()からは、引数にデータが格納された状態で自分で定義した関数であるfunc()が呼び出される。
こうすることによって、他のドメイン上におかれているデータを自分の定義した関数で受け取ることができる。

// 他のドメインにあるべき関数の呼び出し(読み込み)
process();

// 自分で定義した関数
function func(json) {
  alert(json.a + " + " + json.b + " = " + (json.a + json.b));
}

// 他のドメインにあるべき関数
function process() {
  func({ "a": 123, "b": 456 });
}

実際には次のようにして用いる。
この例では、MediaWiki APIのQuery APIを用いて、
WikiPediaに登録されている「慶應」で始まる言葉をリストアップし、上位50位を表示している。
「<script src=”http://ja.wikipedia.org/w/api.php?action=query&list=allpages&apfrom=慶應& aplimit=50&format=json&callback=func”>」の部分で、
実際には上で述べた様なデータを引数とした関数が返ってくるので、ここでfunc()が呼び出される。
このように別のプログラムから呼び出される関数のことをコールバック関数と呼ぶ。
コールバック関数の渡し方は、使うWebサービスによって異なりますので、Webサービスのマニュアルを参照してほしい。

JavaScript部

function func(json) {
  var ele = document.getElementById("listOfKeio");

  var i;
  for (i=0; i < json.query.allpages.length; i++) {
    ele.innerHTML += (i + ": " + json.query.allpages[i].title + "<br />");
  }
}

HTML部

<div id="listOfKeio"></div>
<script src="myprog.js"></script>
<script src="http://ja.wikipedia.org/w/api.php?action=query&list=allpages&apfrom=慶應&aplimit=50&format=json&callback=func"></script>

しかし、この方法を使ってWebサービスを利用するためには大きな問題がある。
script要素でWebサービスのページ、つまりWikipediaのページにアクセスしたタイミングでしかcallback関数を呼び出すことができない点である。
これでは必要なタイミングにデータを取ってくることが出来ない。

そこで、JSONPを利用する場合は次のように、プログラムの中でscript要素を生成する。
JavaScript部の12行目ではscript要素を新たに生成し、変数oに代入している。
createElement()は、新たにHTML要素を作り出すためのメソッドである。
また、13行目ではsetAttribute()メソッドを使ってsrcアトリビュートを12行目で生成したscript要素に追加している。
その後、14行目において、生成したscript要素をdiv要素の子供として、つまりdiv要素の中に設定している。
このことによって、script要素が表示されたタイミング(実際には目には見えないがHTML文書の中には埋め込まれている)、
つまりボタンが押されたタイミングでコールバック関数func()が呼び出され、「慶應」で始まる言葉をリストアップしている。

HTML部

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

<button id="listKeio" onclick="getFromWikipedia()">List</button>
<div id="listOfKeio"></div>

JavaScript部

function func(json) {
  var ele = document.getElementById('listOfKeio');

  var i;
  for (i=0; i < json.query.allpages.length; i++) {
    ele.innerHTML += i + ": " + json.query.allpages[i].title + "<br />";
  }
}

function getFromWikipedia() {
  var e = document.getElementById('listOfKeio');
  var o = document.createElement("script");
      o.setAttribute("src", "http://ja.wikipedia.org/w/api.php?action=query&list=allpages&apfrom=慶應&aplimit=50&format=json&callback=func");
  e.appendChild(o);
}

練習問題11-

上の説明を参考に、Google Mapを表示するプログラムを作成しなさい。

練習問題11-

上の説明を参考に、Google Mapを表示し、地図上をクリックすると、その場所の緯度経度が表示されるプログラムを作成しなさい。

注意: https://maps.googleapis.com/maps/api/js を同じページで複数回呼び出すことはできない。
そのため、他の練習問題と同一ページに作成する場合は、コールバック関数を1つにまとめる必要があるので注意が必要である。

練習問題11-

上の説明を参考に、Google Mapを表示し、地図上をクリックすると、その場所を中心とした地図が表示されるようにしなさい。
地図の中心点を変える場合は、地図オブジェクトのsetCenter()メソッドを使う。
例えば、地図オブジェクトが変数mapに入っている場合は、map.setCenter(lonlat)のようにする。
ただしここで、lonlatは緯度経度が入ったオブジェクトである。

注意: https://maps.googleapis.com/maps/api/js を同じページで複数回呼び出すことはできない。
そのため、他の練習問題と同一ページに作成する場合は、コールバック関数を1つにまとめる必要があるので注意が必要である。

練習問題11-

上の説明を参考に、Google Mapを表示し、地図上をクリックすると、その場所にマーカが設置されるようなプログラムを作成しなさい。
また、その際に、マーカーに”1″, “2”, “3”とラベルが設定されるようにしなさい。
このさい、Markerオブジェクトを生成する際のlabelプロパティの値の指定は文字列でなければならないことに注意すること。

注意: https://maps.googleapis.com/maps/api/js を同じページで複数回呼び出すことはできない。
そのため、他の練習問題と同一ページに作成する場合は、コールバック関数を1つにまとめる必要があるので注意が必要である。

練習問題11-

上の説明を参考に、ボタンを押すと慶應に関連する言葉が50個表示されるプログラムを作成しなさい。