前言
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(); };};
新聞熱點
疑難解答
圖片精選