# REST與RESTful API ## REST ### 概述 * REST全名為**Representational State Transfer**(表現層狀態轉換),為奠基於HTTP協議的World Wide Web(WWW)架構設計風格,目的是讓不同的程式在網際網路中相互傳遞資訊 ### 原則 1. 用戶端-伺服器Client-Server * 使用者介面與伺服器資料儲存的關注點分離 2. 無狀態Stateless * 每一個用戶端的請求必須包含所有必要資訊,伺服器不會保留前一個請求的任何狀態,即每一個請求皆完全獨立 * 無狀態意旨伺服器獨立於所有之前的請求,所以用戶端可以按任何順序去請求資源 3. 可快取性Cacheability * 用戶端從伺服器獲得資源後,若此資源未再被變更,則後續直接由cache取用本地資源,不需再向伺服器發出請求 * 良好的快取機制可減少用戶端及伺服器間的請求及回應,提高互動效率 4. 統一介面Uniform Interface * 簡化系統架構,以減少[耦合性](https://ithelp.ithome.com.tw/articles/10191761),使所有模組各自獨立 * 提供統一的操作方式與規格 5. 分層系統Layered System * 各伺服器藉由系統劃分為不同層次,每層各自獨立,可專責特定功能、個別實施不同的安全策略,便於管理及維護,也提高系統整體安全性 * 藉由負載均衡、共享快取來提升性能 6. 按需代碼Code-On-Demand (optional) * 伺服器可隨時傳送程式碼擴充新功能,以因應用戶端的需求 ## RESTful API ### 概述 * 依循REST架構風格的API稱為RESTful API ### 組成元件 1. 資源位址`URI` * `URI`(Uniform Resource Identifier):區分不同資源的獨一無二識別碼,屬於通用概念,具體方法包含`URL`及`URN` * `URN`(Uniform Resource Name):利用名稱識別資源 * `URL`(Uniform Resource Locator):利用位址識別資源 2. 傳輸資源的類型,如`JSON`、`XML`、`YAML` 3. 對傳輸資源的操作,如`HTTP`請求方法:`GET`、`POST`、`DELETE`等 ### 總結 * RESTful API提供簡單、高效率且靈活的方式進行設計及開發,讓不同的系統更容易整合 ### 參考文章 * [API 是什麼? RESTful API 又是什麼?](https://medium.com/itsems-frontend/api-是什麼-restful-api-又是什麼-a001a85ab638) * [什麼是 RESTful API?](https://www.explainthis.io/zh-hant/swe/restful-api) * [REST - Wikipedia](https://en.wikipedia.org/wiki/REST) * [HTTP - Wikipedia](https://en.wikipedia.org/wiki/HTTP) # 同步與非同步 ### 概述 * JavaScript屬於**單執行緒**(Single-Thread)語言,一次只處理一件事 * 在同步的情況下,程式碼會逐行執行,而在非同步的情況下,則無需等待前一個指令完成,就執行後續的程式碼 * 非同步對於網頁瀏覽很重要,可避免阻塞(Blocking) ### 關於`Callback Function`與非同步 * 可用於控制特定函式的執行時機 * 可作為引數(argument)被傳入另一個函式參數中並執行的函式 ```javascript function printFirst (callbackFn) { console.log ('First!'); callbackFn(); }; function printLater () { console.log ('Later!'); }; printFirst(printLater); // output // First! // Later! ``` ### 常見的`callback function` 如Web API中的`addEventListener()`、`setTimeout()`等 * 由於`setTimeout()`屬於非同步事件,即便`printFirst`函式中定義的時間為0,`printLater`函式仍然會先執行 ```javascript function printFirst () { setTimeout (function () { console.log ('First') }, 0) // 間隔時間0秒 }; function printLater () { console.log ('Later!'); }; printFirst(); printLater(); // output // Later! // First! ``` * 利用`callback function`搭配`setTimeout()`控制函式的執行時機 ```javascript function printFirst (callbackFn) { setTimeout (function () { console.log ('First!'); callbackFn(); }, 3000) }; function printLater () { console.log ('Later!'); } printFirst(printLater); // output // (間隔時間3秒) // First! // Later! ``` ### `Callback function`的缺點:Callback Hell * 堆疊的巢狀結構難以閱讀及維護 ```javascript a (function (rtnA) { b (rtnA, function (rtnB) { c (rtnB, function (rtnC) { d (rtnC, function (rtnD) { e (rtnD, function (rtnE) { f (rtnE, function (rtnF) { console.log (rtnF); // CALLBACK HELL!! }) }) }) }) }) }) ``` * 可利用`Promise`解決 ### 利用`Promise`處理非同步 #### 概述 * `Promise`屬於物件,其建構函式接收`executor function`執行函式的兩個參數:`resolve`、`reject`,分別表示非同步操作成功或失敗,回傳結果後表示`promise`事件結束 ```javascript new Promise ( /* executor */ function (resolve, reject) { ... resolve(); reject(); }) ``` #### 狀態 * 共有三種狀態:等待中`pending`、已完成`fulfilled`、已拒絕`rejected` * `Promise`初始狀態為`pending`,`resolve`用於將`promise`的狀態由`pending`轉變為`fulfilled`,表示非同步操作成功 * 反之,`reject`用於將`promise`的狀態由`pending`轉變為`rejected`,表示非同步操作失敗 #### `Promise`原型方法 * 利用`.then()`搭配`.catch()`分別取得操作成功及失敗的結果 ```javascript let testPromise = new Promise ((resolve, reject) => { let condition = true; if (condition) { setTimeout (() => { resolve('succeeded'); // 成功時回傳值 }, 3000); } else { reject('failed'); // 失敗時回傳值 }; }); testPromise .then ((msg) => { console.log (msg); }) .catch ((errMsg) => { console.log (errMsg); }) ``` * 補充:`.then()`方法可帶入兩個callback function : `onFulfilled`, `onRejected`做為參數,同樣可分別取得操作成功及失敗的成果,以上面的範例進行改寫: ```javascript let testPromise = new Promise ((resolve, reject) => { let condition = false; if (condition) { setTimeout (() => { resolve('succeeded'); }, 30); } else { reject('failed'); }; }); testPromise .then ((msg) => { console.log (msg); }, (errMsg) => { console.log(errMsg) }) ``` #### Promise Chaining * `.then()`及`.catch()`皆可進行串聯傳遞結果,故可利用此特性來避免製造callback hell * `return`回傳的`promise`物件可再進一步被取用 ```javascript function testPromise (condition) { return new Promise ((resolve, reject) => { condition ? resolve (``) : reject (`failed`); }); } testPromise (true) .then (resolve => { console.log (`succeeded(1)`) return testPromise (true) }) .then (resolve => { console.log (`succeeded(2)`) return testPromise (false) // 於此行進入catch }) .then (resolve => { console.log (`succeeded(3)`) // 故第三次成功的結果不會印出 return testPromise (true) }) .catch (reject => { console.log (`failed(1)`) return testPromise (false) }) .catch (reject => { console.log (`failed(2)`) }) // output // succeeded(1) // succeeded(2) // failed(1) // failed(2) ``` ### 參考文章 * [JavaScript 什麼是Callback函式 (Callback Function)?](https://matthung0807.blogspot.com/2019/05/javascript-callback-callback-function.html) * [Promise - JavaScript | MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) * [[教學] JavaScript Promise 的用法](https://www.shubo.io/javascript-promise/) # AJAX技術 ### Fetch * JavaScript用於完成HTTP請求的原生方法,在ES6前只有`XMLHttpRequest`,ES6新增`Fetch` * 基於`Promise`所開發:回傳物件使用`.then()`、`.catch()`...等方法 #### 基本用法 * 回傳的`ReadableStream`物件需針對不同資料類型使用不同的解析方法,才能取得需要的資料 * `.blob()` ([binary large object](https://hackmd.io/@l-zHCaalQSq59NxFixnqyg/rkvXJlCG5))、`.json()`、`.text()`... * `GET`請求 ```javascript fetch ('https://...') .then((response) => { return response.json(); // 解析為JSON物件 }) .then((data) => { console.log (data); // 進一步取得資料 }) ``` ### Axios * 環境安裝:利用[CDN](https://github.com/axios/axios) * 和`Fetch`一樣基於`Promise`所開發 #### 基本用法 * `GET`請求 ```javascript axios.get ('https://...') .then((response) => { console.log (response.data); }) .catch((error) => { console.log (error) }) ``` * `POST`請求 ```javascript axios.post('url', { email: 'example@gmail.com', password: '0000' }) .then((response) => { console.log (response) }) .catch((error) => { console.log (error) }) ``` ### 參考文章 * [Fetch - JavaScript | MDN](https://developer.mozilla.org/en-US/docs/Web/API/fetch) * [Web APIs - 使用原生 Fetch API 獲取遠端資料](https://awdr74100.github.io/2020-08-08-webapis-fetch/) * [axios | GitHub](https://github.com/axios/axios)