一盏浙、起步
首先創(chuàng)建一個(gè)文件夾,然后初始化 package.json :
npm init -y
安裝koa2:
cnpm i koa --save
在文件目錄下新建一個(gè)index.js纺蛆,然后寫下如下代碼:
const Koa = require('koa')
const app = new Koa()
app.use( async(ctx) => {
ctx.body = "hello world"
})
app.listen(1996)
console.log("demo in run")
然后運(yùn)行這個(gè)文件:
nodemon index.js
然后我們就能在后臺(tái)看見這個(gè):
demo is run
然后打開瀏覽器逞度,輸入 http://127.0.0.1:1996就可以看見這個(gè)了:
這樣我們就搭建好了最簡(jiǎn)單的web服務(wù)器。但除了這些帘睦,有一點(diǎn)需要知道的是,在koa2中坦康,async函數(shù)已經(jīng)大規(guī)模使用了竣付,它很好的處理了異步的邏輯,所以學(xué)習(xí)koa2之前滞欠,勁量將async和await解決掉:
const wait1 = () => {
return new Promise(resolve => {
setTimeout(() => {
resolve()
console.log("1s later")
}, 1000)
})
}
const wait2 = () => {
return new Promise((resolve) => {
resolve(setTimeout(()=>{console.log("2s later")},2000))
})
}
async function test() {
const a = await wait1()
const b = await wait2()
console.log("end")
}
console.log("start")
test()
上面的代碼執(zhí)行起來就是這樣的:
start
1s later
end
2s later
它很好的解決了異步的一些麻煩古胆,且寫出來的代碼的可讀性也非常好。
二仑撞、請(qǐng)求數(shù)據(jù)獲取
2.1 Get請(qǐng)求的接收
在Koa2中GET請(qǐng)求可以通過 request 接受收赤兴,但接受的方式有兩種:
- query:返回的是格式化后的參數(shù)對(duì)象
- querystring:返回的請(qǐng)求字符串
我們可以由兩種方式來獲取GET請(qǐng)求妖滔,一種是通過 ctx.request 來獲取GET請(qǐng)求隧哮,一種則是直接在ctx中得到GET請(qǐng)求:
const Koa = require('koa')
const app = new Koa()
app.use(async(ctx) => {
const url = ctx.url
// 使用 ctx.request
const request = ctx.request
const req_query = request.query
const req_querystring = request.querystring
// 直接使用ctx來獲取
const req_ctx = ctx.query
const req_ctx1 = ctx.querystring
ctx.body = {
url,
req_query,
req_querystring,
req_ctx,
req_ctx1,
}
})
app.listen(3000,() => {
console.log("demo1 is run")
})
然后我們?cè)跒g覽器中輸入 http://127.0.0.1:3000?user=srtian&age=18 來訪問頁(yè)面就可以看到這個(gè)(這是經(jīng)過美化的表現(xiàn)):
2.2 POST請(qǐng)求的接收
在 Koa2 中,沒有給對(duì)于 POST 請(qǐng)求的處理封裝方便的獲取參數(shù)的方法座舍,需要通過通過解析上下文 context 中的元素 node.js 請(qǐng)求對(duì)象 req 來獲取沮翔。因此獲取POST請(qǐng)求的步驟可以理解為以下三步:
- 解析上下文 ctx 中的原生 node.js 對(duì)象 req。
- 將POST表單數(shù)據(jù)解析成 query string 字符串曲秉。
- 將字符串轉(zhuǎn)換成 JSON 格式采蚀。
const Koa = require('koa')
const app = new Koa()
app.use(async(ctx) => {
if (ctx.url === '/' && ctx.method === 'GET') {
let html = `
<h2>This is demo2</h2>
<form method="POST" action="/">
<p>username:</p>
<input name="username">
<p>age:</p>
<input name="age">
<p>website</p>
<input name="website">
<button type="submit">submit</button>
</form>
`
ctx.body = html
} else if (ctx.url === '/' && ctx.method === 'POST') {
let postData = await parsePostDate(ctx)
ctx.body = postData
} else {
ctx.body = '<h2>404</h2>'
}
})
const parsePostDate = (ctx) => {
return new Promise((resolve, reject) => {
try{
let postData = ""
ctx.req.on('data', (data) => {
postData += data
})
ctx.req.addListener("end", function() {
let parseData = parseQueryStr(postData)
resolve(parseData)
})
} catch(error) {
reject(error)
}
})
}
const parseQueryStr = (queryStr) => {
const queryData = {}
const 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(3000, () => {
console.log('dom2 is run')
})
然后打開瀏覽器,輸入http://127.0.0.1:3000/:
完善信息后承二,點(diǎn)擊submit:
koa-bodyparser中間件
顯然上面的 POST 請(qǐng)求的接受非常麻煩榆鼠,至少對(duì)我而言,徒手寫個(gè)這樣的輪子在不查資料的情況下是做不到的亥鸠,而這樣的輪子當(dāng)然也有人來做妆够,koa-bodyparser就是一個(gè)造好的輪子识啦。我們?cè)趉oa中把這種輪子就叫做中間件。對(duì)于POST請(qǐng)求的處理神妹,koa-bodyparser中間件可以把koa2上下文的formData數(shù)據(jù)解析到ctx.request.body中颓哮。
首先我們要安裝中間件:
cnpm i koa-bodyparser@3 --save
然后我們就能非常輕松愉快的使用這個(gè)中間件來改造我們上面的代碼了:
const Koa = require('koa')
const app = new Koa()
const bodyParser = require('koa-bodyparser')
app.use(bodyParser())
app.use(async(ctx) => {
if (ctx.url === '/' && ctx.method === 'GET') {
let html = `
<h2>This is demo2</h2>
<form method="POST" action="/">
<p>username:</p>
<input name="username">
<p>age:</p>
<input name="age">
<p>website</p>
<input name="website">
<button type="submit">submit</button>
</form>
`
ctx.body = html
} else if (ctx.url === '/' && ctx.method === 'POST') {
let postData = ctx.request.body
ctx.body = postData
} else {
ctx.body = '<h2>404</h2>'
}
})
app.listen(3000, () => {
console.log('demo2 is run')
})
Koa2 路由
Koa2 原生路由的實(shí)現(xiàn)
路由在web中的作用不言而喻,而要先實(shí)現(xiàn)原生路由鸵荠,需要的到地址欄輸入的路徑冕茅,然后再根據(jù)路徑不同進(jìn)行跳轉(zhuǎn)。而在Koa2中蛹找,我們可以用 ctx.requerst.url 來實(shí)現(xiàn)獲取訪問路徑:
const Koa = require('koa')
const app = new Koa()
app.use(async(ctx) => {
const url = ctx.request.url
ctx.body = url
})
app.listen(3000, () => {
console.log('demo3 is run')
})
加入我們的文件結(jié)構(gòu)是這樣的:
├── demo3.js
├── package.json
└── view
├── register.html
├── index.html
└── login.html
我們就可以這樣來實(shí)現(xiàn)原生路由:
const Koa = require('koa')
const fs = require('fs')
const app = new Koa()
function render(page) {
return new Promise((resolve, reject) => {
let viewUrl = `./view/${page}`
fs.readFile(viewUrl, "binary", (err, data) => {
console.log(1)
if (err) {
reject(err)
} else {
resolve(data)
}
})
})
}
async function route(url) {
let view = '404.html'
switch(url) {
case '/':
view = 'index.html'
break
case '/login':
view = 'login.html'
break
case '/register':
view = 'register.html'
break
case '/index':
view = 'index.html'
break
default:
break
}
let html = await render(view)
return html
}
app.use(async(ctx) => {
const url = ctx.request.url
let html = await route(url)
ctx.body = html
})
app.listen(3000, () => {
console.log('demo3 is run')
})
通過上面的代碼姨伤,我們成功實(shí)現(xiàn)了一個(gè)路由切換的功能,但這樣寫無(wú)疑是不夠優(yōu)雅的庸疾,且也只是在原理上的實(shí)現(xiàn)姜挺,不足以應(yīng)付我們?nèi)粘i_發(fā)中所遇到的種種問題。因此我們和上次一樣彼硫,還是需要引入中間件來達(dá)成我們的目標(biāo)炊豪。
koa-router
首先我們需要下載 koa-router 中間件:
cnpm i koa-router --save
然后我們就能通過koa-router來優(yōu)雅的進(jìn)行路由調(diào)換了:
const Koa = require('koa')
const fs = require('fs')
const app = new Koa()
const Router = require('koa-router')
let home = new Router()
home.get('/', async ( ctx ) => {
let html = `
<ul>
<li><a href="/page/helloworld">/page/helloworld</a></li>
<li><a href="/page/404">/page/404</a></li>
</ul>
`
ctx.body = html
})
// 子路由2
let page = new Router()
page.get('/404', async ( ctx )=>{
ctx.body = '404 page!'
}).get('/helloworld', async ( ctx )=>{
ctx.body = 'helloworld page!'
})
// 裝載所有子路由
let router = new Router()
router.use('/', home.routes(), home.allowedMethods())
router.use('/page', page.routes(), page.allowedMethods())
// 加載路由中間件
app.use(router.routes()).use(router.allowedMethods())
app.listen(3000)
console.log('demo4 is run')