# Lidemy HTTP Challenge 過關方式分享 | pcchen
這個小遊戲有好幾種過關的方式,可以在 terminal 用 curl 指令,或者是載 Postman 來用,這兩種方法都很方便、迅速,但因為在 Huli 的 Lidemy 課程中是透過 JavaScript 程式碼,用基礎的方式來理解 API 的串接方式,因此這裡我嘗試用這個方法來撰寫各關的過關攻略,且語法和課程相似,作為上課內容的複習。
## 各關卡攻略
### 第 0 關
- URL: https://lidemy-http-challenge.herokuapp.com/lv1?token={GOGOGO}
- Method: GET
用JS程式碼前往關卡的方式如下,往後每關只要改 `params` 的 lv? 和 token,因此後面的關卡就不再贅述。
``` javascript=
const request = require('request')
const params = '/lv1?token={GOGOGO}'
request('https://lidemy-http-challenge.herokuapp.com' + params,
(error, response, body) => {
if(error) return console.log('err', error)
console.log(body)
})
```
### 第 1 關
這一關有兩個重點:
1. 圖書館資訊系統的 API 文件:https://gist.github.com/aszx87410/3873b3d9cbb28cb6fcbb85bf493b63ba
2. 得到 token 的方式為用 GET 回傳 name
但要過這一關只要先解決第 2 點就好,API 文件在後面關卡才會用到。因此只要先在上一關的 URL 後面新增 `&name=<your name>`,`<your name>` 的地方隨便輸入即可,例如我叫 Doraemon:
#### JS 程式碼
``` javascript=
const request = require('request')
const params = '/lv1?token={GOGOGO}&name=Doraemon'
request('https://lidemy-http-challenge.herokuapp.com' + params,
(error, response, body) => {
if(error) return console.log('err', error)
console.log(body)
// 輸出結果:
// 啊...原來你叫 Doraemon 啊!下一關的 token 是 {HellOWOrld}
})
```
接下來就用這個 token 繼續前往下一關
### 第 2 關
這一關只要直接一個一個套入參數 id 從 54 到 58 ,看哪個可以成功回傳 token。
- URL: https://lidemy-http-challenge.herokuapp.com/lv2?token={HellOWOrld}&id=56
- Method: GET
#### JS 程式碼
``` javascript=
const request = require('request')
const params = '/lv2?token={HellOWOrld}&id=56'
request('https://lidemy-http-challenge.herokuapp.com' + params,
(error, response, body) => {
if(error) return console.log('err', error)
console.log(body)
// 輸出結果:
// 啊!就是這本書,太謝謝你了。下一關的 token 為:{5566NO1}
})
```
### 第 3 關
這關要新增書籍到圖書資訊系統,因此 URL 要使用圖書系統的網址、Method 是 `POST`,並且要帶有 name 和 ISBN 這兩個資訊。
- URL: https://lidemy-http-challenge.herokuapp.com/api/books
- Method: POST
#### JS 程式碼
```javascript=
const request = require('request')
const path = '/books'
request.post({
url: 'https://lidemy-http-challenge.herokuapp.com/api' + path,
form: {
name: '大腦喜歡這樣學',
ISBN: '9789863594475'
}
},
(error, response, body) => {
if(error) return console.log('err', error)
console.log(body)
// 輸出結果:
// {"message":"新增成功","id":"1989"}
}
)
```
> 因為這裡只要看結果,就沒有再對 body 作 JSON.parse 的處理了。
把 `id=1989` 參數加到這一關的網址,並用 GET 送出 request 就能得到下一關的 token:
``` javascript=
const request = require('request')
const params = '/lv3?token={5566NO1}&id=1989'
request('https://lidemy-http-challenge.herokuapp.com' + params,
(error, response, body) => {
if(error) return console.log('err', error)
console.log(body)
// 輸出結果:
// 這樣子讀者就能趕快看到這本新書了,謝謝!下一關的 token 為 {LEarnHOWtoLeArn}
})
```
### 第 4 關
這一關要**找出所有的書**中,**書名含「世界」兩個字**,且**作者是「村上村樹」** 的書,用程式的邏輯思考就是
**Step 1.** 用 GET 列出所有書籍
**Step 2.** 用 `name.indexOf('世界') >= 0` 判斷書名是否含這兩個字
**Step 3.** 如果也符合 `author === '村上村樹'`,就印出這本書的資訊
- URL: https://lidemy-http-challenge.herokuapp.com/api/books
- Method: GET
#### JS 程式碼
```javascript=
const request = require('request')
const path = '/books'
request('https://lidemy-http-challenge.herokuapp.com/api' + path,
(error, response, body) => {
if (error) return console.log('err', error)
let json
try {
json = JSON.parse(body)
} catch (err) {
console.log(err)
}
for (let i = 0; i < json.length; i++) {
if (json[i].name.indexOf('世界') >= 0 && json[i].author === '村上春樹') {
console.log(json[i])
}
}
// 輸出結果:
// { id: 79, name: '世界末日與冷酷異境', author: '村上春樹', ISBN: '9571313408' }
})
```
再用 GET 回傳 `id=79` 就能得到下一關的 token。
```javascript=
const request = require('request')
const params = '/lv4?token={LEarnHOWtoLeArn}&id=79'
request('https://lidemy-http-challenge.herokuapp.com' + params,
(error, response, body) => {
if(error) return console.log('err', error)
console.log(body)
// 輸出結果:
// 沒錯!這次我很確定了,就是這本!下一關的 token 為 {HarukiMurakami}
})
```
### 第 5 關
這一關要刪除 id 是 23 的書籍,要用到 DELETE 這個 Method。
- URL: https://lidemy-http-challenge.herokuapp.com/api/books/23
- Method: DELETE
#### JS 程式碼
```javascript=
const request = require('request')
const path = '/books/23'
request.delete('https://lidemy-http-challenge.herokuapp.com/api' + path,
(error, response, body) => {
if (error) return console.log('err', error)
let json
try {
json = JSON.parse(body)
} catch (err) {
console.log(err)
}
console.log(json.message)
// 輸出結果:
// 咦...是刪掉了沒錯,但總覺得哪裡怪怪的,算了,先這樣吧!下一關的 token 為 {CHICKENCUTLET}
}
)
```
> 做到這裡還以為是我哪裡做錯了嗎?怪怪的是什麼意思?到下一關才知道原來是題目的設計XD
### 第 6 關
這一題有新的版本的 URL,比較特別的是這一題還要加入帳號密碼驗證。
首先,要先把帳號密碼做 base64 編碼,我是使用這個[網站](https://www.base64encode.org)。輸入帳號密碼 `admin=admin123` 並點選 ENCODE 後,會得到 `YWRtaW46YWRtaW4xMjM=`。
第二步,將編碼完的結果放進 headers 中。
- URL: https://lidemy-http-challenge.herokuapp.com/api/v2/me
- Method: GET
#### JS 程式碼
```javascript=
const request = require('request')
const path = '/me'
request({
url: ' https://lidemy-http-challenge.herokuapp.com/api/v2' + path,
headers:{
'Authorization': 'Basic YWRtaW46YWRtaW4xMjM='
}
},
(error, response, body) => {
if (error) return console.log('err', error)
console.log(body)
// 輸出結果:
// {"username":"admin","email":"lib@lidemy.com"}
}
)
```
接下來再將 `lib@lidemy.com` 這個 email 回傳即可得到 token。
```javascript=
const request = require('request')
const params = '/lv6?token={CHICKENCUTLET}&email=lib@lidemy.com'
request('https://lidemy-http-challenge.herokuapp.com' + params,
(error, response, body) => {
if(error) return console.log('err', error)
console.log(body)
// 輸出結果:
// 對對對,就是這個,這樣才對嘛!下一關的 token 為 {SECurityIsImPORTant}
})
```
### 第 7 關
這關只需要用新的 API 網址再進行一次資料刪除即可,上一關的帳號密碼認證仍要保留著。
刪除完成,印出內容後即可獲得下一關 token。
- URL: https://lidemy-http-challenge.herokuapp.com/api/v2/books/89
- Method: DELETE
#### JS 程式碼
```javascript=
const request = require('request')
const path = '/books/89'
request.delete({
url: 'https://lidemy-http-challenge.herokuapp.com/api/v2' + path,
headers:{
'Authorization': 'Basic YWRtaW46YWRtaW4xMjM='
}
},
(error, response, body) => {
if (error) return console.log('err', error)
let json
try {
json = JSON.parse(body)
} catch (err) {
console.log(err)
}
console.log(json.message)
// 輸出結果:
// 希望下一次進這本書的時候不會再被偷走了。下一關的 token 為 {HsifnAerok}
}
)
```
### 第 8 關
這關要使用到 PATCH,首先要先找出要修改的目標,方法和第四關相似,只是作者的名字改用字串長度判斷。
- URL: https://lidemy-http-challenge.herokuapp.com/api/v2/books
- Method: GET
#### JS 程式碼
```javascript=
const request = require('request')
const path = '/books'
request({
url: 'https://lidemy-http-challenge.herokuapp.com/api/v2' + path,
headers:{
'Authorization': 'Basic YWRtaW46YWRtaW4xMjM='
}
},
(error, response, body) => {
if (error) return console.log('err', error)
let json
try {
json = JSON.parse(body)
} catch (err) {
console.log(err)
}
for (let i = 0; i < json.length; i++) {
if (json[i].name.indexOf('我') >= 0 && json[i].author.length === 4) {
console.log(json[i])
}
}
}
)
```
輸出的內容像這樣:
```
{ id: 3, name: '我殺的人與殺我的人', author: '東山彰良', ISBN: '9262228645' }
{ id: 57, name: '我的櫻花戀人', author: '宇山佳佑', ISBN: '2947749939' }
{
id: 72,
name: '日日好日:茶道教我的幸福15味【電影書腰版】',
author: '森下典子',
ISBN: '9981835427'
}
```
因此需要修改 ISBN 的就是尾數是 7 且 id 是 72 的這本書,接下來只要用利用 PATCH 將 ISBN 改為 9981835423 即可。
- URL: https://lidemy-http-challenge.herokuapp.com/api/v2/books/72
- Method: PATCH
#### JS 程式碼
```javascript=
const request = require('request')
const path = '/books/72'
request.patch({
url: 'https://lidemy-http-challenge.herokuapp.com/api/v2' + path,
headers:{
'Authorization': 'Basic YWRtaW46YWRtaW4xMjM='
},
form: {
ISBN: '9981835423'
}
},
(error, response, body) => {
if (error) return console.log('err', error)
let json
try {
json = JSON.parse(body)
} catch (err) {
console.log(err)
}
console.log(json.message)
// 輸出結果:
// 希望之後他們能引進語音輸入系統,我就只要講講話就好。下一關的 token 為 {NeuN}
}
)
```
### 第 9 關
這題當初我也卡關很久,上網搜尋後仍對於 User Agent 如何使用很疑惑。後來參考了學長姐的做法後,才知道也同樣是利用在 headers 內新增參數的方法。我們必須在 headers 中加入 `User-Agent` 這個參數,來告訴 API 我們是用 IE6 瀏覽器連線,才能取得資料。
這關要加兩個參數,第一個是題目有敘述到的 `'Authorization': 'Basic YWRtaW46YWRtaW4xMjM='`,第二個則是要先上網搜尋 IE6 的 User-Agent 並放入 headers裡,因此這個參數會像這樣:`'User-Agent': 'Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1)'`。
> 我是在這個網站上找到資料的:[Internet Explorer User Agents](https://developers.whatismybrowser.com/useragents/explore/software_name/internet-explorer/)
- URL: https://lidemy-http-challenge.herokuapp.com/api/v2/sys_info
- Method: GET
#### JS 程式碼
```javascript=
const request = require('request')
const path = '/sys_info'
request({
url: 'https://lidemy-http-challenge.herokuapp.com/api/v2' + path,
headers:{
'Authorization': 'Basic YWRtaW46YWRtaW4xMjM=',
'X-Library-Number': '20',
'User-Agent': 'Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1)'
},
},
(error, response, body) => {
if (error) return console.log('err', error)
let json
try {
json = JSON.parse(body)
} catch (err) {
console.log(err)
}
console.log(json.version)
// 輸出結果:
// 1A4938Jl7
}
)
```
接著再將 version 代碼回傳即可。
```javascript=
const request = require('request')
const params = '/lv9?token={NeuN}&version=1A4938Jl7'
request('https://lidemy-http-challenge.herokuapp.com' + params,
(error, response, body) => {
if(error) return console.log('err', error)
console.log(body)
// 輸出結果:
// 限制這麼多都被你突破了,真有你的。要不要考慮以後來我們圖書館當工程師啊?下一關的 token 為 {duZDsG3tvoA}
})
```
### 第 10 關
第 10 關就是單純玩 1A2B 遊戲,只是需要用 GET 傳送 request。假設要猜 1234 這四個數字,發送 request 的方式如下,剩下的純粹就是邏輯遊戲,這邊也不贅述了,猜到數字後就成功拿到第 11 關的 token了。
#### JS 程式碼
```javascript=
const request = require('request')
const params = '/lv10?token={duZDsG3tvoA}&num=1234'
request('https://lidemy-http-challenge.herokuapp.com' + params,
(error, response, body) => {
if(error) return console.log('err', error)
console.log(body)
// 輸出結果:
// 0A2B
})
```
### 第 11 關
在這關有新的 API 網址,先試著將打招呼的 path 用 GET 回傳後,會得到這樣的結果:
```
您的 origin 不被允許存取此資源,請確認您是從 lidemy.com 送出 request。
```
可得知,題目中說的限制就是 request 的來源網址。先偷看一下 hint 寫什麼:
```
伺服器會檢查 origin 這個 header,只要騙過伺服器就行了
```
因此可以知道,解決方法就只要在 headers 中加入 `'origin': 'lidemy.com'`。
- URL: https://lidemy-http-challenge.herokuapp.com/api/v3/hello
- Method: GET
#### JS 程式碼
```javascript=
const request = require('request')
const path = '/hello'
request({
url: 'https://lidemy-http-challenge.herokuapp.com/api/v3' + path,
headers:{
'origin': 'lidemy.com'
},
},
(error, response, body) => {
if (error) return console.log('err', error)
console.log(body)
// 輸出結果:
// Hello! 下一關的 token 為 {r3d1r3c7}
}
)
```
### 第 12 關
這一題看似只需要採用 /delivery_token 這個 path,但送出後會發現並無法直接得到 token。
- URL: https://lidemy-http-challenge.herokuapp.com/api/v3/delivery_token
- Method: GET
#### JS 程式碼
```javascript=
const request = require('request')
const path = '/delivery_token'
request('https://lidemy-http-challenge.herokuapp.com/api/v3' + path,
(error, response, body) => {
if (error) return console.log('err', error)
console.log(body)
// 輸出結果:
// 我已經把運送要用的 token 給你囉,請你仔細找找
}
)
```
這時要注意到題目有一句話非常關鍵:
```
A 到 C,C 才到 B,中間會經過一些轉運點才會到達目的地
```
也就是說,最後 Response 回來的這個結果是經過兩次轉址,因此可以推斷 token 藏在中間經過轉址的這個網址。這部分只跑 JS 程式碼是看不出端倪的,因次改用 DevTools 來看看:

可以看到,發送 `deliver_token` 這個 request 網址後,會先轉址到 `stopover`,接著再一次轉址至 `deliver_token_result`。查看 **stopover** 裡的資訊,可以發現在 headers 中有一項 `X-Lv13-Token: {qspyz}`,這個就是這一關的解答了。
### 第 13 關
我們先試著用 `/logs` 這個 path 來看看會發生什麼事。
- URL: https://lidemy-http-challenge.herokuapp.com/api/v3/logs
- Method: GET
#### JS 程式碼
```javascript=
const request = require('request')
const path = '/logs'
request('https://lidemy-http-challenge.herokuapp.com/api/v3' + path,
(error, response, body) => {
if (error) return console.log('err', error)
console.log(body)
// 輸出結果:
// 此 request 不是來自菲律賓,禁止存取系統資訊。
}
)
```
再看一下 hint 說什麼:
```
你有聽過代理伺服器 proxy 嗎?
```
因此可推測,應是要使用 proxy 來偽裝 request 的伺服器來源是在菲律賓。上網用「proxy 菲律賓」這個關鍵字搜尋後,找到這個[網站](http://free-proxy.cz/zh/proxylist/country/PH/https/ping/all),並且選擇 協定為 https 的 IP 來當作代理伺服器。
若是使用 MacBook,設定代理伺服器的步驟為:`系統偏好設定 > 網路 > 進階 > 代理伺服器`,勾選`安全網頁代理伺服器(HTTPS)`後,填入 IP 位址與 Port。

接下來直接在網址列輸入前往 `https://lidemy-http-challenge.herokuapp.com/api/v3/logs`,就可得到結果。
```
[
{ logType: 'token', value: '{SEOisHard}' }
]
```
### 第 14 關
這一關老實說我只看題目覺得有點理解不能,因此馬上查看 hint XD
```
伺服器是怎麼辨識是不是 Google 搜尋引擎的?仔細想想之前我們怎麼偽裝自己是 IE6 的
```
由這個提示可以聯想到第 9 關的 **User-Agent**。因此只需要上網用「Google User-Agent」查詢一下就能得到答案,示範如下。
- URL: https://lidemy-http-challenge.herokuapp.com/api/v3/index
- Method: GET
#### JS 程式碼
```javascript=
const request = require('request')
const path = '/index'
request({
url: 'https://lidemy-http-challenge.herokuapp.com/api/v3' + path,
headers:{
'User-Agent': 'Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)'
},
},
(error, response, body) => {
if (error) return console.log('err', error)
console.log(body)
}
)
```
得到輸出結果後就拿到最後一關的 token 了,第 15 關沒有內容,到這邊基本上就大功告成了!
## 破關心得分享
非常感謝 Huli 老師用心地設計這個遊戲,這是一個很好的方式去把基本 API 的內容走過、複習過一遍,因為題目的敘述真的都很有趣,會讓人想一關接著一關的玩下去。同時,也練習到用 Postman 和 Curl(雖然我這裡只寫老師上課教到的 JS 程式碼方法),有許多同學在最後心得分享提到可以用這兩個工具輕鬆破關,試了一次後發現這兩種工具確實非常簡便,但同時也慶幸我第一次完成是採用土法煉鋼的方法,因為對於 API 如何用程式操作有更進一步的了解。
第一次坐覺得比較困難的地方是 headers 裡面要放的參數,像第 9 關設定 User-Agent 這裡卡比較久,原本以為這是一個網頁的設定之類的,後來知道是放在 headers 裡面後,在解後面的關卡時就比較有感覺哪些東西可能也是要被放在 headers 裡面。
這篇破關攻略是第二次邊實作邊寫下來的,畢竟第一次在卡關的時候有上網去找過去學長姐的分享,第二次完全靠自己力量再做一次,並同時以筆記方式寫下,藉此加深印象。