API 是什麼?為什麼需要它?
API 全名是 Application Programming Interface(應用程式介面)
最重要的就是這個 Interface(介面)
透過這個介面來跟別人溝通
例如:
USB 是一個介面
- 對電腦來說 ->
電腦只需要根據 USB 這個介面,去實作出「怎麼拿資料、怎麼傳資料」就可以了 - 對「製作 USB 隨身碟的廠商」來說 ->
廠商也只需要根據 USB 這個介面,去實作出「怎麼拿資料、怎麼存資料」就可以了
中間是透過 USB 這個介面來溝通的
使用 API
當我跟某人要東西時,有時沒辦法直接跟他要,就必須透過他提供的 API 來跟他要東西
提供 API
假設我是提供資料的人,我不希望別人都可以直接來存取我的資料庫(我不會直接把資料庫權限給別人),所以我需要提供一個 API,讓別人透過這個介面來存取資料
在這個介面上,我可以定義出「哪些東西可以給、哪些東西不能給」
透過 API,可以讓雙方交換資料
以下舉幾個例子:
作業系統提供了許多 API,我們必須要使用這些 API 才能達成我們想做的事情
我要怎麼知道我電腦目前的網路狀況?
作業系統有提供一個 API,來讓我知道網路狀況
我可以寫程式呼叫這個 API,來知道目前的網路狀況
我要怎麼讀取檔案?
讀取檔案一樣要透過作業系統,都是透過作業系統提供的 API 去讀取檔案(例如 JS 檔案、css 檔案等等)
我要怎麼拿到 Facebook 的好友資料?
我必須去串接 Facebook 的 API,才能拿到上面的好友資料
別人要怎麼拿到我網站上的資訊呢?
我需要提供 API 給別人,別人就可以透過 API 拿到我網站上的資訊
別人如何在我網站上新增資料呢?
我提供給別人「新增資料的 API」,別人就可以透過 API 在我網站上新增資料
API 與 Web API
API 跟 Web API 有什麼不同呢?
API
API 的提供者可以來自世界各地、四面八方,不一定要透過網路才能提供 API
例如:作業系統不用透過網路,也可以提供給我「讀取檔案、新增檔案」的 API
Web API
Web API 顧名思義就是會有「網路」
通常我們提到的 Web API 講的都會是 HTTP API,也就是「透過 HTTP 協定的形式所提供的 API」,透過 HTTP 這個協定來交換資料(透過 request 要資料,拿到的 response 就是我要的資料了)
HTTP API 例如:
Facebook 提供的 Graph API
twitter 提供的 GET statuses/home_timeline
裡面有提供一個 Resource URL https://api.twitter.com/1.1/statuses/home_timeline.json ,
只要呼叫這個 api 網址(也可以傳入一些參數)
request 就會長這樣(就是一個 HTTP 的 request)
GET https://api.twitter.com/1.1/statuses/home_timeline.json
歐付寶提供的 SDK 元件
SDK (software development kit)
可以把 SDK 想成是一個 library,裡面幫我做好了很多個 API
以上面 twitter 的例子來說,因為沒有 SDK,所以我需要自己呼叫 https://api.twitter.com/1.1/statuses/home_timeline.json 這個 api 網址
但如果 twitter 有提供 SDK 的話,我就只需要呼叫 twitter.getTimeline()
這個 function 就可以了(因為 SDK 已經幫我包裝好了)
Lorem Picsum 提供的「拿取假圖的 API」
我只要發送 request 到 https://picsum.photos/200/300 這個網址,得到的 response 就會是一張圖片
串接 HTTP API 實戰
這裡要使用的 API 是 Reqres
它有提供一系列讓我測試用的 API
用 Node.js 發送 request
「透過 Node.js 從“我的電腦”發送 request」跟「透過瀏覽器發送 request」是兩件完全不同的事情
用之前講過的 request 套件 來改:
request 套件只能在 Node.js 上面執行,無法在瀏覽器執行
底下這個預設的模板,沒有地方讓我寫 HTTP method,但是預設就會使用 GET
因此,這段的意思就是:
呼叫這個 request()
函式,就會幫我發送一個 GET request 到 https://reqres.in/api/users 這個網址去,然後把 response Body 印出來
request()
函式裡面的是一個 callback function,我用這個 callback function 來看到回傳的內容
const request = require('request');
request(
'https://reqres.in/api/users',
function (error, response, body) {
console.log(body);
}
);
在 request()
函式裡面,描述「這個 request 所帶的 Header」會帶有這些資訊:
request 這個套件就會幫我發送「帶有這些資訊的 request」出去
method: GET
URL: https://reqres.in/api/users
執行 node index.js
,就可以看到 https://reqres.in/api/users 這個 API 所返回的資料(response Body)
根據這個 API 的格式,只要在 API 網址後面加上 /2
就可以拿到「ID = 2」這個 user 的資料
const request = require('request');
request(
'https://reqres.in/api/users/2',
function (error, response, body) {
console.log(body);
}
);
process.argv
現在,我要做的功能是:
使用的 API 網址是 https://reqres.in/api/users ,但是當我輸入指令 node index.js 6
就可以拿到「ID = 6」這個 user 的資料
作法如下:
Node.js 有提供一個內建的 module 叫做 process
先把 process 引入進來
接著印出 process.argv
argv
的全名是argument variables
,也就是「參數」
const request = require('request');
const process = require('process');
console.log(process.argv)
request(
'https://reqres.in/api/users',
function (error, response, body) {
console.log(body);
}
);
執行 node index.js 6
,可以看到 console.log(process.argv)
印出的結果是一個 array:
- array 的第一個元素
'/usr/local/bin/node'
就是剛剛輸入的指令node index.js 6
裡面的node
(指令的第一個參數) - array 的第二個元素
'/Users/saffran/Desktop/test/index.js'
就是剛剛輸入的指令node index.js 6
裡面的index.js
(指令的第二個參數) - array 的第三個元素
'6'
就是剛剛輸入的指令node index.js 6
裡面的6
(指令的第三個參數),也就是「我要的 ID」[ '/usr/local/bin/node', '/Users/saffran/Desktop/test/index.js', '6' ]
因此,程式碼就可以改成這樣:
在 API 網址後面加上 process.argv[2]
來取得 array 的第三個元素(指令的第三個參數)
const request = require('request');
const process = require('process');
request(
'https://reqres.in/api/users/' + process.argv[2],
function (error, response, body) {
console.log(body);
}
);
這時,當我執行 node index.js 6
時,就可以拿到「ID = 6」這個 user 的資料了
注意!在API 網址後面要先寫好 /
,因為 process.argv[2]
只會取得 6
,而不是 /6
用 post 新增 user
參考文件 request-Forms
const request = require('request');
const process = require('process');
request.post(
{
url: 'https://reqres.in/api/users',
form: {
name: 'Harry',
job: 'wizard'
}
},
function (error, response, body) {
console.log(body);
}
);
執行 node index.js
就可以看到我剛剛新增的資料了
DELETE
const request = require('request');
request.delete(
'https://reqres.in/api/users/2',
function (error, response, body) {
const json = JSON.parse(body); // 小括號裡面一定要是一個「JSON 格式的字串」才行
console.log(json);
}
);
執行 node index.js
,卻出現了錯誤:Unexpected end of JSON input
這是為什麼呢?
先把 body 印出來看看:
const request = require('request');
request.delete(
'https://reqres.in/api/users/2',
function (error, response, body) {
console.log('body: ', body) // 先把 body 印出來看看
const json = JSON.parse(body); // 小括號裡面一定要是一個「JSON 格式的字串」才行
console.log(json);
}
);
執行 node index.js
,會看到:body 印出來是空的,因為 body 並不是一個 JSON 格式的字串,因此下一行的 JSON.parse(body)
就會出現錯誤「Unexpected end of JSON input」
再把 status code 印出來看看:
const request = require('request');
request.delete(
'https://reqres.in/api/users/2',
function (error, response, body) {
console.log(response.statusCode)
}
);
執行 node index.js
,會看到:204,代表「成功刪除,但 body 沒有內容」,這是很正常的,因為我是使用 DELETE,刪除完之後也沒什麼資料需要回給我
PATCH
印出 status code 和 body 看看
const request = require('request');
request.patch(
{
url: 'https://reqres.in/api/users/2',
form: {
name: 'Romeo'
}
},
function (error, response, body) {
console.log(response.statusCode)
console.log(body)
}
);
output:
回傳 200 代表修改成功了
200
{"name":"Romeo","updatedAt":"2020-07-24T10:02:26.286Z"}
Custom HTTP Headers
request 套件也可以客製化 HTTP Headers
範例程式碼:
這裡所帶的 'User-Agent': 'request'
只是為了要示範「可以客製化 HTTP Headers」這件事,因此要帶什麼 Headers 可以自己決定
在 headers
這個 key 裡面,對應到的 value 就是我想要帶的 Headers
const request = require('request');
const options = {
url: 'https://api.github.com/repos/request/request',
headers: {
'User-Agent': 'request'
}
};
function callback(error, response, body) {
if (!error && response.statusCode == 200) {
const info = JSON.parse(body);
console.log(info.stargazers_count + " Stars");
console.log(info.forks_count + " Forks");
}
}
request(options, callback);
上面的程式碼,如果把 options
和 callback
都直接放到 request()
函式裡面,就會是這樣:
const request = require('request');
request({
url: 'https://api.github.com/repos/request/request',
headers: {
'User-Agent': 'request'
}
},
function (error, response, body) {
if (!error && response.statusCode == 200) {
const info = JSON.parse(body);
console.log(info.stargazers_count + " Stars");
console.log(info.forks_count + " Forks");
}
});
而 User-Agent
(user 的代理人)通常指的就是「瀏覽器」,也就是「幫我送 request 的是哪個程式」,因為瀏覽器會代理 user 去發送 request
(User-Agent
是比較文言文的講法)
例如:
開啟這個網址 https://lidemy-book-store.herokuapp.com/books?_limit=5 打開 devtool 的 Network tab,在 Request Headers 可以看到 User-Agent 會寫一些跟瀏覽器相關的資訊:
Chrome/84.0.4147.89
就是我現在的 Chrome 版本
server 就會知道「這個 user 目前是使用 Chrome/84.0.4147.89 幫他送 request 過來」User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.89 Safari/537.36
資料格式的選擇
關於資料結構,在傳送、接收資料時,資料都會有一定的格式
兩種最常用的資料格式,就是:
- XML
- JSON
XML
XML 的全名是 Extensible Markup Language,跟 HTML 一樣都是一種標記語言
用標籤的形式來表示資料
JSON
JSON 的全名是 JavaScript Object Notation,是一種資料格式
JSON 會如此熱門的原因:
- JSON 是基於 JavaScript 的「物件」所產生的一種資料格式,因此跟 JavaScript 的相容性非常好
- JSON 所佔的空間比 XML 少很多
(XML 要使用很多標籤,光是標籤就佔了很多空間)
注意!JSON 只是長得像物件,但其實是「字串」
JSON.parse()
把「JSON 格式的字串」轉成物件
這裡再用前面講到的 Reqres API 為例
const request = require('request');
request(
'https://reqres.in/api/users/2',
function (error, response, body) {
console.log(body);
}
);
執行 node index.js
會印出:
{"data":{"id":2,"email":"janet.weaver@reqres.in","first_name":"Janet","last_name":"Weaver","avatar":"https://s3.amazonaws.com/uifaces/faces/twitter/josephstein/128.jpg"},"ad":{"company":"StatusCode Weekly","url":"http://statuscode.org/","text":"A weekly newsletter focusing on software development, infrastructure, the server, performance, and the stack end of things."}}
console.log(body)
看起來是一個「JavaScript 的物件」,但其實 印出的是一個「字串」(一個 JSON 格式的字串)
因此,要先把字串轉成物件,才能用「物件的方式」存取到裡面的值:
可以用一個 JS 的函式叫做 JSON.parse()
,來把「JSON 格式的字串」轉成「JavaScript 的物件」
注意!JSON.parse()
的小括號裡面一定要是一個「JSON 格式的字串」才行
const request = require('request');
request(
'https://reqres.in/api/users/2',
function (error, response, body) {
const json = JSON.parse(body); // 小括號裡面一定要是一個「JSON 格式的字串」才行
console.log(json);
}
);
這時,印出的 console.log(json)
就會是「JavaScript 的物件」了
{
data: {
id: 2,
email: 'janet.weaver@reqres.in',
first_name: 'Janet',
last_name: 'Weaver',
avatar: 'https://s3.amazonaws.com/uifaces/faces/twitter/josephstein/128.jpg'
},
ad: {
company: 'StatusCode Weekly',
url: 'http://statuscode.org/',
text: 'A weekly newsletter focusing on software development, infrastructure, the server, performance, and the stack end of things.'
}
}
因此,我就可以使用 console.log(json.data.first_name)
印出 Janet
了!
JSON.stringify()
把物件轉成「JSON 格式的字串」
const obj = {
name: 'Harry',
job: 'wizard'
}
console.log(obj)
這時,印出的 obj
就是一個「JavaScript 的物件」:
{ name: 'Harry', job: 'wizard' }
如果我想要把這個「JavaScript 的物件」轉成「JSON 格式的字串」,就可以使用一個函式叫做 JSON.stringify()
const obj = {
name: 'Harry',
job: 'wizard'
}
console.log(JSON.stringify(obj))
這時,印出的 JSON.stringify(obj)
就是一個「JSON 格式的字串」:
{"name":"Harry","job":"wizard"}