文章目录
1. koa
Koa应用于node.js的下一代web框架
Koa它使得中间件能够按照特定的顺序执行,同时支持异步操作。这对于实现诸如日志记录、身份验证、请求处理、响应处理等功能非常有用。
koa和express是同一个团队开发的
- koa旨在为web应用程序和api提供更小,更丰富和更强大的能力
- 相对于express具有更强的异步出来能力
- koa的核心代码只有1600+行,是一个轻量级的web开发框架,可以根据需要安装和使用中间件
1.Koa简介
Koa,作为Node.js平台上一个轻量级的web开发框架,以其独特的中间件流程和洋葱模型而闻名。这个模型不仅简化了异步编程的复杂性,还提供了灵活的中间件管理机制,使得开发者能够轻松构建可扩展的web应用。
Koa是一个由Express幕后团队打造的新web框架,它致力于成为更小、更富有表现力、更健壮的基石。Koa并没有捆绑任何中间件,而是提供了一套优雅的方法,帮助开发者快速编写服务端应用程序。其核心特性包括将Node.js原生的请求(req)和响应(res)对象封装为上下文(context)对象,以及基于async/await的中间件洋葱模型机制。
2.Koa工作原理
洋葱模型的核心原理是借助compose方法。
- 当执行app.listen()监听端口时,Koa内部会调用http模块的createServer方法,并传入一个内置的回调函数。
- 回调函数将使用compose函数来处理中间件数组。
- compose函数通过递归调用中间件,实现了洋葱模型的执行流程。
2.Koa源码分析
compose函数的实现是洋葱模型的核心。它接收一个中间件数组,并返回一个函数,该函数接收上下文(context)和next函数作为参数。在compose函数内部,通过递归调用dispatch函数来实现中间件的执行流程。每当next()被调用时,dispatch函数会递增索引并继续调用下一个中间件。
2. Koa使用
1.Koa的安装
Koa 需要 node v12 或更高版本才能支持 ES2015 和异步函数。
// 安装指令
npm i koa
// 服务启动指令 服务文件名 koaApp.js
node ./koaApp.js
2.Koa的应用程序
const Koa = require('koa');
const app = new Koa();
app.use(async ctx => {
ctx.body = 'Hello World';
});
app.listen(8081, function() {
console.log('服务启动!')
});
3.Koa的属性
属性/方法名 | 默认值 | 说明 |
---|---|---|
app.env | NODE_ENV 或 development | 项目环境 |
app.keys | 设置签名的 Cookie 密钥 | |
app.proxy | false | 当真正的代理头字段将被信任时 |
app.subdomainOffset | 2 | 偏移量 |
app.proxyIpHeader | X-Forwarded-For | 代理 ip 消息头 |
app.maxIpsCount | 从代理 ip 消息头读取的最大 ip限制数, 默认为 0 (代表无限) | |
app.listen() | 启动服务器并监听特定端口 | |
app.callback() | 返回适用于 http.createServer() 方法的回调函数来处理请求 | |
app.use() | 将给定的中间件方法添加到此应用程序 | |
app.context | 为 ctx 添加其他属性 |
打印 new Koa()
- app.keys
需要将signed选项设置为true,这样Koa才会使用app.keys中的密钥进行加密。const Koa = require('koa'); const app = new Koa(); app.keys = ['im a newer secret', 'i like turtle']; app.use(async ctx => { ctx.cookies.set('name', 'tobi', { signed: true }); //一个名为name的普通cookie和一个名为name.sig的签名cookie。 ctx.body = 'Hello World'; }); app.listen(8081, function() { console.log('服务启动!') });
- app.proxy
const Koa = require('koa'); const app = new Koa({ proxy: true });
const Koa = require('koa'); const app = new Koa(); app.proxy = true;
- app.listen()
Koa 应用程序不是 HTTP 服务器的1对1展现。 可以将一个或多个 Koa 应用程序安装在一起以形成具有单个HTTP服务器的更大应用程序。
app.listen(…) 方法只是以下http.createServer(app.callback())方法的语法糖
可以将同一个应用程序同时作为 HTTP 和 HTTPS 或多个地址:const Koa = require('koa'); const app = new Koa(); const http = require('http'); app.use(async ctx => { ctx.body = 'Hello World'; }); http.createServer(app.callback()).listen(8081, function() { console.log('服务启动!') });
const Koa = require('koa'); const app = new Koa(); const http = require('http'); app.use(async ctx => { ctx.body = 'Hello World'; }); http.createServer(app.callback()).listen(8081, function() { console.log('服务启动!端口号:8081') }); http.createServer(app.callback()).listen(3001, function() { console.log('服务启动!端口号:3001') });
- app.callback()
返回适用于 http.createServer() 方法的回调函数来处理请求。 - app.use()
将给定的中间件方法添加到此应用程序。app.use() 返回 this, 因此可以链式表达.app.use(someMiddleware) .use(someOtherMiddleware) .listen(3000)
- app.context
app.context 是从其创建 ctx 的原型,可以通过编辑 app.context 为 ctx 添加其他属性注:
- ctx 上的许多属性都是使用 getter ,setter 和 Object.defineProperty() 定义的。你只能通过在 app.context 上使用 Object.defineProperty() 来编辑这些属性(不推荐)。
- 安装的应用程序目前使用其父级的 ctx 和设置。 因此,安装的应用程序只是一组中间件。
4.Koa错误处理
添加一个 “error” 事件侦听器进行错误处理。
app.on('error', (err, ctx) => {
consolog.error('server error', err)
ctx.status = 401;
ctx.body = err.message;
});
5.Koa的上下文Context
Koa Context 将 node 的 request 和 response 对象封装到单个对象中,为编写 Web 应用程序和 API 提供了许多有用的方法。
Context 具体方法和访问器
方法/访问器 | 说明 |
---|---|
ctx.req | Node 的 request 对象. |
ctx.res | Node 的 response 对象 |
ctx.request | Koa的 request 对象. |
ctx.response | Koa的 response 对象 |
ctx.state | 推荐的命名空间,用于通过中间件传递信息和你的前端视图。 |
ctx.app | 应用程序实例引用 |
ctx.app.emit | ctx.app.emit 发出一个类型由第一个参数定义的事件。对于每个事件,您可以连接 “listeners”,这是在发出事件时调用的函数 |
ctx.cookies.get(name, [options]) | 通过 options 获取 cookie name |
ctx.cookies.set(name, value, [options]) | 通过 options 设置 cookie name 的 value |
ctx.throw([status], [msg], [properties]) | 用来抛出一个包含 .status 属性错误的帮助方法, |
ctx.assert(value, [status], [msg], [properties]) | 当 !value 时抛出一个类似 .throw 错误的帮助方法 |
ctx.respond | 想要写入原始的 res 对象而不是让 Koa 处理你的 response,请使用此参数 |
-
ctx.cookies.get(name, [options])
从请求的标头中提取具有给定名称的 Cookie。如果存在此类 Cookie,则返回其值。否则,不会返回任何内容。Cookie
{ signed: true }可以选择作为第二个参数 options 传递。在这种情况下,将获取签名 Cookie(以附加后缀结尾的同名 Cookie)。如果不存在此类 Cookie,则不会返回任何内容。.sig
-
ctx.cookies.set(name, value, [options])
const Koa = require('koa'); const KeyGrip = require('keygrip'); const app = new Koa(); app.use(async ctx => { app.keys = new KeyGrip(['im a newer secret', 'i like turtle'], 'sha256'); ctx.cookies.set( 'cid', '11111111', { domain: 'localhost:8081', // 写cookie所在的域名 path: '/', // 写cookie所在的路径 maxAge: 10 * 60 * 1000, // cookie有效时长 expires: new Date('2025-06-15'), // cookie失效时间 httpOnly: true, // 是否只用于http请求中获取 overwrite: false, // 是否允许重写,默认false signed: true, // 一个布尔值, 表示是否要对 cookie 进行签名 (默认为 false). 如果为 true, 则还会发送另一个后缀为 .sig 的同名 cookie, secure: false, // cookie 是否仅通过 HTTPS 发送 (HTTP 下默认为 false, HTTPS 下默认为 true) sameSite: false,//一个布尔值或字符串, 表示该 cookie 是否为 "相同站点" cookie (默认为 false). 可以设置为 'strict', 'lax', 'none', 或 true (映射为 'strict'). } ) const cid = ctx.cookies.get('cid', { signed: true }) console.log(cid) ctx.body = 'Hello World'; }); app.listen(8081, function() { console.log('服务启动!端口号:8081') });
-
ctx.throw([status], [msg], [properties])
用来抛出一个包含 .status 属性错误的帮助方法,其默认值为 500const user = { id: 123, name: 'Alice', email: 'alice@example.com' } ctx.throw(400, 'name required', { user: user });
ctx.throw(400, 'name required', { user: user });
等效于const err = new Error('name required'); err.status = 400; err.expose = true; throw err;
请求参数和响应参数属性可查看官网
3. koa-router路由
Koa框架并没有直接提供app.get, app.put, app.post这样的方法来定义路由。在Koa中,我们使用koa-router这个中间件来实现路由的功能。
1. koa-router的使用
- 安装指令
npm install koa-router
- 项目中使用
const Koa = require('koa'); const app = new Koa(); // 引入Router var Router = require('koa-router'); // 实例化 Router var router = new Router(); // 定义路由 router.get('/', (ctx, next) => { ctx.body = 'Hello World!'; }); app.listen(8081, function() { console.log('服务启动!端口号:8081') });
- 设置路径前缀
实例化时传参实现
实例化之后实现var router = new Router({ prefix: '/api' });
var router = new Router(); router.prefix('/api')
- allowedMethods方法
单独的中间件,用于响应 包含允许的方法的标头。
options参数简介属性 类型 说明 throw Boolean throw 错误 notImplemented function throw 返回值来代替默认的 NotImplemented 错误 methodNotAllowed function 抛出返回值来代替默认的 MethodNotAllowed 错误
2. koa-router的参数解析
- 参数解析:params和query
const Koa = require('koa'); const app = new Koa(); // 引入Router var Router = require('koa-router'); var router = new Router(); router.get('/login_get/:id', (ctx, next) => { let str = JSON.stringify(ctx.request.params) let str1 = JSON.stringify(ctx.request.query) ctx.body = `params: ${str}; query: ${str1}` ; }); app .use(router.routes()) .use(router.allowedMethods()); app.listen(8081, function() { console.log('服务启动!端口号:8081') });
- 参数解析:query
const Koa = require('koa'); const app = new Koa(); // 引入Router var Router = require('koa-router'); var router = new Router(); router.get('/login', (ctx, next) => { let str = JSON.stringify(ctx.request.query) ctx.body = str; }); app .use(router.routes()) .use(router.allowedMethods()); app.listen(8081, function() { console.log('服务启动!端口号:8081') });
- 参数解析:json和formData
const Koa = require('koa'); const app = new Koa(); const bodyParser = require('koa-bodyparser'); const multer = require('koa-multer'); const upload = multer(); app.use(bodyParser()); app.use(upload.any()); // 引入Router var Router = require('koa-router'); // 实例化 Router var router = new Router(); // 定义路由 router.post('/login_POST', (ctx, next) => { let str = JSON.stringify(ctx.request.body) ctx.body = str; }); router.post('/login_post_json', (ctx, next) => { let str = JSON.stringify(ctx.request.body) ctx.body = str; }); // 使用路由中间件 app .use(router.routes()) .use(router.allowedMethods()); app.listen(8081, function() { console.log('服务启动!端口号:8081') });
3. koa上传文件
使用koa-multer中间件来处理文件上传。koa-multer是multer的一个为Koa提供的中间件,而multer是一个用于处理multipart/form-data类型数据(主要是文件上传)的中间件,它适用于Express,也可以在Koa中使用。
const Koa = require('koa');
const app = new Koa();
const multer = require('koa-multer');
const storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, '/uploads') // 确保这个文件夹已经存在,物理路径
},
filename: function (req, file, cb) {
cb(null, file.fieldname + '-' + Date.now())
}
})
const upload = multer({ storage: storage });
var Router = require('koa-router');
var router = new Router();
router.post('/upload', upload.single('file'), (ctx, next) => {
console.log(ctx.req.file)
ctx.body = 'str';
});
// 使用路由中间件
app
.use(router.routes())
.use(router.allowedMethods());
app.listen(8081, function() {
console.log('服务启动!端口号:8081')
});
<form action="http://127.0.0.1:8081/upload" method="post" enctype="multipart/form-data">
<input type="file" name="file" /> <br>
<input type="submit" value="Submit" />
</form>
4. 嵌套路由
路由对象上又继续构建其他子路由对象
var router = new Router(); // 总路由
let userRouter = new Router();//创建一个用户路由对象
let classRou = new Router();//创建一个企业用户路由对象
classRou.get('/show',async ctx=>{
ctx.body = '班级';//企业用户方法
})
let admin = new Router();//创建个人用户路由对象
admin.get('/show',async ctx=>{
ctx.body = "个人";//个人路由对象方法
})
userRouter.use('/classInfo',classRou.routes());
userRouter.use('/admin',admin.routes());
router.use('/user', userRouter.routes());
// 使用路由中间件
app
.use(router.routes())
.use(router.allowedMethods());
app.listen(8081, function() {
console.log('服务启动!端口号:8081')
});