一、起步
首先创建一个文件夹,然后初始化 package.json :
npm init -y
安装koa2:
cnpm i koa --save
在文件目录下新建一个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")
然后运行这个文件:
nodemon index.js
然后我们就能在后台看见这个:
demo is run
然后打开浏览器,输入 http://127.0.0.1:1996就可以看见这个了:
这样我们就搭建好了最简单的web服务器。但除了这些,有一点需要知道的是,在koa2中,async函数已经大规模使用了,它很好的处理了异步的逻辑,所以学习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()
上面的代码执行起来就是这样的:
start
1s later
end
2s later
它很好的解决了异步的一些麻烦,且写出来的代码的可读性也非常好。
二、请求数据获取
2.1 Get请求的接收
在Koa2中GET请求可以通过 request 接受收,但接受的方式有两种:
- query:返回的是格式化后的参数对象
- querystring:返回的请求字符串
我们可以由两种方式来获取GET请求,一种是通过 ctx.request 来获取GET请求,一种则是直接在ctx中得到GET请求:
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")
})
然后我们在浏览器中输入 http://127.0.0.1:3000?user=srtian&age=18 来访问页面就可以看到这个(这是经过美化的表现):
2.2 POST请求的接收
在 Koa2 中,没有给对于 POST 请求的处理封装方便的获取参数的方法,需要通过通过解析上下文 context 中的元素 node.js 请求对象 req 来获取。因此获取POST请求的步骤可以理解为以下三步:
- 解析上下文 ctx 中的原生 node.js 对象 req。
- 将POST表单数据解析成 query string 字符串。
- 将字符串转换成 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/:
完善信息后,点击submit:
koa-bodyparser中间件
显然上面的 POST 请求的接受非常麻烦,至少对我而言,徒手写个这样的轮子在不查资料的情况下是做不到的,而这样的轮子当然也有人来做,koa-bodyparser就是一个造好的轮子。我们在koa中把这种轮子就叫做中间件。对于POST请求的处理,koa-bodyparser中间件可以把koa2上下文的formData数据解析到ctx.request.body中。
首先我们要安装中间件:
cnpm i koa-bodyparser@3 --save
然后我们就能非常轻松愉快的使用这个中间件来改造我们上面的代码了:
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 原生路由的实现
路由在web中的作用不言而喻,而要先实现原生路由,需要的到地址栏输入的路径,然后再根据路径不同进行跳转。而在Koa2中,我们可以用 ctx.requerst.url 来实现获取访问路径:
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')
})
加入我们的文件结构是这样的:
├── demo3.js
├── package.json
└── view
├── register.html
├── index.html
└── login.html
我们就可以这样来实现原生路由:
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')
})
通过上面的代码,我们成功实现了一个路由切换的功能,但这样写无疑是不够优雅的,且也只是在原理上的实现,不足以应付我们日常开发中所遇到的种种问题。因此我们和上次一样,还是需要引入中间件来达成我们的目标。
koa-router
首先我们需要下载 koa-router 中间件:
cnpm i koa-router --save
然后我们就能通过koa-router来优雅的进行路由调换了:
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')