owned this note
owned this note
Published
Linked with GitHub
# API 基礎
###### tags: `Tag(網路基礎)` `Tag(API)`
### API 是什麼?
>[參考資料](https://blog.techbridge.cc/2017/05/20/api-ajax-cors-and-jsonp/) 輕鬆理解 Ajax 與跨來源請求
:::success
就像是自動販賣機的==按鈕/面版==
可口可樂公司的商品中有各類可樂飲料
1.今天你想取得一瓶原味可樂
2.只要單純的按下==面版上==原味可樂的 ==按鈕==
3.接著你就可以在取物口拿到一瓶可樂
上面第二個動作中的==面版/按鈕, 就是所謂的 API== 了
==透過這個面版, 你可以拿到可口可樂公司的飲料==
過程大概就是:
```
- 想要一瓶可樂(想要的資料)
- 按下按鈕(送出資料需求)
- 拿到飲料(取得資料)
```
:::
>[參考資料](https://cola.workxplay.net/what-is-an-api/) 什麼是API?
>[參考資料](https://medium.com/@hulitw/ramen-and-api-6238437dc544) 從拉麵店的販賣機理解什麼是 API
全名為 Application Programming Interface,中文翻譯為「應用程式==介面==」
>簡單來說就是==方便溝通、交換資料的管道==。
API 有分很多種:
:::success
```
* 軟硬體廠商的 API
* 有作業系統用的 API ,用於傳輸、寫入、讀取資料…等等電腦上一切的操作
* 自己團隊內部用的 API
* 對於網頁來說就是 Web API
是基於 http 協定下運作的 API
```
:::
---
### 什麼是 JSON 格式資料?
>[參考資料](https://www.footmark.info/programming-language/javascript/json-format-and-javascript/) JSON 格式與 JavaScript 解析教學範例
#### JSON 格式
JSON 檔案具備了鍵`(Key)`與值`(Value)`
###### ==`JSON 字串`== `可以包含 object (物件) 或 array (陣列) 資料`
``` JS
{
"number": "1020501",
"name": "小傑",
"age": 32,
"sex": "M",
"interest": [
"網頁設計",
"撰寫文章"
]
}
```
#### 如何載入 JSON 檔案
==**串接API的本質就是交換資料**==
---
### JavaScript 處理 JSON
>[參考資料](https://www.footmark.info/programming-language/javascript/json-format-and-javascript/) JSON 格式與 JavaScript 解析教學範例
###### `JavaScript物件`
``` js
{name: 'Renee',isTall: true}
```
###### `JSON 格式的字串`
物件的屬性名name,==在JSON中被引號包起來變成'name'==,也就是說==原本的屬性名到了JSON變成字串==
``` js
{'name':'Jen','isTall':'true'}
```
###### `JavaScript物件 & JSON 格式的字串 一起印出來比對`
![](https://i.imgur.com/RUHaNEE.png)
![](https://i.imgur.com/LFXJWTg.png)
#### `物件與JSON字串的轉換,有兩個重要函式`
==`JSON.stringify()`== 可以 ==將 `JavaScript` 物件序列化成 `JSON` 字串==
第二個參數 可以是 是一個 function 或是 一個 array
第三個參數,來控制結果字串的縮排間距,並會自動換行
```javascript=
// JavaScript 物件
var member = {
"number": "1020501",
"name": "小傑",
"age": 32,
"sex": "M",
"interest": [
"網頁設計",
"撰寫文章"
]
};
// 使用第二個參數 function 方式,排除 age 特性不被序列為 JSON 字串
console.log(
JSON.stringify(member, function(key, value) {
if (key === "age") return undefined;
return value;
})
);
```
![](https://i.imgur.com/9JbsbTD.png)
==`JSON.parse()`== 方法可以將 `JSON` 字串 ==解析成 `JavaScript` 物件== ,這樣就可使用 `JavaScript` 進行操作了
第二個參數 function 用來轉換解析出的屬性值,return 的值決定最後在物件上的特性值
```javascript=
// 宣告字串須使用 `` 包起來
var jsonString = `{
"number": "1020501",
"name": "小傑",
"age": 32,
"sex": "M",
"interest": [
"網頁設計",
"撰寫文章"
]
}`;
// 使用第二個參數 function 方式,排除 age 特性不被包括在物件
var member = JSON.parse(jsonString, function(key, value) {
if (key === "age") return undefined;
return value;
});
// 取得物件
console.log(member);
```
![](https://i.imgur.com/pnyMmBG.png)
>[參考資料](https://www.tutorialspoint.com/proper-way-to-catch-exception-from-json-parse) Proper way to catch exception from JSON.parse
>[參考資料](https://thecodebarbarian.com/the-80-20-guide-to-json-stringify-in-javascript) The 80/20 Guide to JSON.stringify in JavaScript
>[參考資料](https://www.digitalocean.com/community/tutorials/js-json-parse-stringify) JSON.parse() and JSON.stringify()
###### `try…catch 語法使用`
可以==在程式執行可能拋出錯誤的地方使用==,主動捕捉並處理錯誤,==避免整個程式因為發生錯誤而停止執行。==
``` js
try {
// 預期可能會發生錯誤的程式碼
} catch (e) {
// try 區塊有拋出錯誤時,則執行這裡的程式碼
} finally {
// finally 區塊的程式碼一定會在最後被執行
// 可以省略 finally 區塊
}
```
```javascript=
function validateData(jsonData){
let data
try{
data = JSON.parse(jsonData);
} catch(e) {
console.log('Error data', e)
data = null
}
return data
}
console.log(validateData('[1, 5, "false"]'))
console.log(validateData('1, 11')) //null
```
![](https://i.imgur.com/6X24vmA.png)
>[參考資料](https://pjchender.blogspot.com/2017/12/js-error-handling.html) 談談 JavaScript 中的錯誤處理 Error Handling
>[參考資料](https://eyesofkids.gitbooks.io/javascript-start-from-es6/content/part3/error.html) 錯誤與例外處理
---
API 文件通常都不會給出完整網址,因為絕大多數 API 其實都是透過==兩個部分所組成==
- Base URL
- 不同的 path
###### `範例1 串接API,執行後會列出前十本書籍的 id 以及書名`
>[參考資料](https://tigercosmos.xyz/post/2018/11/master_js/object/) 30 天 Javascript 從入門到進階:物件
>[參考資料](https://wcc723.github.io/javascript/2017/12/22/javascript-template-string/) JavaScript Template String 樣板字串
>[color=#ff0cc6][參考資料](https://www.jishuwen.com/d/2GBN/zh-tw) 正確配置 Visual Studio Code 開發和除錯 TypeScript
`Base URL`: `https://lidemy-book-store.herokuapp.com`
![](https://i.imgur.com/j2Kku7Y.png)
###### `學習看request文件`
>[文件連結](https://github.com/request/request#custom-http-headers)
![](https://i.imgur.com/SXWnSnh.png)
```javascript=
// 引入了request的library
const request = require('request');
// 抓取 url 的內容
request.get({
url: 'https://lidemy-book-store.herokuapp.com/books?_limit=10',
}, (error, reponse, body) => {
let data;
try {
data = JSON.parse(body); // 用 JSON.parse處理JSON格式的字串
} catch (error) {
console.log('error');
}
for (let i = 0; i < data.length; i++) {
//ES6 新語法 Template Literals:使用 ${ } 來加入變數或函式
//使用 ${ data[i].id } 來插入變數
console.log(`${data[i].id} ${data[i].name}`)
}
}
);
```
###### `範例2 串接API,可以手動加入參數後顯示前 20 本書的資料、刪除、新增以及修改書本`
>[參考資料](https://www.fooish.com/javascript/switch-case.html) JavaScript switch 語法
>[參考資料](https://www.footmark.info/programming-language/php/ajax-javascript-jquery-example-php/) AJAX JavaScript 與 jQuery 教學範例 for PHP
###### `學習看request文件`
>[文件連結](https://github.com/request/request#custom-http-headers)
![](https://i.imgur.com/8zLx8wP.png)
`Base URL`: `https://lidemy-book-store.herokuapp.com`
:::success
小補充 : `process` 模組
`process` 是 `Node.js` 內建的模組,功能為「 ==取得指令的參數== 」。
- 引入: `const process = require('process')`
- 拿使用者下的參數: ==`process.argv`==
:::
###### `印出process.argv 觀察使用方法`
``` js
console.log(process.argv)
```
會發現印出來的==是一個陣列==,[arr[0]](第一個元素)是 node、 [arr[1]](第二個元素)是 server.js ,[arr[2]](第三個元素)可以得到 我們要的內容。
結論:使用`process.argv` 會==從[arr[2]](第三個元素)開始使用== :`process.argv[2]`
![](https://i.imgur.com/gAk8WQ3.png)
```javascript=
// 顯示前 20 本書的資料、刪除、新增以及修改書本
// 串接API
// 引入了request的library
const request = require('request');
// 讀取使用者下的參數
const args = process.argv;
const apiUrl = 'https://lidemy-book-store.herokuapp.com';
const action = args[2];
const params = args[3];
// ES6 新語法 Template Literals:使用 ${ } 來加入變數或函式
// 使用 ${ apiUrl } 來插入變數
function listBooks() {
// eslint-disable-next-line consistent-return
request(`${apiUrl}/books?_limit=20`, (error, response, body) => {
if (error) {
// eslint-disable-next-line no-console
return console.log('讀取資料失敗', error);
}
const data = JSON.parse(body);
for (let i = 0; i < data.length; i += 1) {
// eslint-disable-next-line no-console
console.log(`${data[i].id} ${data[i].name}`);
}
});
}
function readBook(id) {
// eslint-disable-next-line consistent-return
request(`${apiUrl}/books/${id}`, (error, response, body) => {
if (error) {
// eslint-disable-next-line no-console
return console.log('讀取資料失敗', error);
}
const data = JSON.parse(body);
// eslint-disable-next-line no-console
console.log(data);
});
}
function deleteBook(id) {
// eslint-disable-next-line consistent-return
request.delete(`${apiUrl}/books/${id}`, (error) => {
if (error) {
// eslint-disable-next-line no-console
return console.log('刪除失敗', error);
}
// eslint-disable-next-line no-console
console.log('刪除成功!');
});
}
function createBook(name) {
request.post({
url: `${apiUrl}/books`,
form: {
name,
},
// eslint-disable-next-line consistent-return
}, (error) => {
if (error) {
// eslint-disable-next-line no-console
return console.log('新增失敗', error);
}
// eslint-disable-next-line no-console
console.log('新增成功!');
});
}
function updateBook(id, name) {
request.patch({
url: `${apiUrl}/books/${id}`,
form: {
name,
},
// eslint-disable-next-line consistent-return
}, (error) => {
if (error) {
// eslint-disable-next-line no-console
return console.log('更新失敗', error);
}
// eslint-disable-next-line no-console
console.log('更新成功!');
});
}
// switch 語法
// 變數的值是 list,則會執行 case 'list': listBooks();區塊的程式碼
switch (action) {
case 'list':
listBooks();
break;
case 'read':
readBook(params);
break;
case 'delete':
deleteBook(params);
break;
case 'create':
createBook(params);
break;
case 'update':
updateBook(params, args[4]);
break;
default:
// eslint-disable-next-line no-console
console.log('Available commands: list, read, delete, create and update');
}
```
###### `範例3 串接API,輸入國家的英文名字,就能夠查詢符合的國家的資訊`
>[參考資料](https://www.footmark.info/programming-language/php/ajax-javascript-jquery-example-php/) AJAX JavaScript 與 jQuery 教學範例 for PHP
>[參考資料](https://tigercosmos.xyz/post/2018/11/master_js/object/) 30 天 Javascript 從入門到進階:物件
>[參考資料](https://wcc723.github.io/javascript/2017/12/22/javascript-template-string/) JavaScript Template String 樣板字串
###### `學習看API文件`
>[文件連結](https://restcountries.eu/#api-endpoints-name)
![](https://i.imgur.com/RLmhG7Z.png)
![](https://i.imgur.com/H0RLlsz.png)
```javascript=
// 輸入國家的英文名字,就能夠查詢符合的國家的資訊
// 引入了request的library
const request = require('request');
// 讀取使用者下的參數
const args = process.argv;
const apiUrl = 'https://restcountries.eu/rest/v2';
const name = args[2];
// ES6 新語法 Template Literals:使用 ${ } 來加入變數或函式
// 使用 ${ apiUrl } 來插入變數
// eslint-disable-next-line
request(`${apiUrl}/name/${name}`, (error, response, body) => {
if (error) {
return console.log('讀取資料失敗', error);
}
// 用 JSON.parse處理JSON格式的字串
// eslint-disable-next-line
let data = JSON.parse(body);
// status 請求回應狀態,也就是 HTTP 狀態碼(status code)
// 4xx:客戶端錯誤 表示客戶端提交的請求中有錯誤或者不能被完成
if (data.status >= 400 && data.status < 500) {
return console.log('找不到國家資訊');
}
// node hw3.js korea
// args[2] 需要是國家名稱
if (!name) {
return console.log('請輸入國家名稱');
}
for (let i = 0; i < data.length; i += 1) {
console.log('========================');
// ES6 新語法 Template Literals:使用 ${ } 來加入變數或函式
// 使用 ${ data[i].id } 來插入變數
console.log(`國家: ${data[i].name}`);
console.log(`首都: ${data[i].capital}`);
console.log(`貨幣: ${data[i].currencies[0].code}`);
console.log(`國碼: ${data[i].callingCodes[0]}`);
}
});
```
###### `範例4 串接API,呼叫 Twitch API,並拿到「最受歡迎的遊戲列表(Get Top Games)」`
###### `依序印出目前觀看人數跟遊戲名稱`
>[參考資料](https://www.footmark.info/programming-language/php/ajax-javascript-jquery-example-php/) AJAX JavaScript 與 jQuery 教學範例 for PHP
>[參考資料](https://ithelp.ithome.com.tw/articles/10192541) 突發任務:Twitch API
>[參考資料](https://blog.huli.tw/2017/08/27/ajax-and-cors/) 輕鬆理解 Ajax 與跨來源請求
>[參考資料](https://pjchender.blogspot.com/2017/01/javascript-es6-for-of.html) JavaScript ES6 中的 for ... of(處理陣列的好幫手)
>[參考資料](https://tigercosmos.xyz/post/2018/11/master_js/object/) 30 天 Javascript 從入門到進階:物件
###### `學習看request文件`
>[文件連結](https://github.com/request/request#custom-http-headers)
![](https://i.imgur.com/KTdgjTp.png)
###### `學習看API文件`
>[文件連結](https://dev.twitch.tv/docs/v5)
>[文件連結](https://dev.twitch.tv/docs/v5/reference/games)
###### ==`在 request header 中帶上 ClientID 跟另一個參數 Accept`==
![](https://i.imgur.com/M5E8WiE.png)
###### ==`從API文件觀察 如何取得baseUrl`==
![](https://i.imgur.com/jomB4qU.png)
###### ==`從API文件觀察 如何取得目前觀看人數跟遊戲名稱的資料`==
![](https://i.imgur.com/uhopx1y.png)
```javascript=
// 呼叫 Twitch API,並拿到「最受歡迎的遊戲列表(Get Top Games)」
// 依序印出目前觀看人數跟遊戲名稱
// 引入了request的library
const request = require('request');
// Twitch developer dashboard 註冊一個新的 Application 會拿到一個 ClientID
const clientID = 'br15dzlr0ukquy97xhv00y3i9r9g4x';
const baseUrl = 'https://api.twitch.tv/kraken';
request({
method: 'GET',
//拿到「最受歡迎的遊戲列表(Get Top Games)」
url: `${baseUrl}/games/top`,
headers: {
'Client-ID': clientID,
'Accept': 'application/vnd.twitchtv.v5+json'
}
}, function(error,response,body){
if(error){
return console.log(error)
}
// 用 JSON.parse處理JSON格式的字串
const data = JSON.parse(body)
const games = data.top
//ES6 語法 for...of
//可以把陣列的值一個個取出
for(let game of games) {
//用 . 來取得物件的資料
console.log(game.viewers + ' ' + game.game.name)
}
})
```
---
### `Lidemy HTTP Challenge`
###### `第一關`
用 `GET` 方法跟圖書館管理員`lib` 說我的名字
```javascript=
const request = require('request');
request.get(
{ // 把名字傳給lib
url: 'https://lidemy-http-challenge.herokuapp.com/lv1?token={GOGOGO}&name=ruofan',
},
(error, response, body) => {
// eslint-disable-next-line no-console
console.log(body);
},
);
```
![](https://i.imgur.com/OFBborn.png)
###### `第二關`
幫`lib`找一本書
>[API文件](https://gist.github.com/aszx87410/3873b3d9cbb28cb6fcbb85bf493b63ba)
```javascript=
const request = require('request');
request.get(
{
// 從api文件取得書籍得資料
url: 'https://lidemy-http-challenge.herokuapp.com/api/books/56?token={HellOWOrld}&id=56',
// 再傳給lib
url: 'https://lidemy-http-challenge.herokuapp.com/lv2?token={HellOWOrld}&id=56',
},
(error, response, body) => {
// eslint-disable-next-line no-console
console.log(body);
},
);
```
![](https://i.imgur.com/PzuI4ag.png)
###### `第三關`
```javascript=
const request = require('request');
request.post(
{
url: 'https://lidemy-http-challenge.herokuapp.com/api/books?token={5566NO1}',
form: {
name: '大腦喜歡這樣學',
ISBN: '9789863594475',
},
},
(error, response, body) => {
// eslint-disable-next-line no-console
console.log(body);
},
);
request.get(
{ // 把名字傳給lib
url: 'https://lidemy-http-challenge.herokuapp.com/lv3?token={5566NO1}&id=1989',
},
(error, response, body) => {
// eslint-disable-next-line no-console
console.log(body);
},
);
```
![](https://i.imgur.com/H5fVL9e.png)
###### `第四關`
>[參考資料](https://ithelp.ithome.com.tw/articles/10201503) Postman - 測試 API 神器 1/2
>[參考資料](https://pjchender.github.io/2018/08/31/guide-%E4%BD%BF%E7%94%A8-js-%E8%A7%A3%E6%9E%90%E7%B6%B2%E5%9D%80%E8%88%87%E8%99%95%E7%90%86%E7%B6%B2%E5%9D%80%E4%B8%AD%E7%9A%84%E5%8F%83%E6%95%B8%EF%BC%88url-parameters%EF%BC%89/) 使用 JS 解析網址與處理網址中的參數(URL Parameters)
>[API文件](https://gist.github.com/aszx87410/3873b3d9cbb28cb6fcbb85bf493b63ba)
###### `解法`
###### 使用 `encodeURI()` ==轉換網址==
`Base URL`: `https://lidemy-http-challenge.herokuapp.com/api`
```javascript=
const request = require('request');
let str = '世界';
let uri = encodeURI(`https://lidemy-http-challenge.herokuapp.com/api/books?q=${str}`);
request.get(uri, function (error, response, body) {
console.log(JSON.parse(body));
});
```
![](https://i.imgur.com/4dSfO5r.png)
###### `postman`
使用`postman`找到書名含有『世界』的書,`id:79`即是我們要的那一本!
![](https://i.imgur.com/uNRmRhh.png)
``` js
[{"id":2,"name":"當我想你時,全世界都救不了我","author":"肆一","ISBN":"5549173495"},{"id":27,"name":"從你的全世界路過","author":"張嘉佳","ISBN":"8426216529"},{"id":79,"name":"世界末日與冷酷異境","author":"村上春樹","ISBN":"9571313408"},{"id":90,"name":"文學的40堂公開課:從神話到當代暢銷書,文學如何影響我們、帶領我們理解這個世界","author":"約翰.薩德蘭","ISBN":"7978376866"}]
```
```javascript=
const request = require('request');
request.get(
{
// 從api文件取得書籍得資料
url: 'https://lidemy-http-challenge.herokuapp.com/api/books/79?token={LEarnHOWtoLeArn}&id=79',
// 再傳給lib
url: 'https://lidemy-http-challenge.herokuapp.com/lv4?token={LEarnHOWtoLeArn}&id=79',
},
(error, response, body) => {
// eslint-disable-next-line no-console
console.log(body);
},
);
```
![](https://i.imgur.com/N7mv33k.png)
###### `第五關`
幫忙刪除`id:23`的書
```javascript=
const request = require('request');
request.delete(
{
// 從api文件取得書籍得資料
url: 'https://lidemy-http-challenge.herokuapp.com/api/books/23?token={HarukiMurakami}&id=23',
},
(error, response, body) => {
// eslint-disable-next-line no-console
console.log(body);
},
);
```
![](https://i.imgur.com/uVKCCmB.png)
###### `第六關`
>[API文件](https://gist.github.com/aszx87410/1e5e5105c1c35197f55c485a88b0328a)
>[BASE64編碼器](https://www.ez2o.com/App/Coder/Base64)
>[參考資料](https://ithelp.ithome.com.tw/articles/10185699?sc=pt) 小白合送的土耳其絨毯- Http Basic Authentication
>[參考資料](https://matthung0807.blogspot.com/2019/11/java-string-base64-encode-decode.html) Java String Base64 編碼(Encode)及解碼(Decode)
>[參考資料](https://carsonwah.github.io/http-authentication.html) HTTP認證(HTTP Authentication)
###### `Node.js 可使用` ==Buffer.from() 進行 base64 編碼==
>`關鍵字 http basic authorization`
```javascript=
const request = require('request');
let account = 'admin';
let pwd = 'admin123';
let base64Str = Buffer.from(`${account}:${pwd}`).toString('base64');
//console.log(base64Str);
request.get(
{
url: 'https://lidemy-http-challenge.herokuapp.com/api/v2/me',
url: 'https://lidemy-http-challenge.herokuapp.com/lv6?token={CHICKENCUTLET}&email=lib@lidemy.com',
headers: {
'Authorization': `Basic ${base64Str}`
}
},
(error, response, body) => {
// eslint-disable-next-line no-console
console.log(body);
},
);
```
![](https://i.imgur.com/GDbncG8.png)
###### ==`HTTP Basic`==
###### `編碼前`
``` js
Authorization: Basic base64(admin:admin123)
```
> [color=#ff9a76]使用 [BASE64編碼器](https://www.ez2o.com/App/Coder/Base64) 轉換
###### `編碼後`
``` js
Authorization: Basic YWRtaW46YWRtaW4xMjM=
```
###### ` postman`
###### `驗證方法為傳入 Authorization 的 Header`
>`base API`: `https://lidemy-http-challenge.herokuapp.com/api/v2/me`
![](https://i.imgur.com/M1IJ5PT.png)
![](https://i.imgur.com/HvFFXfx.png)
###### `將 email 放在 queryString`
```javascript=
const request = require('request');
request.get(
{
url: 'https://lidemy-http-challenge.herokuapp.com/lv6?token={CHICKENCUTLET}&email=lib@lidemy.com',
},
(error, response, body) => {
// eslint-disable-next-line no-console
console.log(body);
},
);
```
![](https://i.imgur.com/GDbncG8.png)
###### `第七關`
###### 刪除一本書 `id:89`
```javascript=
const request = require('request');
let account = 'admin';
let pwd = 'admin123';
let base64Str = Buffer.from(`${account}:${pwd}`).toString('base64');
//console.log(base64Str);
request.delete(
{
url: 'https://lidemy-http-challenge.herokuapp.com/api/v2/books/89',
headers: {
'Authorization': `Basic ${base64Str}`
}
},
(error, response, body) => {
// eslint-disable-next-line no-console
console.log(body);
},
);
```
![](https://i.imgur.com/OUcAmNs.png)
###### `postman`
###### `一樣是 DELETE 方法,不同的是,這次要加上剛剛的 Authorization`
![](https://i.imgur.com/euX9QOT.png)
###### `第八關`
查詢書名含有『我』的書籍
###### 使用 `encodeURI()` ==轉換網址==
```javascript=
const request = require('request');
let account = 'admin';
let pwd = 'admin123';
let base64Str = Buffer.from(`${account}:${pwd}`).toString('base64');
let queryStr = '我';
let uri = encodeURI(`https://lidemy-http-challenge.herokuapp.com/api/v2/books?q=${queryStr}`);
request.get(
{
uri,
headers: {
'Authorization': `Basic ${base64Str}`
}
}, (error, response, body) =>{
console.log(JSON.parse(body));
},
);
```
![](https://i.imgur.com/qTIu1f1.png)
```javascript=
const request = require('request');
let account = 'admin';
let pwd = 'admin123';
let base64Str = Buffer.from(`${account}:${pwd}`).toString('base64');
request.patch(
{
url: 'https://lidemy-http-challenge.herokuapp.com/api/v2/books/72',
contentType: 'application/x-www-form-urlencoded',
headers: {
'Authorization': `Basic ${base64Str}`
},
form: {
name: '日日好日:茶道教我的幸福15味【電影書腰版】',
ISBN: '9981835423'
}
}, (error, response, body) =>{
console.log(JSON.parse(body));
},
);
```
![](https://i.imgur.com/krS9LeJ.png)
###### `postman`
![](https://i.imgur.com/CKce6rA.png)
``` js
[{"id":2,"name":"當我想你時,全世界都救不了我","author":"肆一","ISBN":"5549173495"},{"id":3,"name":"我殺的人與殺我的人","author":"東山彰良","ISBN":"9262228645"},{"id":7,"name":"你已走遠,我還在練習道別","author":"渺渺","ISBN":"3722233689"},{"id":9,"name":"你是我最熟悉的陌生人","author":"Middle","ISBN":"9765734253"},{"id":22,"name":"我輩中人:寫給中年人的情書","author":"張曼娟","ISBN":"7241428897"},{"id":38,"name":"我和我追逐的垃圾車","author":"謝子凡","ISBN":"7797349452"},{"id":57,"name":"我的櫻花戀人","author":"宇山佳佑","ISBN":"2947749939"},{"id":60,"name":"你走慢了我的時間","author":"張西","ISBN":"8811544334"},{"id":66,"name":"我是許涼涼","author":"李維菁","ISBN":"8389193464"},{"id":72,"name":"日日好日:茶道教我的幸福15味【電影書腰版】","author":"森下典子","ISBN":"9981835427"},{"id":90,"name":"文學的40堂公開課:從神話到當代暢銷書,文學如何影響我們、帶領我們理解這個世界","author":"約翰.薩德蘭","ISBN":"7978376866"},{"id":95,"name":"我想吃掉你的胰臟【電影珍藏版】","author":"住野夜","ISBN":"2615985356"},{"id":100,"name":"慢情書:我們會在更好的地方相遇嗎?","author":"林達陽","ISBN":"7418527246"}]
```
###### `作者的名字是四個字,key 錯的 ISBN 最後一碼為 7`
`id:79` 這本書符合我們要找的資訊
``` js
{"id":72,"name":"日日好日:茶道教我的幸福15味【電影書腰版】","author":"森下典子","ISBN":"9981835427"}
```
###### `PATCH方法修改書籍的 ISBN 資訊 (一樣須帶驗證)`
![](https://i.imgur.com/oI1tbPx.png)
###### `第九關`
>[API文件](https://gist.github.com/aszx87410/1e5e5105c1c35197f55c485a88b0328a)
![](https://i.imgur.com/uxRt3c8.png)
想要存取的話要符合兩個條件:
1. 帶上一個 ==`X-Library-Number` 的 `header`,我們圖書館的編號是 20==
2. 伺服器會用 `user agent` 檢查==是否是從 `IE6 `送出的 `Request`==,不是的話會擋掉
###### `偽造IE6的 request header`
>[參考資料](http://www.rhdgroup.com.tw/asp/WebStat/FMozilla.asp) 訪問者HTTP_USER_AGENT字符串分析
>[參考資料](https://progressbar.tw/posts/234) 淺談網路訪問的識別證UserAgent
```javascript=
const request = require('request');
let account = 'admin';
let pwd = 'admin123';
let base64Str = Buffer.from(`${account}:${pwd}`).toString('base64');
request.get(
{
url: 'https://lidemy-http-challenge.herokuapp.com/api/v2/sys_info',
headers: {
'Authorization': `Basic ${base64Str}`,
'X-Library-Number': 20,
'User-Agent': 'Mozilla/5.0 (Windows; U; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 2.0.50727)',
}}, (error, response, body) =>{
console.log(JSON.parse(body));
},
);
```
![](https://i.imgur.com/SGG8tlf.png)
```javascript=
const request = require('request');
let account = 'admin';
let pwd = 'admin123';
let base64Str = Buffer.from(`${account}:${pwd}`).toString('base64');
request.get(
{
url: 'https://lidemy-http-challenge.herokuapp.com/api/v2/sys_info',
url:'https://lidemy-http-challenge.herokuapp.com/lv9?token={NeuN}&version=1A4938Jl7',
headers: {
'Authorization': `Basic ${base64Str}`,
'X-Library-Number': 20,
'User-Agent': 'Mozilla/5.0 (Windows; U; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 2.0.50727)',
}}, (error, response, body) =>{
console.log(body);
},
);
```
![](https://i.imgur.com/EXMOy3P.png)
###### `postman`
![](https://i.imgur.com/YExpcvP.png)
###### `query string 回傳剛剛得到的 version資訊`
![](https://i.imgur.com/J4007jL.png)
###### `第十關`
出題者會==出一個四位數不重複的數字==,例如說 `9487`。
如果猜 `9876`,`lib`會說 `1A2B`,`1A` 代表 `9` 位置對數字也對,`2B` 代表 `8` 跟 `7` 你猜對了但位置錯了。
把要猜的數字放在 `query string` 用 `num` 當作 `key` 傳給 `lib`
```javascript=
const request = require('request');
let account = 'admin';
let pwd = 'admin123';
let base64Str = Buffer.from(`${account}:${pwd}`).toString('base64');
request.get(
{
url:'https://lidemy-http-challenge.herokuapp.com/lv10?token={duZDsG3tvoA}&num=9613',
headers: {
'Authorization': `Basic ${base64Str}`
}
}, (error, response, body) =>{
console.log(body);
},
);
```
![](https://i.imgur.com/9uohEP7.png)
###### `postman`
![](https://i.imgur.com/VPFVb3K.png)
###### `第十一關`
跟菲律賓的圖書館館員打招呼
>[API文件](https://gist.github.com/aszx87410/0b0d3cabf32c4e44084fadf5180d0cf4)
`Base URL`: `https://lidemy-http-challenge.herokuapp.com/api/v3`
>hint: 伺服器會檢查 origin 這個 header,只要騙過伺服器就行了
>request header 內==偽造 origin==
> [color=#ff9a76][參考資料](https://yakimhsu.com/project/project_w8_Ajax_Form.html) 用 ==node.js 呼叫 API== 與在==網頁上呼叫==的根本差異是什麼?
``` js
Request 從瀏覽器發出來:有許多限制
自己發 Request:沒有限制
```
>[參考資料](https://developer.mozilla.org/zh-TW/docs/Web/HTTP/CORS) mozilla
```javascript=
const request = require('request');
request.get(
{
url:'https://lidemy-http-challenge.herokuapp.com/api/v3/hello',
headers: {
'origin': 'lidemy.com',
}
}, (error, response, body) =>{
console.log(body);
},
);
```
![](https://i.imgur.com/wyD7LKR.png)
###### `postman`
![](https://i.imgur.com/c3d2Q5H.png)
###### `第十二關`
>server side redirect 的原理
>轉址背後的原理
>[參考資料](https://seo.whoops.com.tw/301-302-redirect-seo/) 3分鐘了解 301 與 302 Redirect 重定向之間的差異與它們如何影響網站 SEO 排名
使用 `chrome 開發者工具`,在 `Network 分頁`中觀察 `request` ,==`網址列中輸入以下網址,訪問 API`==
``` js
https://lidemy-http-challenge.herokuapp.com/api/v3/deliver_token
```
被轉址到其他地方了
##### `deliver_token > stopover > deliver_token_result`
==`透過 Network 分頁觀察 stopover`== ,發現 `header` 內夾帶 `X-Lv13-Token: {qspyz}`
![](https://i.imgur.com/8vS8JBT.png)
###### `第十三關`
>[hint](https://lidemy-http-challenge.herokuapp.com/lv13?token={qspyz}&hint=13) 找關鍵字 代理伺服器 proxy
>[菲律賓 proxy](http://free-proxy.cz/zh/proxylist/country/PH/http/ping/all)
![](https://i.imgur.com/lpwZyBA.png)
![](https://i.imgur.com/SPwTMF9.png)
###### `第十四關`
>[hint](https://lidemy-http-challenge.herokuapp.com/lv14?token={SEOisHard}&hint=1)
>[參考資料](https://ithelp.ithome.com.tw/articles/10209356) User-Agent 說明與使用
>[API文件](https://gist.github.com/aszx87410/0b0d3cabf32c4e44084fadf5180d0cf4)
>各個搜尋引擎的[User-Agent](http://www.lrxin.com/archives-325.html)
>`User-Agent` 可以設定要使用哪種搜尋引擎
```javascript=
const request = require('request');
request.get(
{ // 獲取首頁內容
url:'https://lidemy-http-challenge.herokuapp.com/api/v3/index',
headers: {
'origin': 'lidemy.com',
'User-Agent': 'Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)',
}
}, (error, response, body) =>{
console.log(body);
},
);
```
![](https://i.imgur.com/NkF7WZA.png)
![](https://i.imgur.com/CFT6MV5.png)
---
### 資料格式 `XML` & `JSON`
### `XML`
>以前傳資料的格式,現代開發比較少用
* 全名為 Extensible Markup Language
* 跟 HTML 有點像、都是標記語言 Markup Language,特色是「 ==內容用前後標籤包起來== 」,因為此格式檔案較大、且不好閱讀,所以現代開發比較少人用 XML。
### `JSON`
>比起 XML 更輕量,為現代最普遍、常用的==資料格式==。
* 全名為 JavaScript Object Notation
* ==跟 JavaScript 的物件很像==,==但 key 值必須要用雙引號 "key" 包起來==
### `RESTful API`
現代 API 有一大部分都是走 ==RESTful 設計風格==。
>[參考資料](https://medium.com/%E5%8F%B2%E5%8A%AA%E6%AF%94%E7%9A%84%E7%AC%AC%E4%B8%80%E5%80%8B%E5%AE%B6/restful%E7%9A%84%E4%B8%80%E9%BB%9E%E5%BF%83%E5%BE%97%E5%B0%8F%E8%A8%98-944d0b981e82) RESTful的一點心得小記
>是一種廣泛流行的 API 設計規範、不是規定
### 常見 Restful CRUD 指令:
>[參考資料](https://blog.techbridge.cc/2019/02/01/linux-curl-command-tutorial/) Linux Curl Command 指令與基本操作入門教學
#### 取得資料: GET
``` js
curl -X GET "http://www.example.com/api/resources"
```
#### 新增 JSON 資料: POST
``` js
curl -X POST -H "Content-Type: application/json" -d '{"status" : false, "name" : "Jack"}' "http://www.example.com/api/resources"
```
#### 刪除資料: DELETE
``` js
curl -X DELETE "http://www.example.com/api/resources/1
```
### 必學指令 curl
>[參考資料](https://blog.techbridge.cc/2019/02/01/linux-curl-command-tutorial/) Linux Curl Command 指令與基本操作入門教學
>[參考資料](hhttps://noob.tw/postman/) API 實作(三):以 Postman 測試 API
透過 HTTP Protocol==下載和上傳檔案的指令==
``` js
curl [options] [URL...]
```
#### 預設是 GET 方法
用 curl 發 request 到 google 首頁
curl 後面加網址,就會在終端機內顯示回傳的 response,可能是 HTML、JSON 或是 XML 等格式,根據輸入的 URL 內容而定。
* 最基本的作法就是: `curl http://www.google.com`
#### 測試一個基本的` RESTful API`
``` js
curl -X [method] [url]
curl -X GET "http://localhost:3000/article/1"
```