highlight.js

星期日, 6月 17, 2012

jQuery 與 jsonp

jsonp 簡單的來說, 就是使用 ajax 存取跨網域 json 格式資料的技術, 這主要是因為瀏覽器通常都會因為安全的理由, 限制 ajax 機制存取非來源網站的資料, 也就是所謂的 "origin policy"。換句話說, 只要資料不是和發出 ajax 請求的網頁位於同一個網域, 瀏覽器就不會讓 ajax 存取該資料。最簡單的例子就是如果你直接瀏覽本機檔案系統上的 HTML 檔, 從這個 HTML 檔發出 ajax 存取網路上任何網站提供的 json 資料, 就會因為這份 HTML 檔式是位於本機的檔案系統上, 和網路上的任何網站都不在同一個網域而無法存取。

以下我們以一個簡單的例子來說明, 這裡我們要存取的是台北市資料公開平台中的停水資訊, 你可以透過以下的 URL 取得 json 格式的停水資訊:

http://taipeicityopendata.cloudapp.net/v1/TaipeiOGDI/StopWaterInfo?format=json

如果我們使用以下的 HTML 網頁 (此程式修改自此網頁) 去存取停水資訊, 會發現瀏覽器根本不會送出請求:
<!DOCTYPE html>
<html lang="en">
  <head>
    <title>JQuery JSONP</title>
    <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
    <script>
    $(document).ready(function(){
 
        $("#useJSONP").click(function(){
            $.ajax({
                url: 'http://taipeicityopendata.cloudapp.net/v1/TaipeiOGDI/StopWaterInfo',
                data: {format: 'json'},
                dataType: 'json',
                success: jsonpCallback
            });
        });
 
    });
     
    function jsonpCallback(data){
    $('#jsonpResult').text(data.d[0].stitle);
    }
    </script>
  </head> 
  
  <body>
    <input type="button" id="useJSONP" value="Use JSONP"></input>
    <span id="jsonpResult"></span>
  </body>
</html>

如果使用 Chrome 的開發人員工具,在 Console 頁次中會看到錯誤訊息 "XMLHttpRequest cannot load .... Origin null is not allowed by Access-Control-Allow-Origin.", 表示因為只限來源網域才能進行 ajax, 所以無法載入所要的資料。

為了解決這個問題, 就有人想出了 jsonp 的方法。由於瀏覽器在處理 <script src="...">
的時候, 並沒有來源網站的限制, 因此只要能動態的產生一個 <script>
元素, 指定 src 屬性, 就可以動態載入並執行一段 JavaScript 碼, 若是 server 端配合, 產生類似以下的程式碼:

jsonCallnack(原本的 json 資料)

那麼當瀏覽器為動態產生的 <script> 元素載入此段程式碼執行, 就會呼叫 jsonCallback 函數處理 json 資料, 達到跨網域存取資料的目的。

為了完成上述動作, 一般就是在 <script> 的 src 屬性值指定的 URL 添加 "callback=jsonCallback" 參數, server 端收到這個請求時, 就會檢查 callback 參數的值, 將此參數搭配 json 資料產生一段如同前述內容的 JavaScript 碼。

你可以發現 jsonp 要能成功, 必須仰賴 server 端配合, 而我們所存取的台北市資料公開平台就支援 jsonp, 會在發現查詢字串中有 callback 參數時, 改用 jsonp 的方式運作, 傳回一段 JavaScript 碼, 若是沒有 callback 參數, 就直接傳回 json 格式的資料。

在 jQuery 中, 只要在呼叫 ajax() 時把 dataType 選項指定為 "jsonp", ajax() 就會改用 jsonp 的方式, 動態產生 <script>, 並且自動依據 success 選項指定的 callback 函數, 在 url 選項的內容加入 "callback=XXX" 的參數, 其中 XXX 就是 success 選項所指定函數的名稱。

如果我們把之前的範例改為這樣:

