[TOC]
Koa2
Koa2是現(xiàn)在最流行的基于Node.js平臺(tái)的web開發(fā)框架漓骚,它很小,但擴(kuò)展性很強(qiáng)
使用 koa 編寫 web 應(yīng)用榛泛,通過(guò)組合不同的 generator蝌蹂,可以免除重復(fù)繁瑣的回調(diào)函數(shù)嵌套,并極大地提升錯(cuò)誤處理的效率曹锨。一個(gè)Koa應(yīng)用就是一個(gè)對(duì)象孤个,包含了一個(gè)middleware數(shù)組,這個(gè)數(shù)組由一組Generator函數(shù)組成沛简。這些函數(shù)負(fù)責(zé)對(duì)HTTP請(qǐng)求進(jìn)行各種加工齐鲤,比如生成緩存斥废、指定代理、請(qǐng)求重定向等等给郊。這些中間件函數(shù)基于 request 請(qǐng)求以一個(gè)類似于棧的結(jié)構(gòu)組成并依次執(zhí)行牡肉。
運(yùn)行環(huán)境搭建
知識(shí)為了能夠讓koa運(yùn)行起來(lái),沒(méi)有在框架中使用
知識(shí)簡(jiǎn)單的初始化項(xiàng)目淆九,簡(jiǎn)單示例
安裝Node
- 最好的安裝的方法就是使用
nvm
進(jìn)行安裝 - 在官網(wǎng)下載指定操作系統(tǒng)和版本進(jìn)行安裝统锤,和安裝軟件一樣,一步一步來(lái)就可以
初始化項(xiàng)目
找個(gè)文件夾
cd
進(jìn)去-
初始化
package.json
文件npm init -y
-
安裝
koa
npm install --save koa
-
測(cè)試是否可以運(yùn)行
- 在文件夾的根目錄下新建
js
文件 - 可將官方的示例代碼復(fù)制或者手動(dòng)輸入進(jìn)去
const Koa = require('koa'); const app = new Koa(); // response app.use(ctx => { ctx.body = 'Hello Koa'; }); app.listen(3000);
-
在終端中輸入
node index.js
-
當(dāng)然這里需要
js
文件修改后熱加載的話炭庙,可以使用nodemon
運(yùn)行-
安裝
nodemon
npm install --save-dev nodemon
-
使用
nodemon index.js
這樣每次修改之后就不用重啟服務(wù)器了
-
- 在文件夾的根目錄下新建
Koa2
中的請(qǐng)求
GET
請(qǐng)求
在koa2中GET請(qǐng)求通過(guò)request接收饲窿,但是接受的方法有兩種:query和querystring。
query:返回的是格式化好的參數(shù)對(duì)象煤搜。
-
querystring:返回的是請(qǐng)求字符串。
const Koa = require('koa'); const app = new Koa(); app.use(async(ctx)=>{ let url = ctx.url; let request = ctx.request; //從request中接受query,querystring /* 兩者都是查詢的參數(shù) query: 是格式化后的數(shù)據(jù)唧席, querystring: 字符串類型 他們既在request中可以獲取也可以在ctx中獲取 */ let req_query = request.query; let req_querystring = request.querystring; let ctx_query = ctx.query; let ctx_querystring = ctx.querystring; ctx.body = { url, req_query, req_querystring, ctx_query, ctx_querystring } }); app.listen(8999,()=>{ console.log('serve is listening on 8999'); });
- 使用
nodemon
啟動(dòng)服務(wù)之后擦盾,在瀏覽器打開端口訪問(wèn), - 在路徑中輸入不同的請(qǐng)求以及請(qǐng)求參數(shù)淌哟;會(huì)打印出
body
對(duì)應(yīng)的值
- 使用
POST
請(qǐng)求
對(duì)于POST請(qǐng)求的處理迹卢,Koa2沒(méi)有封裝方便的獲取參數(shù)的方法,需要通過(guò)解析上下文context中的原生node.js請(qǐng)求對(duì)象req來(lái)獲取徒仓。
獲取Post請(qǐng)求的步驟:
- 解析上下文ctx中的原生nodex.js對(duì)象req腐碱。
- 將POST表單數(shù)據(jù)解析成query string-字符串.(例如:name='blog'&age=18)
- 將字符串轉(zhuǎn)換成JSON格式。
ctx.request和ctx.req的區(qū)別
- ctx.request:是Koa2中context經(jīng)過(guò)封裝的請(qǐng)求對(duì)象掉弛,它用起來(lái)更直觀和簡(jiǎn)單症见。
- ctx.req:是context提供的node.js原生HTTP請(qǐng)求對(duì)象。這個(gè)雖然不那么直觀殃饿,但是可以得到更多的內(nèi)容谋作,適合我們深度編程。
ctx.method 得到請(qǐng)求類型
- Koa2中提供了ctx.method屬性乎芳,可以輕松的得到請(qǐng)求的類型遵蚜,然后根據(jù)請(qǐng)求類型編寫不同的相應(yīng)方法,這在工作中非常常用奈惑。我們先來(lái)作個(gè)小例子吭净,根據(jù)請(qǐng)求類型獲得不同的頁(yè)面內(nèi)容。GET請(qǐng)求時(shí)得到表單填寫頁(yè)面肴甸,POST請(qǐng)求時(shí)寂殉,得到POST處理頁(yè)面。
const Koa = require("koa");
const app = new Koa();
app.use(async ctx => {
if (ctx.url === "/" && ctx.method === "GET") {
let html = `
<h1>這是get的表單提交</h1>
<form action="/post" method="POST">
<p>姓名</p>
<input type="text" />
<p>年齡</p>
<input type="text" />
<br/>
<button type="submit">提交</button>
</form>
`;
ctx.body = html;
} else if (ctx.url==='/post' && ctx.method === "POST") {
let result = await parsePostData(ctx);
// console.log(result);
ctx.body = result;
} else {
ctx.body = `<h1>404 NOT FOUND</h1>`;
}
});
//使用ctx.req.on來(lái)接收事件
function parsePostData(ctx){
return new Promise((resolve,reject)=>{
try {
let postData = '';
ctx.req.on('data', data =>{
console.log(data);
postData += data;
});
ctx.req.addListener('end',()=>{
let parseData = parseQueryStr(postData);
resolve(parseData);
});
} catch (error) {
reject(error);
}
});
}
//將POST字符串解析JSON對(duì)象
function parseQueryStr(queryStr){
let queryData={};
let queryStrList = queryStr.split('&');
console.log(queryStrList);
for( let [index,queryStr] of queryStrList.entries() ){
let itemList = queryStr.split('=');
console.log(itemList);
queryData[itemList[0]] = decodeURIComponent(itemList[1]);
}
return queryData
}
app.listen(8999, () => {
console.log("serve is listening on 8999");
});
使用koa-bodyParser
中間件
接收并解析POST請(qǐng)求
koa-bodyparser中間件可以把koa2上下文的formData數(shù)據(jù)解析到ctx.request.body中原在。
-
安裝中間件
npm install --save koa-bodyparser
-
和
koa
一樣,需要引入使用//注意這里的bodyparser不是駝峰命名的規(guī)范 const bodyParser = require('koa-bodyparser') //這里bodyParser是一個(gè)方法,需要調(diào)用 app.use(bodyParser());
-
直接使用
ctx.request.body
進(jìn)行獲取POST
請(qǐng)求參數(shù)const Koa = require("koa"); const bodyParser = require('koa-bodyparser'); const app = new Koa(); app.use(bodyParser()); app.use(async ctx => { if (ctx.url === "/" && ctx.method === "GET") { let html = ` <h1>這是get的表單提交</h1> <form action="/p" method="POST"> <p>姓名</p> <input type="text" /> <p>年齡</p> <input type="text" /> <br/> <button type="submit">提交</button> </form> `; ctx.body = html; } else if (ctx.url==='/p' && ctx.method === "POST") { let result = ctx.request.body; // console.log(result); ctx.body = result; } else { ctx.body = `<h1>404 NOT FOUND</h1>`; } }); app.listen(8998, () => { console.log("serve is listening on 8998"); });
koa2
中的路由
koa2
中原生路由的實(shí)現(xiàn)
const Koa = require('koa');
const fs = require('fs');
const app = new Koa();
function render(page){
return new Promise((resolve,reject)=>{
let pageUrl = `./page/${page}`;
fs.readFile(pageUrl,"binary",(err,data)=>{
console.log(444);
if(err){
reject(err)
}else{
resolve(data);
}
})
})
}
async function route(url){
let page = '404.html';
switch(url){
case '/':
page ='index.html';
break;
case '/index':
page ='index.html';
break;
case '/todo':
page = 'todo.html';
break;
case '/404':
page = '404.html';
break;
default:
break;
}
let html = await render(page);
return html;
}
app.use(async(ctx)=>{
let url = ctx.request.url;
let html = await route(url);
ctx.body=html;
})
app.listen(3000);
console.log('starting at 3000');
koa-router
中間件的使用
-
安裝
koa-router
中間件npm install --save koa-router
-
引入以及簡(jiǎn)單使用
-
new Router()
中的prefix
是為了給路由添加前綴 - 訪問(wèn)的時(shí)候我們就都是
/name/list
有統(tǒng)一的前綴
const Koa = require("koa"); const Router = require('koa-router'); const app = new Koa(); const router = new Router({ prefix: '/name' }); router .get('/',(ctx,next)=>{ ctx.body = 'get'; }) .get('/list',(ctx,next)=>{ ctx.body = 'get list'; }) /**router.allowedMethods() 這時(shí)候只有當(dāng)請(qǐng)求路徑匹配到了/list才會(huì)執(zhí)行allowedMethods, 然后根據(jù)ctx.status設(shè)置response響應(yīng)頭 **/ app .use(router.routes()) .use(router.allowedMethods()); app.listen(8998, () => { console.log("serve is listening on 8998"); });
-
路由的層級(jí)
也就是我們常說(shuō)的父子路由
-
具體的寫法見代碼
const Koa = require('koa'); const Router = require('koa-router'); const app = new Koa(); const router = new Router(); const home = new Router(); const page = new Router(); router .get('/',async (ctx)=>{ ctx.body = ` <h3>路由跳轉(zhuǎn)</h3> <a href="/home"><button>home路由</button></a> <a href="/home/homeChild1"><button>home子路由1</button></a> <a href="/home/homeChild2"><button>home子路由2</button></a> <a href="/page"><button>page路由</button></a> <a href="/page/pageChild1"><button>page子路由1</button></a> <a href="/page/pageChild2"><button>page子路由2</button></a> ` }) /* 路由中的參數(shù)傳遞 使用ctx.query進(jìn)行獲取 */ .get('/home',async (ctx)=>{ ctx.body = ctx.query; }) .get('/page',async (ctx)=>{ ctx.body = 'page'; }); home .get('/homeChild1',async (ctx,next)=>{ ctx.body = 'home-child-1'; }) .get('/homeChild2',async (ctx,next)=>{ ctx.body = 'home-child-2'; }); page .get('/pageChild1',async (ctx,next)=>{ ctx.body = 'page-child-1'; }) .get('/pageChild2',async (ctx,next)=>{ ctx.body = 'page-child-2'; }); //裝載子路由 router .use('/home',home.routes(),home.allowedMethods()) .use('/page',page.routes(),page.allowedMethods()); // app.use(async (ctx)=>{ // ctx.body = 'body'; // }); app .use(router.routes()) .use(router.allowedMethods()); app.listen(8999,()=>{ console.log('serve is listening on 8999'); })
Koa2
中使用Cookie
koa的上下文(ctx)直接提供了讀取和寫入的方法鸽斟。
讓我們直接操作cookie
-
ctx.cookies.get(name,[optins]):
讀取上下文請(qǐng)求中的cookie
夕冲。 -
ctx.cookies.set(name,value,[options])
:在上下文中寫入cookie
。
ctx.cookies.set(name, value, [options])
通過(guò) options
設(shè)置 cookie name
的 value
:
-
maxAge
一個(gè)數(shù)字表示從 Date.now() 得到的毫秒數(shù) -
signed
cookie 簽名值 -
expires
cookie 過(guò)期的Date
-
path
cookie 路徑, 默認(rèn)是'/'
-
domain
cookie 域名 -
secure
安全 cookie -
httpOnly
服務(wù)器可訪問(wèn) cookie, 默認(rèn)是 true -
overwrite
一個(gè)布爾值拌倍,表示是否覆蓋以前設(shè)置的同名的 cookie (默認(rèn)是 false). 如果是 true, 在同一個(gè)請(qǐng)求中設(shè)置相同名稱的所有 Cookie(不管路徑或域)是否在設(shè)置此Cookie 時(shí)從 Set-Cookie 標(biāo)頭中過(guò)濾掉。
const Koa = require("koa");
const Router = require('koa-router');
const app = new Koa();
const router = new Router();
router
.get('/',(ctx,next)=>{
})
.get('/list',(ctx,next)=>{
})
app
.use(async(ctx)=>{
if(ctx.url ==='/'){
ctx.cookies.set('myCookieTest1','zhouqd1',{
domain: '127.0.0.1', // 寫cookie所在的域名
path: '/', // 寫cookie所在的路徑
maxAge: 1000*60*60*24, // cookie有效時(shí)長(zhǎng)
expires: new Date('2020-12-31'), // cookie失效時(shí)間
httpOnly: false, // 是否只用于http請(qǐng)求中獲取
overwrite: false // 是否允許重寫
});
ctx.body = 'cookie is ok';
}else{
if(ctx.cookies.get('myCookieTest1')){
ctx.body = ctx.cookies.get('myCookieTest1');
}
}
})
.use(router.routes())
.use(router.allowedMethods())
app.listen(8998, () => {
console.log("serve is listening on 8998");
});
koa2
中的模板
開發(fā)中不可能把所有的html代碼全部卸載JS里,這顯然不現(xiàn)實(shí)兼呵,也沒(méi)辦法完成大型web開發(fā)。必須借用模板機(jī)制來(lái)幫助我們開發(fā)腊敲,這節(jié)課我們就簡(jiǎn)單了解一下Koa2的模板機(jī)制击喂,koa2的目標(biāo)機(jī)制要依靠中間件來(lái)完成開發(fā)。
-
安裝中間件
在koa2中使用模板機(jī)制必須依靠中間件碰辅,我們這里選擇koa-views中間件
npm install --save koa-views
-
安裝ejs模板引擎
npm install --save ejs
-
編寫模板
安裝好ejs模板引擎后懂昂,就可以編寫模板了,為了模板統(tǒng)一管理没宾,我們新建一個(gè)view的文件夾凌彬,并在它下面新建index.ejs文件。
<!DOCTYPE html> <html> <head> <title><%= title %></title> </head> <body> <h1><%= title %></h1> <p>EJS Welcome to <%= title %></p> </body> </html>
-
koa
文件const Koa = require('koa'); const path = require('path'); const views = require('koa-views'); const app = new Koa(); app.use(views(path.join(__dirname,'./views/'),{ extension: 'ejs' })); app.use(async (ctx) =>{ console.log(ctx); const title = 'hello koa template' await ctx.render('index',{title}); }); app.listen(8999,()=>{ console.log('服務(wù)器開啟成功,請(qǐng)打開8999....'); })
koa2
中的靜態(tài)資源的處理
在后臺(tái)開發(fā)中不僅有需要代碼處理的業(yè)務(wù)邏輯請(qǐng)求循衰,也會(huì)有很多的靜態(tài)資源請(qǐng)求铲敛。比如請(qǐng)求js,css会钝,jpg伐蒋,png這些靜態(tài)資源請(qǐng)求。也非常的多迁酸,有些時(shí)候還會(huì)訪問(wèn)靜態(tài)資源路徑先鱼。用koa2自己些這些靜態(tài)資源訪問(wèn)是完全可以的,但是代碼會(huì)雍長(zhǎng)一些奸鬓。所以這節(jié)課我們利用koa-static中間件來(lái)實(shí)現(xiàn)靜態(tài)資源的訪問(wèn)型型。
-
安裝
koa-static
npm install --save koa-static
-
新建
static文件夾
在其中放入一些圖片或者css等靜態(tài)資源的文件const Koa = require('koa'); const path = require('path'); const static = require('koa-static'); const app = new Koa(); const staticPath = './static/' app.use(static(path.join(__dirname,staticPath))); app.use(async (ctx) =>{ ctx.body = 'hello static'; }); app.listen(8999,()=>{ console.log('服務(wù)器開啟成功,請(qǐng)打開8999....'); })