Pasing request
這一個章節我們來聊聊如何讀取一個請求的內容。
撰寫網站的時候,我們會希望前端使用者能傳遞一些請求,後端伺服器讀取了使用者的請求後做出相對應的服務,達到互動的效果。例如:使用者輸入帳號密碼登入網站,或者讓前端使用者可以有管道可以查詢後端的資料庫等等。
在前端我們可以透過表單來達到這個目的,或者也可以使用AJAX的方式來達成。而請求送出後,伺服器能夠解析請求,再來渲染對應的模板回前端。我們這個章節分成兩個部分來進行,第一部分是GET方法的QueryString,第二部分是POST方法的表單解析。
Get - QueryString parsing
在Koa中,爬取QueryString是再簡單不過的事情了,Koa會自動parsing query string的內容,儲存成ctx.query
物件。
我們依舊延續上次YouTube循環播放的那個範例,不過我們這次不使用URL parameters,而改使用GET表單的方式來讀取使用者想要重複播放哪個影片。
在Controller中爬取QueryString
// GET-parsing.ts
import Koa = require('koa');
import Router = require('koa-router');
import Path = require('path');
import Views = require('koa-views');
const app = new Koa();
const router = new Router();
app.use(Views(Path.join(__dirname, 'views'), {
extension: 'pug',
map: {
pug: 'pug',
},
}));
router.get('/loop/', async (ctx) => {
const VideoID = ctx.query.VideoID; // 讀取QueryString中的VideoID
await ctx.render('looper', {
VideoID: VideoID ? VideoID : undefined, // 如果存在VideoID變數則傳給前端,否則就傳undefined
});
});
app.use(router.routes()).use(router.allowedMethods());
app.listen(3000);
這邊標題下Controller是因為在Koa框架中,Routing部分其實就是類似于MVC架構的Controller
looper模板
<!DOCTYPE html>
<!-- views/looper.pug -->
html(lang="en")
head
meta(charset="UTF-8")
meta(name="viewport", content="width=device-width, initial-scale=1.0")
meta(http-equiv="X-UA-Compatible", content="ie=edge")
title My awesome youtube looper
body
h1 Awesome Youtube Looper
form(action="/loop" method="GET")
span VideoID:
input(type="text" name="VideoID")
br
input(type="submit" value="submit")
br
if VideoID
iframe(src=`https://www.youtube.com/v/${VideoID}?version=3&loop=1&playlist=${VideoID}&autoplay=1`, frameborder="0" allowfullscreen)
else
p Hi~ Please enter your favorite video, I can loop it~
我們這次增加了一個表單讀取參數,並且讓他使用GET方法request/loop
頁面,在15~18行的部分,先判斷變數是否存在,而有這不同的行為。
因此這次的成果如下圖所示
請特別注意表單送出後網址的變化,GET方法會將參數緊接在
?
後,參數是一個key-value的組合,用&
符號連接多組參數
因此我們的參數可以長這樣/loop?VideoID=pgGM7lBEvBs&AutoPlay=1
代表傳遞了兩個參數VideoID與AutoPlay
POST - Body Parsing
你可能已經發現了一件事,GET方法會將所有傳遞的變數擺放在URL的後方,如此一來傳遞這個請求時,其實很容易被觀察到表單的內容,因此我們會希望使用POST方法來傳遞一些隱私性較高的參數,例如使用者的帳號密碼等等。
要爬取POST請求我們必須使用koa-bodyparser
中間件來達成。
安裝koa-bodyparser
yarn add koa-bodyparser @types/koa-bodyparser
使用koa-bodyparser
這邊我們做個簡單的會員註冊機制,而會員資料放在記憶體當中(其實就是放在變數中),當使用者瀏覽/users
時會看到所有的使用者明細,當使用者瀏覽/regist
時會到一個註冊頁面,註冊的表單則是POST到/regist
,因此/regist
在不同方法時會有不同行為。
// POST-parsing.ts
import Koa = require('koa');
import Router = require('koa-router');
import Path = require('path');
import Views = require('koa-views');
import bodyParser = require('koa-bodyparser');
const app = new Koa();
const router = new Router();
interface IUser { // 建立一個IUser的interface,如果是寫Javascript可以不用寫這個
username: string;
password: string;
}
const users: IUser[] = []; // 建立一個空的users陣列,裡頭即將存放多個users
app.use(Views(Path.join(__dirname, 'views'), {
extension: 'pug',
map: {
pug: 'pug',
},
}));
app.use(bodyParser()); // 插入bodyParser中間件,必須要由這個中間件才可以爬取POST資料
router.get('/users', async (ctx) => { // 瀏覽/users時渲染全部會員的資料
await ctx.render('users', {
users,
});
});
router
.get('/regist', async (ctx) => { // 瀏覽/regist時渲染註冊頁面
await ctx.render('regist');
})
.post('/regist', async (ctx) => { // 處理/regist表單
const user: IUser = {
username: ctx.request.body.username, // bodyParser爬取到的資訊會存放在ctx.request.body物件中
password: ctx.request.body.password,
};
users.push(user);
console.log(users);
await ctx.redirect('/users');
});
app.use(router.routes()).use(router.allowedMethods());
app.listen(3000);
Tracecode
- 第25行: 由於Koa並沒有內建爬取POST表單的功能,因此我們引入
koa-bodyparser
中間件來幫我們解析 - 第39-40行: bodyParser爬取到的資訊會存放在
ctx.request.body
中
References:
1. koajs/bodyparser
所有範例中的程式碼皆放置在koa-tutorial-sample專案中