前言
Koa 2.x 版本是当下最流行的 NodeJS 框架, Koa 2.0 的源码特别精简,不像 Express 封装的功能那么多,所以大部分的功能都是由 Koa 开发团队(同 Express 是一家出品)和社区贡献者针对 Koa 对 NodeJS 的封装特性实现的中间件来提供的,用法非常简单,就是引入中间件,并调用 Koa 的 use 方法使用在对应的位置,这样就可以通过在内部操作 ctx 实现一些功能,我们接下来就讨论常用中间件的实现原理以及我们应该如何开发一个 Koa 中间件供自己和别人使用。
Koa 的洋葱模型介绍
我们本次不对洋葱模型的实现原理进行过多的刨析,主要根据 API 的使用方式及洋葱模型分析中间件是如何工作的。
洋葱模型特点
// 引入 Koaconst Koa = require("koa");// 创建服务const app = new Koa();app.use(async (ctx, next) => { console.log(1); await next(); console.log(2);});app.use(async (ctx, next) => { console.log(3); await next(); console.log(4);});app.use(async (ctx, next) => { console.log(5); await next(); console.log(6);});// 监听服务app.listen(3000);// 1// 3// 5// 6// 4// 2
我们知道 Koa 的 use 方法是支持异步的,所以为了保证正常的按照洋葱模型的执行顺序执行代码,需要在调用 next 的时候让代码等待,等待异步结束后再继续向下执行,所以我们在 Koa 中都是建议使用 async/await 的,引入的中间件都是在 use 方法中调用,由此我们可以分析出每一个 Koa 的中间件都是返回一个 async 函数的。
koa-bodyparser 中间件模拟
想要分析 koa-bodyparser 的原理首先需要知道用法和作用, koa-bodyparser 中间件是将我们的 post 请求和表单提交的查询字符串转换成对象,并挂在 ctx.request.body 上,方便我们在其他中间件或接口处取值,使用前需提前安装。
npm install koa koa-bodyparser
koa-bodyparser 具体用法如下:
koa-bodyparser 的用法
const Koa = require("koa");const bodyParser = require("koa-bodyparser");const app = new Koa();// 使用中间件app.use(bodyParser());app.use(async (ctx, next) => { if (ctx.path === "/" && ctx.method === "POST") { // 使用中间件后 ctx.request.body 属性自动加上了 post 请求的数据 console.log(ctx.request.body); }});app.listen(3000);
根据用法我们可以看出 koa-bodyparser 中间件引入的其实是一个函数,我们把它放在了 use 中执行,根据 Koa 的特点,我们推断出 koa-bodyparser 的函数执行后应该给我们返回了一个 async 函数,下面是我们模拟实现的代码。
文件:my-koa-bodyparser.js
const querystring = require("querystring");module.exports = function bodyParser() { return async (ctx, next) => { await new Promise((resolve, reject) => { // 存储数据的数组 let dataArr = []; // 接收数据 ctx.req.on("data", data => dataArr.push(data)); // 整合数据并使用 Promise 成功 ctx.req.on("end", () => { // 获取请求数据的类型 json 或表单 let contentType = ctx.get("Content-Type"); // 获取数据 Buffer 格式 let data = Buffer.concat(dataArr).toString(); if (contentType === "application/x-www-form-urlencoded") { // 如果是表单提交,则将查询字符串转换成对象赋值给 ctx.request.body ctx.request.body = querystring.parse(data); } else if (contentType === "applaction/json") { // 如果是 json,则将字符串格式的对象转换成对象赋值给 ctx.request.body ctx.request.body = JSON.parse(data); } // 执行成功的回调 resolve(); }); }); // 继续向下执行 await next(); };};
新闻热点
疑难解答
图片精选