API 是什麼?
API 就是一段網址,只要照著對方要求的規格來輸入正確的網址,就能串接對方提供的資料。配合 HTTP 協定的規範,就可以達成更多資料交換的功能。
用 node.js 呼叫 API 與在網頁上呼叫的根本差異是什麼?
Node.js:
使用 Node.js 來呼叫 API,Node 會直接讓電腦向 Server 發送 request,並將 response 完整呈現出來,不會檢查 response 的安全性,所以存在安全風險。
瀏覽器:
從瀏覽器上呼叫 API 會先透過 「 瀏覽器 」
處理,再讓電腦發送 request 給 Server,回傳回來的 response 會經過瀏覽器
檢查,確保資料安全才會呈現出來。
傳送資料 - 方法1: 表單 form
跟 JavaScript 並沒有太大關係,是瀏覽器提供的一種發送 request 的方法
- 流程:
瀏覽器發送一個 帶上參數 的 request 到一個新的頁面
然後將 Server 回傳的 「 response 渲染在頁面上 」 - 缺點:瀏覽器會「換頁」顯示結果
<form method="POST" action="https://google.com">
...
</form>
- GET 方法:參數就會直接被放在 URL 上面
- POST 方法: 參數會放在 request 的 body 裡面
傳送資料 - 方法2: Ajax
全名為 Asynchronous JavaScript and XML
- 流程:
瀏覽器發送一個 帶上參數 的 request 到 一個新的頁面
然後將 Server 回傳的 「 response 傳給頁面上的 JS 」 - 優點:不像表單一樣有換頁問題
Asynchronous 這個單字指的是「非同步」
- 「同步」的情況,發送完 request 與等待 Response 之間的過程,整個 JavaScript 引擎是不會執行任何東西的!你點任何有牽涉到 JavaScript 的東西,都不會有反應,因為 JavaScript 還在等 Response 回來。
- 「非同步」的情況,發送完 request 之後就不管它了,不等結果回來就繼續執行下一行
// 假設有個發送 Request 的函式叫做 sendRequest
var result = sendRequest('https://api.twitch.tv/kraken/games/top?client_id=xxx');
// 上面 Request 發送完之後就執行到這一行,所以 result 不會有東西
// 因為 Response 根本沒有回來
console.log(result);
- 非同步的 Function 不能直接透過 return 把結果傳回來」,為什麼?因為像上面這個例子,它發送 Request 之後就會執行到下一行了,這個時候根本就還沒有 Response
- 下圖非同步在發送 Request 之後,不用等 Response 回來,可以繼續執行其他程式碼,等
Response 回來之後,會透過 Callback Function 把資料帶進來。
// 假設有個發送 Request 的函式叫做 sendRequest
sendRequest('https://api.twitch.tv/kraken/games/top?client_id=xxx', callMe);
function callMe (response) {
console.log(response);
}
// 或者寫成匿名函式
sendRequest('https://api.twitch.tv/kraken/games/top?client_id=xxx', function (response) {
console.log(response);
});
XMLHttpRequest
- JavaScript 提供的 API,主是要拿來發送 Request 與接收 response
要發送 Request 的話,就要透過瀏覽器幫我們準備好的一個物件,叫做XMLHttpRequest,範例程式碼如下:
var request = new XMLHttpRequest();
request.open('GET', `https://api.twitch.tv/kraken/games/top?client_id=xxx`, true);
request.onload = function() {
if (request.status >= 200 && request.status < 400) {
// Success!
console.log(request.responseText);
}
};
request.send();
- 指令:
const request = new XMLHttpRequest;
:實例物件
request.open()
:設定 Request
設定 Method
& URL
& 同步 | 非同步
& 使用者 & 密碼
前兩項為必填、後三項可選
request.open(<method>, <url>, <async:b>, <user:s>, <password:s>)
request.setRequestHeader()
:設定 Request header
request.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded'); // => 設定 Content-Type
request.setRequestHeader('Client-ID', token); // => 設定 client-ID
request.onload
:監聽 load 事件
當瀏覽器拿到 response
時,會觸發 onload
事件綁定的 callback function
如果是用 addEventListener
方法監聽,事件名稱是 load
request.onload = function() {
// => callback
}
request.onerror
:監聽 error 事件
request.onerror = function() {
// => callback
}
request.send()
:發送 Request
function sendRequest() {
const request = new XMLHttpRequest();
const url ='https://dvwhnbka7d.execute-api.us-east-1.amazonaws.com/default/lottery';
const errorMessage = "系統不穩定,請再試一次!";
request.open('GET', url, true);
request.onload = () => {
if (request.status >= 200 && request.status < 400) {
let data;
try {
data = JSON.parse(request.response)
} catch(err) {
alert(errorMessage);
console.log(err);
return;
}
if (!data.prize) {
alert(errorMessage);
return;
}
} else {
alert(errorMessage);
}
}
request.onerror = () => {
alert(errorMessage);
}
request.send();
}
同源政策 Same Origin Policy
只要瀏覽器發的 request 位置 跟 Server 端的 位置是不同源(不同網域 ), request 就會被瀏覽器擋掉。
相同 domain 就是同源,但同網域的 http & https 也是不同源,所以如果你是接別人 API 的話,大多數情形都是不同源的。
值得注意的一點是:「 Request 還是有發出去,而且瀏覽器也確實有收到 Response 」,重點是因為 瀏覽器 才會有同源政策,主要是因為安全性的考量,不讓你的 JavaScript 拿到並且傳回錯誤。
CORS 跨來源資源共用
全名為 Cross-Origin Resource Sharing
- 其中做了兩層防護:
第一層:不隨意讓使用者拿資料,需設定 Access-Control-Allow-Origin
第二層:不隨意讓使用者更改資料,需通過 Preflight Request 檢查
第一層防護 - 不隨意讓使用者拿資料
如果想開啟跨來源 HTTP 請求的話,Server 必須在 Response 的 Header 裡面加上Access-Control-Allow-Origin
當瀏覽器收到 Response 之後,會先檢查Access-Control-Allow-Origin
裡面的內容,如果裡面有包含現在這個發起 Request 的 Origin 的話,就會允許通過,讓程式順利接收到 Response。
Content-Type: application/json
Content-Length: 71
Connection: keep-alive
Server: nginx
Access-Control-Allow-Origin: * //這行是重點
Cache-Control: no-cache, no-store, must-revalidate, private
Expires: 0
Pragma: no-cache
Twitch-Trace-Id: e316ddcf2fa38a659fa95af9012c9358
X-Ctxlog-Logid: 1-5920052c-446a91950e3abed21a360bd5
Timing-Allow-Origin: https://www.twitch.tv
*
星號就代表萬用字元,意思是任何一個 Origin 都接受。所以當瀏覽器接收到這個 Response 之後,比對目前的 Origin 符合*
這個規則,檢驗通過,允許我們接受跨來源請求的回應。
除了這個 Header 以外,其實還有其他的可以用,例如說 Access-Control-Allow-Headers
跟 Access-Control-Allow-Methods
,就可以定義接受哪些 Request Header 以及接受哪些 Method。
- 總結:
需要確保 Server 端有加上Access-Control-Allow-Origin
,不然 Response 會被瀏覽器給擋下來並且顯示出錯誤訊息。
第二層防護 - Preflight Request
CORS 會把 Request 分成兩種
簡單請求:
- ( 非 GET/POST/HEAD || 帶 Header 參數 )沒有加任何自定義的 Header,而且又是 GET/POST/HEAD 的話,就是簡單請求
- 屬於簡單請求的 Request 不用經過預檢
非簡單請求:
- 避免使用者任意更改資料庫內容,須先經過預檢
- 會先送出一個 Request 叫做 Preflight Request,中文翻作「預檢請求」,因為非簡單請求 可能會更改資料庫內容,因此會先透過 Preflight Request 去確認後續的請求能否送出。
- 如果這個 Preflight Request 沒有過的話,真的 Request 也就不會發送了,這就是預檢請求 的目的。
傳送資料 - 方法3: JSONP
全名叫做 JSON with Padding
- 有鑒於
Ajax
一定會受到同源政策影響,所以有個另類的方式拿到 Response 資料,就是JSONP
。 - 網頁上有些標籤不受同源政策的限制,像是圖片
<img>
,因為沒有安全性的問題,所以可以存取跨來源的圖片。還有像是引入 JS 的標籤<script>
也可以引入其他 domain 的 js 進來, 等我們拿到 src 的內容 (response) ,再進行解析!所以簡單來說JSONP
就是 利用sr
不受同源限制的特性,直接載入一隻帶參數的js,當作是發 request,用一個 function 包裝起來。 - 下圖透過你帶過去的callback這個參數當作函式名稱,把 JavaScript 物件整個傳到 Function 裡面,你就可以在 Function 裡面拿到資料。
<script src="https://api.twitch.tv/kraken/games/top?client_id=xxx&callback=receiveData&limit=1"></script>
<script>
function receiveData (response) {
console.log(response);
}
</script>
- 但
JSONP 的缺點
就是你要帶的那些參數「 永遠都只能用附加在網址上的方式(GET)帶過去,沒辦法用 POST 」` - 使用 JSONP 傳送資料,
也要 Server 端有提供 JSONP 的方法
( 意指用 callback function 包起來 )才行,不然回傳的 Response 就只是字串而已,沒有辦法取得資料 如果能用 CORS 的話,還是應該優先考慮 CORS
補充:
資料格式 XML & JSON
XML
Extensible Markup Language
跟 HTML 有點像、都是標記語言
Markup Language,特色是「 內容用前後標籤包起來 」,因為此格式檔案較大
、且不好閱讀
,所以現代開發比較少人用 XML。
JSON
JavaScript Object Notation
乍看跟 JavaScript 的物件很像,但 key 值必須要用雙引號 "key" 包起來
JSON 字串可以包含 陣列 ( 用中括號 [ ] ) 以及 物件 ( 用大括號 { } )
- 小規定:
JSON 格式不能使用註解
Vaule 值沒有辦法放 function