前言
Link to Header
目標:熟悉網路基礎知識及練習使用 Node.js 串接基本的 API
具備知識: Node.js 串接基本的 API、網路基礎知識
以下為題目解說與所學內容,並於最後有附上解題過程。
Let's Start to play this game!
Lv 0 說明
- 練習:網址搭配正確的 token
Lv 1 你叫什麼名字?
Lv 1: https://lidemy-http-challenge.herokuapp.com/lv1?token={GOGOGO}
- 練習: review 第 0 關的網址 token 結合
- API 文件:https://gist.github.com/aszx87410/3873b3d9cbb28cb6fcbb85bf493b63ba
Lv 2 可以幫我找出某一本書嗎?
Lv 2: https://lidemy-http-challenge.herokuapp.com/lv2?token={HellOWOrld}
- 看懂 API 文件敘述:新增/刪除/查詢/更改書籍需要用哪一個 Method,然後 path 是什麼。
- 結合 API 及網址使用
Lv 3 可以幫我優先上架這本新書《大腦喜歡這樣學》嗎?
Lv3: https://lidemy-http-challenge.herokuapp.com/lv3?token={5566NO1}
- API 文件上說:新增書籍,需要使用
POST
method。 - 然後發現 HTTP 在 POST 所使用的規則:
content type 為:application/x-www-form-urlencoded
。
在此使用 request 的 npm 套件,因此需要使用 POST
的話,需要符合格式傳送,這個格式就是上述的 content-type
,在 HTTP 內容類型 (Content-Type) 提到「普遍用於 HTML 中的 POST 表單 (e.g., 提交帳號密碼),是『類似百分比編碼的鍵值對』形式,例如:『name=%E5%8B%9D&password=9487』」
利用 HTTP GET vs POST 的解說,對 request 中 GET method
做進一步瞭解,GET
有查詢/檢索/獲取的意思,如果需要使用GET
來找到書籍的話,需要額外在表單(form)輸入參數(parameter)中,會做 URL 編碼 (URL-Encoding)在 URL 透過查詢(query)會以鍵值對(key value)的形式顯示,例如:查詢幾歲的女生,key 為年紀,因此將 key 稱 age,value 為歲數,所以將 value 設為 24,加上條件為女生,再透過 get
查詢生成的 URL:https://echo.paw.cloud:443/hello/world?age=24&gender=female。
其中,urlencoded
就是產生編碼的意思,如果 form 內的資料呈現以下格式:
form:{
name: 'littlePrince',
ISBN: '324232&123=3'
}
會在 URL加上資訊為 name=littlePrince&ISBN=324232&123=3
,那這樣 server 會搞不清楚 form 內的 ISBN 為324232
還是 324232&123=3
,因此需要透過編碼形式的轉換,避免混淆。在 URL 編碼中,會把字符分成兩種:保留字符(具特殊字元)及未保留字符,在 URL 編碼中會將特殊字符做轉換,如此一來,ISBN: 324232&123=3
就會轉換成324232%26123%3d3
,這樣透過編碼後,就可以正確帶入資訊。
Lv 4 抱歉,我找錯書了,可以告訴我書名有「世界」、作者為村上春樹的ID嗎?
Lv 4: https://lidemy-http-challenge.herokuapp.com/lv4?token={LEarnHOWtoLeArn}
- 自己解的時候,只想到使用
process.argv
再執行 node.js 時輸入參數來印出書籍。 - 參考別人的心得後,突然想起這個小遊戲應該是循序漸進的關卡,可以利用上一關的「產生編碼」的概念來解。
Lv 5 有人捐錯書,要把書拿回去,所以要將這本書從系統中刪除
Lv 5: https://lidemy-http-challenge.herokuapp.com/lv5?token={HarukiMurakami}
- 練習 request.delete method,和 lv 4
POST
method 在content-type
上做區別。 - 承 lv 4,也可以使用「產生編碼」來解。
Lv 6 圖書系統應該需要登入才能修改,怎麼沒有登入就能新增跟刪除呢?回報給工程師後,得到一個新的 API 文件,然後就來嘗試看看怎麼登入並修改吧!
新文件:https://gist.github.com/aszx87410/1e5e5105c1c35197f55c485a88b0328a
Lv 6: https://lidemy-http-challenge.herokuapp.com/lv6?token={CHICKENCUTLET}
- 認識「HTTP 基本認證」(英語:Basic access authentication),允許 http 使用者在瀏覽器發出請求(request)觀看一個需要身分認證的頁面時,需要使用者提供使用者名稱(username)及使用者密碼(password),通過認證,才能打開頁面。其優點為:簡單便捷,流行的網頁瀏覽器都支援 HTTP 基本認證;缺點為:沒有任何加密或演算,僅有 Base64 編碼跟傳輸,容易被破解,另一個是在不關閉瀏覽器的狀況下,有效讓使用者登出。
- 學習在 Node.js中使用「HTTP 基本認證」,並學習如何使用 Base64 編碼,此外 API 文件上也有教學說明。
Lv 7 系統修復完成囉!記得使用新的系統,有一本書老是被偷走,所以要把 id=89 的書從系統中刪除。
Lv 7: https://lidemy-http-challenge.herokuapp.com/lv7?token=%7BSECurityIsImPORTant%7D
- 照著 API 文件內的指示做就行囉!
Lv 8 發現在輸入資料時,把有一本書名有個「我」,作者名 4 個字的書,key 錯的 ISBN 最後一碼為 7,只要把最後一碼改成 3 就行了!
Lv 8: https://lidemy-http-challenge.herokuapp.com/lv8?token={HsifnAerok}
- 利用查詢書籍的方式,找出題目要求的書籍 ID。
- 利用更新書籍資訊(PATCH)的方式,更新書籍的 ISBN。
Lv 9 工程師說從一般方法活得的網址不太對,需要兩個資料才能獲得正確的網址!
Lv9: http://lidemy-http-challenge.herokuapp.com/lv9?token={NeuN}
條件
- 加上 X-Library-Number 的 header,然後這個圖書館的編號為 20。
- 伺服器會用 user agent 檢查是否是從 IE6 送出的 Request,不是的話,會被擋住。
- 條件一已給了 key and value,只要放進 header 即可。
- 條件二需要檢查數不是從 IE6 送出的 reqeust。
Lv 10 工程師說從一般方法活得的網址不太對,需要兩個資料才能獲得正確的網址!
Lv10: http://lidemy-http-challenge.herokuapp.com/lv10?token={duZDsG3tvoA}
- 根據提示,猜數字。
通關密碼:
Lv 1: https://lidemy-http-challenge.herokuapp.com/lv1?token={GOGOGO}&name=jean
Tip: 填上名字就行了!
Lv 2: https://lidemy-http-challenge.herokuapp.com/lv2?token={HellOWOrld}&id=56
Tip: id 從 54 ~ 58 一個一個輸入
Lv 3: https://lidemy-http-challenge.herokuapp.com/lv3?token={5566NO1}&id=1989
Tip: 見 API 文件上,新增書籍需要使用到 POST method,在 headers 內,除了寫提交表單(form),還要記得內容格式(content-type)為application/x-www-form-urlencoded
(default),其他格式參見 npm request
,表單格式,依照 API 文件說明,參數有:「name: 書名, ISBN: 書籍編號」,最後再使用 console.log(body)
,會印出{"message":"新增成功","id":"1989"}
。
Lv 4: https://lidemy-http-challenge.herokuapp.com/lv4?token={LEarnHOWtoLeArn}&id=79
法 1: 自己想到的解法:尋找書籍含有「世界」二字,可以使用 process.argv
的參數,在執行 node xx.js 時,加入所需參數「世界」,並印出所有包含「世界」的書名,尋找作者「村上春樹」。
const request = require('request')
const process = require('process')
if ( process.argv[2] === 'read') {
request.get(
{
url: 'https://lidemy-http-challenge.herokuapp.com/api/books/',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'},
},
(error, response, body) => {
const json = JSON.parse(body)
for (let i=0; i< json.length ; i++) {
if (json[i].name.indexOf(process.argv[3]) !== -1) {
console.log(json[i])
}
}
}
)
}
$ node lv4.js read 世界
{ 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'
}
法 2: 參考他人的解法:利用 ‵encodeURI()‵ 將「世界」編碼,帶入 API 網址搜尋後印出結果。
Lv 5:
const request = require('request')
const process = require('process')
if (process.argv[2] === 'delete') {
let idNum = process.argv[3];
request.delete(
'https://lidemy-http-challenge.herokuapp.com/api/books/'+idNum,
(error, response, body) => {
if (!error && response.statusCode >= 200) {
console.log('body',body)
console.log(`statusCode: ${response.statusCode}`)
}
}
)
}
$ node lv5.js delete 23
{"message":"\n咦...是刪掉了沒錯,但總覺得哪裡怪怪的,算了,先這樣吧!下一關的 token 為 {CHICKENCUTLET}\n"}
statusCode: 200
lv 6: https://lidemy-http-challenge.herokuapp.com/lv6?token=%7BCHICKENCUTLET%7D&email=lib@lidemy.com
從 API 文件中,得知 username: password 的內容格式,拿去 Base64 編碼,得到Authorization: xxxx
後,放進 headers
裡面。
const request = require('request')
//https://lidemy-http-challenge.herokuapp.com/api/v2/sys_info
request.get(
{
url: 'https://lidemy-http-challenge.herokuapp.com/api/v2/me',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Authorization': 'Basic YWRtaW46YWRtaW4xMjM='
}
},
(error, response, body) => {
if (!error && response.statusCode >= 200) {
console.log('body:',body)
console.log(`statusCode: ${response.statusCode}`)
}
}
)
$ node lv6.js
body: {"username":"admin","email":"lib@lidemy.com"}
statusCode: 200
lv 7:
依照 API 文件刪除書籍的 request method: delete,再用 console.log 印出 body 及 statusCode 確認結果。
const request = require('request')
if (process.argv[2] === 'delete') {
let idNum = process.argv[3];
request.delete(
{
url: 'https://lidemy-http-challenge.herokuapp.com/api/v2/books/'+idNum,
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Authorization': 'Basic YWRtaW46YWRtaW4xMjM='
}
},
(error, response, body) => {
if (!error && response.statusCode >= 200) {
const json = JSON.parse(body)
console.log('body:',json.message)
console.log(`statusCode: ${response.statusCode}`)
}
}
)
}
$ node lv7.js delete 89
body:
希望下一次進這本書的時候不會再被偷走了。下一關的 token 為 {HsifnAerok}
statusCode: 200
Lv 8:
- 先使用
node xx..js read 我
尋找書本,但是將題目所要求的條件一:作者名稱 4 個字,及條件二: ISBN 末碼為 7 的書本利用 if 條件式挑出來,印出書本的 id,再使用node xx.js update <idNumber> <BookNewISBN>
將書本更新資訊。
const request = require('request')
const process = require('process')
if (process.argv[2] === 'update') {
let idNum = process.argv[3];
let newISBN = process.argv[4];
request.patch(
{
url: 'https://lidemy-http-challenge.herokuapp.com/api/v2/books/'+idNum,
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Authorization': 'Basic YWRtaW46YWRtaW4xMjM='
},
form: {
ISBN: newISBN
}
},
(error, response, body) => {
if (!error && response.statusCode >= 200) {
const json = JSON.parse(body)
console.log('body message:',json.message)
console.log(`statusCode: ${response.statusCode}`)
}
}
)
} else if (process.argv[2] === 'read') {
let keyword = process.argv[3]
request.get(
{
url: 'https://lidemy-http-challenge.herokuapp.com/api/v2/books?q=',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Authorization': 'Basic YWRtaW46YWRtaW4xMjM='
},
},
(error, response, body) => {
if (!error && response.statusCode >= 200) {
const json = JSON.parse(body)
for (let i=0; i< json.length; i++) {
let bookname=json[i].name;
let thisISBN = json[i].ISBN
let thisAuthor = json[i].author
if ( bookname.indexOf(process.argv[3]) !== -1) {
if (thisAuthor.length === 4) {
if (thisISBN[thisISBN.length-1] === '7') {
//console.log('thisISBN.length:',thisISBN.length-1, thisISBN[thisISBN.length-1])
console.log(json[i].id, json[i].name, json[i].ISBN)
}
}
}
}
}
console.log(`statusCode: ${response.statusCode}`)
}
)
}
$ node lv8.js read 我
72 日日好日:茶道教我的幸福15味【電影書腰版】 9981835427
statusCode: 200
$ node lv8.js update 72 9981835423
body message:
希望之後他們能引進語音輸入系統,我就只要講講話就好。下一關的 token 為 {NeuN}
statusCode: 200
Lv9: http://lidemy-http-challenge.herokuapp.com/lv9?token=%7BNeuN%7D&version=1A4938Jl7
什麼是 User Agent 呢?中文為「使用者代理」,當 server 端一旦收到 client 端的身份識別後,就會做出相對的回應,例如:client 端是從電腦端還是手機端,這樣就 server 端就可以 response 出手機形式的網頁或者是電腦形式的網頁,提升 client 的使用體驗。
那 IE6 的 user Agent 是什麼呢?搜尋了 MDN 等等,好像都是其他版本的瀏覽器,例如 Chrome、IE9等等,然後複製上 user Agent,得到的是 Invalid
。所以又搜尋了一番!終於在 stackFlow 上找到 IE6 的 user Agent。
const request = require('request')
const process = require('process')
request.get(
{
url: 'https://lidemy-http-challenge.herokuapp.com/api/v2/sys_info',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Authorization': 'Basic YWRtaW46YWRtaW4xMjM=',
'X-Library-Number': '20',
'User-Agent': 'MMozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)'
}
},
(error, response, body) => {
if (!error && response.statusCode >= 200) {
const json = JSON.parse(body)
console.log(body)
console.log(`statusCode: ${response.statusCode}`)
}
}
)
$ node lv9_1.js
body {"message":"success","version":"1A4938Jl7","owner":"lib","createdAt":"121290329301"}
statusCode: 200
Lv 10: http://lidemy-http-challenge.herokuapp.com/lv10?token=%7BduZDsG3tvoA%7D&num=9613
參考:
參考別種解法:Lidemy HTTP Challenge 破關紀錄
什麼是 URLEncode:[轉] 混亂的 URLEncode
URL 編碼意思:UrlEncodeDecode
什麼是 HTTP 基本認證:HTTP基本認證 Wikipedia
什麼是 userAgent:要怎麼辨別使用者瀏覽者用哪種瀏覽器呢?用UserAgent是最簡單的方式喔!
IE6 的 userAgent:Why is IE9 sending a user agent string of IE6?