创建 Koa 应用
-
利用 http 模块 创建(http 融合的方式,有的需求使用 http 更方便一点)
const Koa = require("koa"); const app = new Koa(); const http = require("http"); const server = http.createServer(app.callback()); server.listen(port, callback);
-
直接创建 Koa 实例
const Koa = require("koa"); const app = new Koa(); app.listen(port, callback);
注册中间件
// 中间件的函数格式
function middleware1(ctx, next){
// ctx 上下文对象
// next 移交给下一个中间件的函数
}
// 注册中间件
app.use(middleware1);
中间件原理
中间件执行顺序
-
示例代码 1
const Koa = require('koa'); const app = new Koa(); app.use(async (ctx, next) => { console.log(1); next(); // 调用 next 表示执行下一个中间件 console.log(2); }) app.use(async (ctx, next) => { console.log(3); next(); console.log(4) }) app.use(async (ctx, next) => { console.log(5) next(); console.log(6) }); app.listen(4000); // 输出: // 1 // 3 // 5 // 6 // 4 // 2
-
示例代码 2
const Koa = require('koa'); const app = new Koa(); const log = () => { return new Promise((resolve, reject) => { setTimeout(() => { console.log('logger'); resolve() }, 3000); }) } app.use(async (ctx, next) => { console.log(1); next(); // 调用 next 表示执行下一个中间件 console.log(2); }) app.use(async (ctx, next) => { console.log(3); await log(); // -- 等待 next(); console.log(4) }) app.use(async (ctx, next) => { console.log(5) next(); console.log(6) }); app.listen(4000); // 输出: // 1 // 3 // 2 // logger // 5 // 6 // 4
-
示例代码 3
const Koa = require('koa'); const app = new Koa(); const log = () => { return new Promise((resolve, reject) => { setTimeout(() => { console.log('logger'); resolve() }, 3000); }) } app.use(async (ctx, next) => { // fn1 console.log(1); await next(); console.log(2); ctx.body = 'hello 1'; // 会取第一个中间件执行完的结果(执行顺序在最后) }) app.use(async (ctx, next) => { console.log(3); await log(); // -- 等待 ctx.body = 'hello 2' next(); console.log(4) }) app.use(async (ctx, next) => { console.log(5) ctx.body = 'hello 3'; next(); console.log(6) }); app.listen(3000); // 界面响应: hello 1
context
-
context 实现了不同应用以及不同请求之间的环境隔离
-
ctx 实现代理
响应流程
当给 body 赋值时, Koa 会将 status 自动赋值为 200 或 204
简化 api
为了方便使用, Koa 将 request 和 response 中的很多成员提取到了 context 中,并使用访问器控制
cookie
Koa 中的加密是利用第三方库 KeyGrip 完成的,该库使用多个秘钥,轮流用它们加密目标字符串,在解密时,选择合适的秘钥进行解密;
这种叫做旋转加密的方式,更加难以破解;
-
Koa 原生支持 cookie ,不需要安装其他中间件
ctx.cookies.set(name, value, [options]); // 设置cookie ctx.cookies.get(name); // 获取cookie
-
Koa 同样支持加密的 cookie
app.keys = ['im a newer secret', 'i like turtle']; // 加密的多个秘钥 ctx.cookies.set(name, value, { signed: true}); // 设置加密的cookie ctx.cookies.get(name, {signed: true}); // 解密cookie
自定义空间
有时,某些中间件希望添加一些额外的信息,以方便后续中间件处理,比如当前登录的用户信息;
Koa 建议把这些信息添加到 ctx.state 中,该属性默认是一个空对象,专门提供给中间件开发者添加额外信息的;
错误处理
利用中间件进行错误处理
为了方便处理错误,最好使用 try…catch 将其捕获;
但是,为每个中间件都写 try…catch 太麻烦,可以让最外层的中间件,负责所有中间件的错误处理;
const Koa = require('koa')
const fs = require('fs')
const { promisify } = require('util')
const readFile = promisify(fs.readFile)
const app = new Koa()
// 在最外层添加异常捕获的中间件
app.use(async (ctx, next) => {
try {
await next()
} catch (err) {
ctx.status = 500
ctx.body = err.message
ctx.app.emit('error', err, ctx)
// ctx.body = '服务端内部错误'
}
})
app.use(async (ctx, next) => {
// next() // 无法捕获后面的异步中间件
// return next() // 可以捕获异步错误
await next() // 可以捕获异步错误
})
app.use(async ctx => {
// 没有这个文件,会报错
const data = await readFile('./dnskjandsa.html')
ctx.type = 'html'
ctx.body = data
})
app.listen(3000, () => { console.log('http://localhost:3000') })
利用 EventEmitter 事件监听进行错误处理
由于 Koa 实现了 EventEmitter ,除了错误处理之外,还可以利用它做别的事情
Koa 实现了 EventEmitter ,因此,它是通过注册事件的方式处理错误的
const Koa = require('koa')
const fs = require('fs')
const { promisify } = require('util')
const readFile = promisify(fs.readFile)
const app = new Koa()
app.use(async ctx => {
// 没有这个文件,会报错
const data = await readFile('./dnskjandsa.html')
ctx.type = 'html'
ctx.body = data
})
app.on('error', err => { console.log('app error', err) })
app.listen(3000, () => { console.log('http://localhost:3000') })
释放 error 事件
需要注意的是,如果错误被 try…catch 捕获,就不会触发 error 事件;
这时,必须调用
ctx.app.emit()
,手动释放 error 事件,才能让监听函数生效;
const Koa = require('koa')
const fs = require('fs')
const { promisify } = require('util')
const readFile = promisify(fs.readFile)
const app = new Koa()
// 在最外层添加异常捕获的中间件
app.use(async (ctx, next) => {
try {
await next()
} catch (err) {
ctx.status = 500
ctx.body = err.message
ctx.app.emit('error', err, ctx)
}
})
app.use(async ctx => {
// 没有这个文件,会报错
const data = await readFile('./dnskjandsa.html')
ctx.type = 'html'
ctx.body = data
})
app.on('error', err => { console.log('app error', err) })
app.listen(3000, () => { console.log('http://localhost:3000') })
Koa👉 介绍
上一篇