在Web開發上我們經常聽過且用過RESTful API,是經常遇到的技術名詞,那麼RESTful API到底是什麼?以及為何要用RESTful API?
在本文中ㄚ建將介紹RESTful API是什麼?並建立一個簡單的RESTful API。
- RESTful API 是什麼?
- 為何要使用REST API?
- RESTful API的概念是什麼?
- 如何建立一個RESTful API
什麼是RESTful API?
在了解RESTful API之前讓我們先從基本的API的概念開始。
什麼是API?
讓我們先從下面的影片來了解API的概念。
API全名為Application Programming Interface(應用程式介面),它定義了軟體之間通訊時需要遵循的規則。我們使用的網站或是app就如同影片中譬喻的餐廳,API扮演著應用程式之簡彼此溝通的橋樑,如同影片中送餐的服務生負責傳遞(轉達)我們的行為/點餐需求,傳遞需求並建立連結。而我們不需要知道其(api)背後的運作方式。
也就是當我們需要某些功能例如開發一個提供地圖定位的服務,可以將我們的需求(點餐行為)透過API(服務生)傳遞給其他開發人員或是第3方提供的API服務(廚房),例如Google Map API,整個過程我們只需要知道3件事:
- 發送API之前需要提供什麼資料?
- 成功時API會回應什麼?
- 失敗時API會回應什麼?
因此我們不必了解Google Map API背後的邏輯機制,只需要依照開發人員或是提供API服務的第三方,例如Google,提供的規格文件來使用即可。如此一來在開發上省下了自行開發並維護API的難題,而我們只需要專注於處理與維護由API取得結果即可!
對API有了概念之後,接下來便可以進入本文的主軸RESTful API。
什麼是REST?
REST的全名為Representational State Transfer( 表現層狀態轉移),REST並不是獨特的技術規範,而是一種軟體架構的風格。
遵循REST架構所設計的API便被稱為REST API,而實作REST架構的Web服務則稱為RESTful Web服務。在術語的稱呼上RESTful API與REST API可以相互交換使用。
在實務上並非所有前端UI都會與HTML頁面一起工作,或是透過Server Side Render來產生畫面。我們可以從下列3種實務上應用來看:
- 行動app。
- SPA網站。
- 其他服務API:如Google Map、中央氣象局等其他網站的Open API。
上面的應用例子之間的共同點便是僅Client與Server透過資料進行溝通,我們不希望由Server傳遞HTML的程式來構建頁面,Client只是需要Server提供的資料,這便是使用API的核心思想。為了在開發上增進API的可視性與跨平台可移植性,透過REST對API的運作施加設計規範便是RESTful API。
如何設計RESTful API?
在開始設計RESTful API之前,首先我們需要先從RESTful-Triangle的概念來了解RESTful API三個重要概念。
RESTful-Triangle
- Nouns 名詞:用來定義傳輸資源的URL。
- Verbs 動詞:對Nouns名詞的操作方式,也就是HTTP Methods。
- Content Types 資源呈現方式:資源的呈現方式有許多種,最常用的便是JSON,相較於其他方式JSON更為簡潔,且容易轉換成JavaScript處理。
以上3點的組合也就是API端點(API Endpoints),它們構成了RESTful API的定義。
HTTP Methods (HTTP Verbs)
在定義了API的URL之後,我們便必須告知Server需要對資料做什麼?方法便是由HTTP Methods完成,下面介紹5種常用的HTTP Methods:
- GET:從Server取得資源或是狀態。
- POST:將資源發佈到Server,例如新增一筆資料。
- PUT:在Server上新增或修改指定資源。
- PATCH:更新Server上現有資源的一部分。
- DELETE:刪除Server的資源。
REST 架構風格的原則
在RESTful-Triangle我們理解了RESTful API的概念,以下的REST架構原則定義了一些設計RESTful API的核心原則:
- 統一介面:具有HTTP Methods、route與明確定義的請求與響應資料結構的組合。
- 無狀態:Server與Client不儲存連接歷史,每個請求都是獨立的。
- 可快取性:RESTful API可以透過快取來改善Server的回應時間。
- 客戶端服務器分離:Client不關心持久化的資料儲存。
- 分層系統:Server可以將請求轉發到其他API。Client可以連線到其他授權的API,而仍然可以接收來自Server的回應。
- 隨需編碼:可執行的程式碼可從Server傳輸到Client,以臨時擴展或自訂Client功能。
這6項原則之中,其中統一介面與無狀態較為核心。
統一介面:簡單定義了我們設計RESTful API時應該具有明確定義的API端點(API Endpoints),也就是上述RESTful-Triangle的3個概念。這讓使用者了解API需要哪些資料,以及返回那些資料。
無狀態:這邊要注意一點!由於HTTP的無狀態特性,我們設計API時,Client與Server是完全分離的,所以不共享任何連線歷史。意味著狀態由Client自行保存,向Server請求時附上。Server不會保存Client的狀態資訊,因此是無狀態的。這樣的設計讓Server每次能完全理解並滿足請求。
建立一個RESTful API
現在我們已經知道設計RESTful API所需的概念以及原則,接下來一起來實際設計一個RESTful API吧!
事前準備
這邊將透過nodejs搭配express建立我們的RESTful API。
安裝方式可以可以參閱nodejs官網
接著開啟vscode,在專案的資料夾內開啟終端機(Terminal)輸入下列指令,建立所需的環境。
npm init
npm install --save express
npm install --save-dev nodemon
npm install --save body-parser
建立環境後,首先修改package.json的設定。
在下圖的scripts位置加入下面提供的程式碼。
//package.json
"start": "nodemon app.js"
如此一來,每當修改程式後,我們便不需要再手動重啟Server。
接下來在根目錄新增routes、controllers資料夾以及app.js。
routes是用來存放api的路由設定,controllers存放負責控制api的檔案。
然後各別資料夾新增helloApi.js。
整個專案的檔案配置如下圖:
下面是各個檔案的程式碼:
app.js:負責管理我們API的路由helloApi以及啟動Server,這邊設定是port 8080,有需要可以自行更改。
// app.js
const express = require('express');
const bodyParser = require('body-parser');
const helloApiRoutes = require('./routes/helloApi');
const app = express();
app.use(bodyParser.json());
// 配置helloApi路由
app.use('/helloApi', helloApiRoutes);
// port8080
app.listen(8080);
constrollers的helloApi.js負責管理api回應的內容。
這邊設定了post與get回應的內容。
// constrollers/helloApi.js
// api 回應的內容
exports.getPosts = (req, res, next) => {
res.status(200).json({
post: [{
title: "First Post!",
content: "This is the first post!"
}]
});
};
//
exports.createPost = (req, res, next) => {
const title = req.body.title;
const content = req.body.content;
// create post in db
res.status(201).json({
message: "Post create successfully!",
post: {
id: new Date().toISOString(),
title: title,
content: content,
}
})
};
routes的helloApi.js負責定義api的get文章的路由。
// routes/helloApi.js
const express = require('express');
const helloApi_Controller = require('../controllers/helloApi');
const router = express.Router();
// GET helloApi/posts
router.get('/posts', helloApi_Controller.getPosts);
// POST /helloApi/post
router.post('/post', helloApi_Controller.createPost);
module.exports = router;
接著在終端機(Terminal)執行npm start啟動Server。
測試我們的API?
首先,來看API的get方法。
打開瀏覽器在localhost:8080輸入API的路由helloApi/post,下圖便是成功取得API回應的結果。
接下來測試API的post方法。
要測試post方法,便需要由前端發送API請求給Server,那麼該有哪些工具可以協助我們?
這邊阿建提供2種方式來測試API:
第一種:Postman,這是一套免費且方便的工具,你可以到Postman官網免費下載安裝。
第二種:codepen,這是可以線上編寫前端的網站,可以用來模擬前端的應用,還可以分享你的成果!
這裡阿建使用第二種方式。原因是,透過codepen我們可以快速建立前端的頁面,並讓我們模擬第三方使用者的操作情形。
這裡阿建提供codepen的範例,在啟動Server後不妨試試看!
See the Pen
restapi test by chieninker (@chieninker)
on CodePen.
首先打開控制台的console,接著點擊Get Posts按鍵,這時Server非但沒有正確的回應,反而出現一段錯誤!
這便是常見的CORS(跨來源資源共用)錯誤。至於錯誤究竟出在哪裡?以及如何解決?
接下來讓阿建帶你一步步處理這個錯誤。
CORS是什麼?
跨來源資源共用(CORS)全名為Cross-Origin Resource Sharing,是一種,它是如何產生?
先看下面這張圖:
在默認的情況下,瀏覽器不允許我們跨站取得資源,也就是當Client與Server在同一網域時,如上圖的A部分,彼此可以相互發送請求與回應。但是當Client與Server處於不同網域便會產生CORS錯誤,如上圖的B部分。其中原因是瀏覽器背後的安全機制所致,所以我們無法跨網域、跨伺服器與跨源共享資源。
然而我們設計API便是希望將Server的資料為各種不同的Client提供服務,這個機制會對API的開發造成問題。
為了解決CORS錯誤,我們需要告訴瀏覽器(這邊是CodePen)讓它可以接受由我們的Server發送的回應。
解決方式
為了解決問題,我們必須在Server更改一些地方。回到app.js,我們需要在這裡設定API的Header。
const express = require('express');
const bodyParser = require('body-parser');
const helloApiRoutes = require('./routes/helloApi');
const app = express();
app.use(bodyParser.json());
// CORS error
app.use((req, res, next) => {
res.setHeader('Access-Control-Allow-Origin', '*');
res.setHeader('Access-Control-Allow-Methods', 'GET, POST');
res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization');
next();
})
app.use('/helloApi', helloApiRoutes);
app.listen(8080);
- Access-Control-Allow-Origin:設定能訪問Server的網域,‘*’表示允許任何網域跨站存取資源。
- Access-Control-Allow-Methods:允許的HTTP Methods
- Access-Control-Allow-Headers:允許的請求標頭,這邊設定Client可以發送在標頭中包含額外授權資料的請求。
為API加入Header後,接著回到CodePen,並重新發送我們的請求,可以看到Server回應的訊息了!
參考資料
NodeJS – The Complete Guide
MDN-CORS
MDN-Access-Control-Allow-Headers
AWS-什麼是 RESTful API?
RESTful API 設計準則與實務經驗