爲什麼要學 Koa
,不學 Express
?
因爲不想寫回調,日常開發習慣 async await
處理異步流程,因此選擇 Koa 2
。(以下簡稱 Koa)
Koa2 核心概念
洋蔥模型 其實就是 async await
執行流程,在 koa
每次調用中間件時,回調函數中 next
會吧執行權遞交給下一個回調函數。按筆者通俗的解釋爲:洋蔥先從外側切到內側,再從內測切到另一邊的外側。其實也就是回調函數被 async await
化了而已。
ctx(context)
上下文,request
, response
都被合併封裝到 ctx
中,方便每個中間件進行操作,調用。
打印一下 ctx
,可見,ctx
描述的是,每個完整的 http
所包含的信息,包括但不限於 請求方法(method
),url(request url
),響應碼(status
), 響應信息 message
, header
等。
路由 路由的處理也是包含在中間件中的。路由的概念並不陌生,Angular
中除了基本的 Router 路由器讓你處理跳轉路由之外,還有 Activeted
路由,來處理不同的 Url
和接受 query
等。 在我看來,這裏 Koa
的路由,其實跟 Angular Activeted
的概念很像。代碼:
// 根路由
app.use(async (ctx, next) => {
if(ctx.request.path === '/') {
ctx.response.body = '<h1>index Router</h1>';
}
else {
await next();
}
});
// todo 路由
app.use(async (ctx, next) => {
if(ctx.request.path === '/todo') {
ctx.response.body = '<h1>Todo Router</h1>';
}
else {
await next();
}
});
完事。 Demo:
但官方已經有更好的中間件了:koa-router
const Koa = require('koa');
const app = new Koa();
const router = require('koa-router')()
// 根路由
router.get('/', async (ctx, next) => {
ctx.response.body = '<h1>index Router</h1>';
});
// todo 路由
router.get('/todo', async (ctx, next) => {
ctx.response.body = '<h1>Todo Router</h1>';
});
app.use(router.routes());
具名路由:
// 給路由起個名字,便於複用
router.get('user', 'users/:id', async (ctx, next) => {
ctx.response.body = `你要訪問的是 : ${ctx.response.path}`;
});
query param 以及 param 對象
// params
router.get('/home/:id', async (ctx,next) => {
ctx.response.type = 'application/json';
ctx.response.body = ctx.params;
});
// query-params
router.get('/home' async (ctx, next) => {
ctx.response.type = 'application/json';
ctx.response.body = `${ctx.request.query}`;
});
params:-object:
(
query-params:
拿 Angular
中的路由做一下類比:
// 形如 /home/:id
this.activitedRoute.paramMap.subscribe( param => console.log(param)); // {"id": "4"}
// 形如 /home?a=1&b=2
this.route.queryParamMap.subscribe(res => console.log(res)); // map<string,number> = new Map({a: 1, b:2});
都是根據瀏覽器的 urlParamsMap
來實現的。
body-parser
上述的所有請求, 都是基於 GET
請求的,那麼如果客戶端發來 post
請求,怎麼解析 send 過來的數據呢?通過路由嗎?做不到啊!所以還是得藉助 ctx 上掛着的的對象來進行解析,因爲 post 請求接受 form Data
也是異步的,所以要給 ctx.request
綁定監聽:
const Koa = require('koa')
const router = require('koa-router')()
const app = new Koa()
// 通用路由,直接給出 form 表單,誘導進一步的 post 操作
app.use(async (ctx, next) => {
ctx.body = `
<form action="/submit" method="post">
<input name="account" type="text"> 賬號
<br/>
<input name="password" type="password"> 密碼
<br/>
<button>提交</button>
</form>
`;
await next();
});
// 進行 post 操作時的 middleware
app.use(async (ctx, next) => {
if (ctx.request.url === '/submit') {
// ctx.response.type = 'application/json';
const data = await parseData(ctx);
ctx.response.body = data;
}
await next();
});
// 接收上下文,通過 事件監聽 和 promise 來達到解析數據的目的
function parseData(ctx) {
return new Promise((resolve, reject) => {
try {
let str = ''
ctx.req.on('data', (data) => {
str += data
})
ctx.req.addListener('end', () => {
resolve(str)
})
} catch (err) {
reject(err)
}
});
}
// add router middleware:
app.use(router.routes());
app.listen(3000, () => {
console.log('server is running at http://localhost:3000')
});
Demo:
But, 不用這麼麻煩,有對應的輪子了: koa-bodyparser
該中間件,會將 post 的數據,全部解析進 ctx.request.body
中
核心代碼:
const bodyparser = require('koa-bodyparser');
// 直接使用中間件
app.use(bodyparser());
app.use(async (ctx, next) => {
ctx.body = `
<form action="/submit" method="post">
<input name="account" type="text"> 賬號
<br/>
<input name="password" type="password"> 密碼
<br/>
<button>提交</button>
</form>
`;
await next();
});
app.use(async (ctx, next) => {
if (ctx.request.url === '/submit') {
// ctx.response.type = 'application/json';
ctx.response.body = ctx.request.body;
}
await next();
});
來試試效果吧!
搞定了。