koa是由Express原班人马打造的,致力于成为一个更小、更富有表现力、更健壮的Web框架。…
基本用法
搭建最简单的服务
1 | const Koa = require('koa') |
Context对象
Koa提供一个Context对象,表示一次对话的上下文(包括HTTP和HTTP)。通过加工这个对象,就可以控制返回给用户的内容。
常用对象:
Context.response.body:将你想返回给前端的数据赋值给该对象。
Content-Type
Koa返回的mine类型是是text/plain,如果想返回其他类型的内容,可以先用ctx.request.accepts判断一下,客户端希望接受什么数据(根据 HTTP Request 的Accept字段),然后使用ctx.response.type指定返回类型。
路由
通过ctx.request.path可以获取用户请求的路径,由此实现简单的路由。但如果你不是想做一个玩具的话,你是不会用这种方式来实现路由的。
一般来说,我们会借助koa-route
模块来实现路由。
koa-route模块
1 | const route = require('koa-route') |
静态服务
express自身具备提供静态服务的功能,但是到了koa,这个功能被剥离出去,通过koa-static
来实现。1
2
3
4
5const path = require('path')
const serve = require('koa-static')
// 将指定的目录静态呈递出去
const main = serve(path.join(__dirname))
app.use(main)
重定向
ctx.response.redirect()方法可以发出一个302跳转,将用户导向另一个路由。1
2
3
4
5
6const redirect = ctx => {
// 这个方法有点像乾坤大挪移
ctx.response.redirect('/')
ctx.response.body = '<a href="/">Index Page</a>'
}
app.use(route.get('/redirect', redirect))
中间件
中间件大概是koa中,最核心也是最重要的概念了。
中间件栈
多个中间件串联起来就形成了中间件栈。最先添加的中间件最先入栈,一旦在中间件中调用next(),那么代码的执行环境会从当前中间件跳到其栈结构上面的中间件中。到了栈结构顶部的中间件时,顶层的中间件开始出栈,最终回到栈结构底部。
这就类似于我们向天空抛出一颗石子,石子先上升,后掉落的过程。
异步中间件
异步中间件需要将中间件写成async函数。1
2
3
4const main = async function (ctx, next) {
ctx.response.type = 'html'
ctx.response.body = await fs.readFile('./demos/template.html', 'utf8')
}
中间件的合成
koa-compose模块可以将多个中件合成为一个。1
2
3
4
5
6
7
8
9
10const compose = require('koa-compose')
const logger = (ctx, next) => {
console.log(`${Date.now()} ${ctx.request.method} ${ctx.request.url}`)
next()
}
const main = ctx => {
ctx.response.body = 'Hello World'
}
const middlewares = compose([logger, main])
app.use(middlewares)
错误处理
500错误
1 | const main = ctx => { |
404错误
如果将ctx.response.status设置成404,就相当于ctx.throw(404),返回404错误。1
2
3
4const main = ctx => {
ctx.response.status = 404
ctx.response.body = 'Page Not Found'
}
抛出错误
为了方便处理错误,最好使用try…catch将其捕获。但是,为每个中间件都写try…catch太麻烦,我们可以让最里层的中间件,负责所有中间件的错误处理。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15const handler = async (ctx, next) => {
try {
await next()
} catch (err) {
ctx.response.status = err.statusCode || err.status || 500
ctx.response.body = {
message: err.message
}
}
}
const main = ctx => {
ctx.throw(500)
}
app.use(handler)
app.use(main)
error事件的监听
运行过程中一旦出错,Koa会触发一个error事件。监听这个事件,也可以处理错误。1
2
3
4
5
6const main = ctx => {
ctx.throw(500)
}
app.on('error', (err, ctx) =>
console.error('server error', err)
)
释放error事件
需要注意的是,如果错误被try…catch捕获,就不会触发error事件。这时,必须调用ctx.app.emit(),手动释放error事件,才能让监听函数生效。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17const handler = async (ctx, next) => {
try {
await next()
} catch (err) {
ctx.response.status = err.statusCode || err.status || 500
ctx.response.type = 'html'
ctx.response.body = '<p>Something wrong, please contact administrator.</p>'
ctx.app.emit('error', err, ctx)
}
}
const main = ctx => {
ctx.throw(500)
}
app.on('error', function(err) {
console.log('logging error ', err.message)
console.log(err)
})
Web App的功能
Cookies
ctx.cookies用来读写Cookie。1
2
3
4
5
6
7const main = function(ctx) {
// 读cookies
const n = Number(ctx.cookies.get('view') || 0) + 1
// 写cookies
ctx.cookies.set('view', n)
ctx.response.body = n + ' views'
}
表单
koa-body
模块可以用来从POST请求的数据体里面提取键值对。1
2
3
4
5
6
7const koaBody = require('koa-body')
const main = async function(ctx) {
const body = ctx.request.body
if (!body.name) ctx.throw(400, '.name required')
ctx.body = { name: body.name }
}
app.use(koaBody())
文件上传
koa-body
模块还可以用来处理文件上传。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18const os = require('os')
const path = require('path')
const koaBody = require('koa-body')
const main = async function(ctx) {
const tmpdir = os.tmpdir()
const filePaths = []
const files = ctx.request.body.files || {}
for (let key in files) {
const file = files[key]
const filePath = path.join(tmpdir, file.name)
const reader = fs.createReadStream(file.path)
const writer = fs.createWriteStream(filePath)
reader.pipe(writer)
filePaths.push(filePath)
}
ctx.body = filePaths
}
app.use(koaBody({ multipart: true }))
最后
本文大量参考阮一峰的网络日志。原文链接