$("#useJSONP").click(function(){
  $.ajax({
    url: 'http://taipeicityopendata.cloudapp.net/v1/TaipeiOGDI/StopWaterInfo',
    data: {format: 'json'},
    dataType: 'jsonp',
    success: jsonpCallback
   });
});

就可以看到瀏覽器上顯示傳回的停水資訊中的第一筆資料的 stitle 欄位內容, 若是觀察開發人員工具中的 Network 頁次, 會看到發出的請求為:

http://taipeicityopendata.cloudapp.net/v1/TaipeiOGDI/StopWaterInfo?callback=jQuery1720631575214676559_1339854122194&format=json&_=1339854129075

你可能會覺得很奇怪, 明明我們指定的 callback 函數是 jsonpCallback, 為什麼 URL 中的 callback 參數內容卻不一樣?這是因為 jQuery 會在中間介接一個 jQurey 自動產生的函數, 在這個函數中再去呼叫我們指定的 callback 函數。

如果你要存取的 server 端不是以 "callback" 作為識別名稱, 而且你沒有辦法修改 server 端的程式, jQuery 的 ajax() 函數也提供你透過 jsonp 選項自訂參數名稱的功能, 在這種情況下, 你可以透過 jsonpCallback 選項指定 callback 函數, 此外, 還可以再透過 success 選項指定成功執行時要呼叫的 callback 函數。我們可以再把之前的範例改為這樣:

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>JQuery JSONP</title>
    <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
    <script>
    $(document).ready(function(){
 
        $("#useJSONP").click(function(){
            $.ajax({
                url: 'http://taipeicityopendata.cloudapp.net/v1/TaipeiOGDI/StopWaterInfo',
                data: {format: 'json'},
                dataType: 'jsonp',
                jsonp: 'callback',
                jsonpCallback: 'jsonpCallback',
                success: function(){
                    alert("success");
                }
            });
        });
 
    });
     
    function jsonpCallback(data){
    $('#jsonpResult').text(data.d[0].stitle);
    }
    </script>
  </head> 
  
  <body>
    <input type="button" id="useJSONP" value="Use JSONP"></input>
    <span id="jsonpResult"></span>
  </body>
</html>

執行後除了可以看到取回的停水資訊外, 也會因為 success 選項指定的 callback 函數, 而顯示訊息視窗。如果觀察開發人員工具列中 Network 頁次, 可以看到發出的請求其 URL 為:

http://taipeicityopendata.cloudapp.net/v1/TaipeiOGDI/StopWaterInfo?callback=jsonpCallback&format=json&_=1339854333659

可以確認 jQuery 完全依照 jsonp 與 jsonpCallback 選項的值改寫了 url 選項指定的內容。透過上述的說明, 就可以利用 jQuery 進行跨網域的 ajax 存取了。

星期四, 6月 14, 2012

解決資料夾/檔案都變成唯讀的狀況

其實我之前應該就有遇過, 但以前似乎都選擇逃避的方式, 今天又再遇到一次, 趕快把發生此種狀況的步驟以及可能的解決方案整理出來:
  • 發生資料夾/檔案變唯讀的過程:我通常資料都放在 D:, 系統安裝在 C:, 今天發生的狀況是原本用來登入的帳戶 u01 發生user profile 錯誤, 無法登入, 就想說新建一個帳號來用, 建了 u02 登入後, 就發現 d: 下許多資料夾都變成唯讀, 即使用檔案總管取消唯讀屬性, 甚至用 attrib -r 都沒用。
  • 仔細看了這些變成唯讀的資料夾, 發現除了有 Administrators 群組可以完全控制的權限外, 這些資料夾都額外有加入 u01 使用者可完全控制的權限 (u01 與 u02 都有加入 Administrators 權限)。
  • 我就直接把這些資料夾也加入 u02 可以完全控制的權限, 就解決了。
目前還沒時間取搞懂這 Windows 的權限管理, 所以還說不出個所以然到底為什麼, 晚一點再來研究!