Get Utility: Getting a Script Node with JSON Data
先までのConnection Managerをつかったサンプルでは、same-origin policyにより、他のサイトのサービス(WebService)を利用するためにサーバーサイドのプログラム(Proxy)を使用した。
これは、他のサイトのコンテンツをJavaScriptに直接読み込んでeval()してしまうことで、クロスサイトスクリプティング(XSS)の脅威にさらされてしまうためである。
ただ、自分自身の用意したサーバーコンテンツや、一般に信頼のおけるサイトで提供されるサービスについては、JavaScriptで直接取りにいきたくなるのが人情。
Get Utilityは、そのような場合に用いるツール。Javascriptから、直接、他のサイトのサービスを取得しに行ける。
以下に、YUIで挙げられているサンプルを若干modifyしつつ解析していくが、その前に、このサンプルが「Module Pattern」と呼ばれるパターンで書かれていることに注目したい。
詳しくは、丁寧に書かれているYUI BLogの記事を参照だが、なるほどと納得。
何より、スコープがはっきりするのがよい。
YUIに挙げられているサンプルは、サイトを指定して、そのInbound Link(バックリンク)を検索するもの。
バックリンクというと、Googleのオーガニック検索で、Page Rankと言われて利用されているサイトの評価指標のひとつ。
これを検索するWebサービスが、YAHOO! Developer Networkで「Site Explorer Inbound Links API」として提供されている(Site Explorer Inbound Links APIの仕様はこちらを参照)。
以下のサンプリングでは、ラベルの日本語化とスタイルの変更、Exampleではscriptがbodyの後に来ているのを、前に変更した。
サンプルの初期画面は以下。
URLを入れて、「Click here to get JSON data」ボタンを押すと、全体の取得件数、トップから20件のバックリンクのタイトルとリンクが表示される。
以下に、Javascriptを含むhtmlのソースを示す。
(注)ソース中でWebServiceを呼ぶためには、Application IDというのが必要になる。
YAHOO! Developer Networkで簡単取得できるので、それに置き換えること。
また、「Site Explorer Inbound Links API」のページのRATE LIMITで書かれているように、1つのIPアドレスあたり5000クエリー/日で非商用目的であることが定められている。
<HTML> <HEAD> <META http-equiv="Content-Type" content="text/html; charset=UTF-8"> <META http-equiv="Content-Style-Type" content="text/css"> <TITLE>Ajax_Sampling</TITLE> <link rel="stylesheet" type="text/css" href="scripts/yui/button/assets/skins/sam/button.css" /> <style type="text/css"> body { margin:0; padding:0; } #container { width: 550px; hight: auto; padding: 3px; background-color: #6D739A; color: white; border: 1px solid green; } #container ol { /*bringing lists on to the page with breathing room */ margin-left:2em !important; } #container ol li { /*giving OL's LIs generated numbers*/ list-style: decimal outside !important; } #container h3 { margin-top: 1em; } </style> <!-- 読み込むjs --> <script type="text/javascript" src="scripts/yui/utilities/utilities.js" > </script> <script type="text/javascript" src="scripts/yui/button/button-min.js" > </script> <script type="text/javascript"> // このサンプルは、module patternで実装されています。 // 内容はYUI Blogへ。http://yuiblog.com/blog/2007/06/12/module-pattern // プログラムの注釈も、それに沿っています。 // // << module pattern >> // Name Spaceの定義; // YAHOOオブジェクトのメンバーとして、空のオブジェクト(YAHOO.example.SiteExplorer) // を作る。 YAHOO.namespace("example.SiteExplorer"); // 上で作ったName Space一杯に関数を定義する。 // オブジェクトの階層は、YAHOO=>YAHOO.example.SiteExplorerとなる。 // これにより、YAHOO.example.SiteExplorerオブジェクトは、ひとつの関数で占められる。 // この内部では、プロパティーや関数は、thisで指定できる。 YAHOO.example.SiteExplorer = function() { // // このオブジェクト(YAHOO.example.SiteExplorer)スコープで // 共通に使う変数 // var Event = YAHOO.util.Event, Dom = YAHOO.util.Dom, Button = YAHOO.widget.Button, Get = YAHOO.util.Get, // Example通りのこの場所はうまくない //(ExampleではScriptが末尾にあるのでよい)。 // elResults = Dom.get("results"), elResults, tIds = {}, loading = false, current = null; // // << module pattern >> // module patternに沿ったprivate method.(その1) // // Get Utility のsuccessハンドラー。 var onSiteExplorerSuccess = function(o) { loading = false; // この例では、WebServiceのAPIにより、return内のcallbackメソッドを // 呼ぶようになっているので、そこに実際の処理を記入している。 // (Get utilityのコールバックは、responseさえあればsuccessとなるが、 // Webserviceのcallbackは、それ以上に正確であるため) // // ただし、Get utilityの機能の機能により、success時のコールバックとして // このメソッドもコールされる。 // if (o.tId in tIds) { // WebserviceのCallbackと、Get Utilityのcallbackが一致する場合。 // 普通は、こっちにはいる。 } else { // WebserviceのCallbackと、Get Utilityのcallbackが一致しない場合。 alert("Get utility はonSuccessで発火したが、"+ "webserviceのcallbackは発火しなかった。 " + "ここで、トランザクションをリトライするか、"+ "ユーザーに失敗を通知するかをすることができる"); } } // // << module pattern >> // module patternに沿ったprivate method.(その2) // // Get Utility のfailureハンドラー。 var onSiteExplorerFailure = function(o) { alert("データの取得に失敗しました。"); } // // << module pattern >> // module patternに沿ったprivate method.(その3) // // Yahoo! Site Explorer WebServiceから指定したURLへのバックリンク // を取得する。 // http://developer.yahoo.com/search/siteexplorer/V1/inlinkData.html var getSiteExplorerData = function() { if (loading) { return; } loading = true; //Load the transitional state of the results section: elResults.innerHTML = "<h3>次のキーワードのLinkを検索しています。 " + Dom.get("searchString").value + ":</h3>" + "<img src='http://l.yimg.com/us.yimg.com/i/nt/ic/ut/bsc/busybar_1.gif' " + "alt='お待ちください...'>"; // the Yahoo Site Explorer APIのURL(とQueryString)を準備する。 // (注)appidは自分で取得したもの。outputはjson形式で20件取得する。 // call backにYAHOO.example.SiteExplorer.callbackを指定している。 var sURL = "http://search.yahooapis.com/SiteExplorerService/V1/inlinkData?" + "appid="your_id" + "&results=20&output=json&omit_inlinks=domain" + "&callback=YAHOO.example.SiteExplorer.callback" + "&query=" + encodeURIComponent(Dom.get("searchString").value); // Get Utilityを使い、Web Serviceからデータを取得するcallbak定義。 // この例では、WebserviceのAPIで指定している。 // なので、以下は、スタブ(中身はないもの定義する)。 // ただし、Get Utilityの機能で、下記に定義したcallbackには飛んでくる。 var transactionObj = Get.script(sURL, { onSuccess: onSiteExplorerSuccess, onFailure: onSiteExplorerFailure, scope : this } ); // ScriptメソッドはトランザクションIDを含むオブジェクトを戻す。 // そのトランザクションIDを保管する。 current = transactionObj.tId; } // // << module pattern >> // module patternに従い、return 配下に無名関数を定義する。 // // この無名関数は、ただちに実行されて、この場合、init,callbackというプロパティーが // YAHOO.example.SiteExplorerオブジェクトに追加される。 // return { // DOMReadyで最初に呼ばれる初期化関数。 init: function() { // (注意) // DOMReady後でなければできない、メンバー変数の定義。 elResults = Dom.get("results"); //id=siteExplorerのsubmitイベントにハンドラーを仕掛ける。 Event.on("siteExplorer", "submit", function(e) { // submit formのデフォルトの動きを抑止する。 Event.preventDefault(e); // WebServiceの呼び出しを実行する。 getSiteExplorerData(); }, this, true ); // input type=submit にbuttonをインスタンス化する。 var oButton = new Button("getSiteExplorerData"); }, // WebServiseの呼び出し時に定義するcallback関数。 // resultsにJSONデータが戻ってくる。 callback: function(results) { // currentは、Webservice実行時のトランザクションID。 // trueを入れて、「うまくいった」とマークしておく。 tIds[current] = true; // Webserviceの戻り値から情報を取得する。 var aResults = results.ResultSet.Result; var totalLinks = results.ResultSet.totalResultsAvailable; var returnedLinkCount = results.ResultSet.totalResultsReturned; if(aResults) { // 結果がある場合;処理と表示をする。 var html = "<h4>" + totalLinks + " のリンクが取得できた; 以下に最初の " + returnedLinkCount + "リンクを表示</h4><ol>"; for (var i=0; i < aResults.length; i++) { html += "<li><strong>" + aResults[i].Title + ":</strong> <a href='" + aResults[i].Url + "'>" + aResults[i].Url + "</a></li>"; } html += "</ol>"; } else { // データが取得できなかった場合 var html = "<h3>リンクが見つかりませんでした。</h3>"; } //結果の編集 elResults.innerHTML = html; } // return の終わり }; }(); // 以下は、グローバル(ページスコープ)に定義する。 // // DOMは完全にloadされたら、サンプルを初期化する。 YAHOO.util.Event.onDOMReady( //DomReadyイベントで発火するハンドラ YAHOO.example.SiteExplorer.init, //ハンドラに渡すオブジェクト(関数) YAHOO.example.SiteExplorer, //ハンドラは、上記のオブジェクトのスコープをもつ。 true ); </script> </HEAD> <!-- class=" yui-skin-sam"の指定はbuttunのskin適用に必要 --> <BODY class=" yui-skin-sam"> <div id="container"> <p> 入力したサイトのバックリンクを検索します。 </p> <!--Use a real form that works without JavaScript:--> <form id="siteExplorer" method="GET" action="http://siteexplorer.search.yahoo.com/advsearch"> <label for="searchString">Site URL:</label> <input id="searchString" type="text" name="p" value="http://developer.yahoo.com/yui" size="40"> <input type="hidden" name="bwm" value="i"> <input type="hidden" name="bwms" value="p"> <input id="getSiteExplorerData" type="submit" value="Click here to get JSON data."> </form> <div id="results"> </div> </div> </BODY> </HTML>