登录
注册
开源
企业版
高校版
搜索
帮助中心
使用条款
关于我们
开源
企业版
高校版
私有云
模力方舟
AI 队友
登录
注册
Gitee 2025 年度开源项目评选中
代码拉取完成,页面将自动刷新
捐赠
捐赠前请先登录
取消
前往登录
扫描微信二维码支付
取消
支付完成
支付提示
将跳转至支付宝完成支付
确定
取消
Watch
不关注
关注所有动态
仅关注版本发行动态
关注但不提醒动态
3
Star
45
Fork
21
DreamCoders
/
CoderGuide
代码
Issues
1169
Pull Requests
0
Wiki
统计
流水线
服务
JavaDoc
PHPDoc
质量分析
Jenkins for Gitee
腾讯云托管
腾讯云 Serverless
悬镜安全
阿里云 SAE
Codeblitz
SBOM
我知道了,不再自动展开
更新失败,请稍后重试!
移除标识
内容风险标识
本任务被
标识为内容中包含有代码安全 Bug 、隐私泄露等敏感信息,仓库外成员不可访问
说说对中间件概念的理解,如何封装 node 中间件?
待办的
#IAG9KV
陌生人
拥有者
创建于
2024-07-29 16:04
<h2>一、是什么</h2><p>中间件(Middleware)是介于应用系统和系统软件之间的一类软件,它使用系统软件所提供的基础服务(功能),衔接网络上应用系统的各个部分或不同的应用,能够达到资源共享、功能共享的目的</p><p>在<code>NodeJS</code>中,中间件主要是指封装<code>http</code>请求细节处理的方法</p><p>例如在<code>express</code>、<code>koa</code>等<code>web</code>框架中,中间件的本质为一个回调函数,参数包含请求对象、响应对象和执行下一个中间件的函数</p><p><img src="https://static.ecool.fun//article/a76490e1-1167-476a-aadd-cbe205d6e962.png" alt="" data-href="" style=""/></p><p>在这些中间件函数中,我们可以执行业务逻辑代码,修改请求和响应对象、返回响应数据等操作</p><h2>二、封装</h2><p><code>koa</code>是基于<code>NodeJS</code>当前比较流行的<code>web</code>框架,本身支持的功能并不多,功能都可以通过中间件拓展实现。通过添加不同的中间件,实现不同的需求,从而构建一个 <code>Koa</code> 应用</p><p><code>Koa</code> 中间件采用的是洋葱圈模型,每次执行下一个中间件传入两个参数:</p><ul><li>ctx :封装了request 和 response 的变量</li><li>next :进入下一个要执行的中间件的函数</li></ul><p>下面就针对<code>koa</code>进行中间件的封装:</p><p><code>Koa</code>的中间件就是函数,可以是<code>async</code> 函数,或是普通函数</p><pre><code class="language-js">// async 函数 app.use(async (ctx, next) => { const start = Date.now(); await next(); const ms = Date.now() - start; console.log(`${ctx.method} ${ctx.url} - ${ms}ms`); }); // 普通函数 app.use((ctx, next) => { const start = Date.now(); return next().then(() => { const ms = Date.now() - start; console.log(`${ctx.method} ${ctx.url} - ${ms}ms`); }); });</code></pre><p>下面则通过中间件封装<code>http</code>请求过程中几个常用的功能:</p><h3>token校验</h3><pre><code class="language-js">module.exports = (options) => async (ctx, next) { try { // 获取 token const token = ctx.header.authorization if (token) { try { // verify 函数验证 token,并获取用户相关信息 await verify(token) } catch (err) { console.log(err) } } // 进入下一个中间件 await next() } catch (err) { console.log(err) } }</code></pre><h3>日志模块</h3><pre><code class="language-js">const fs = require('fs') module.exports = (options) => async (ctx, next) => { const startTime = Date.now() const requestTime = new Date() await next() const ms = Date.now() - startTime; let logout = `${ctx.request.ip} -- ${requestTime} -- ${ctx.method} -- ${ctx.url} -- ${ms}ms`; // 输出日志文件 fs.appendFileSync('./log.txt', logout + '\n') }</code></pre><p><code>Koa</code>存在很多第三方的中间件,如<code>koa-bodyparser</code>、<code>koa-static</code>等</p><p>下面再来看看它们的大体的简单实现:</p><h3>koa-bodyparser</h3><p><code>koa-bodyparser</code> 中间件是将我们的 <code>post</code> 请求和表单提交的查询字符串转换成对象,并挂在 <code>ctx.request.body</code> 上,方便我们在其他中间件或接口处取值</p><pre><code class="language-js">// 文件: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(); }; };</code></pre><h3>koa-static</h3><p><code>koa-static</code> 中间件的作用是在服务器接到请求时,帮我们处理静态文件</p><pre><code class="language-js">const fs = require("fs"); const path = require("path"); const mime = require("mime"); const { promisify } = require("util"); // 将 stat 和 access 转换成 Promise const stat = promisify(fs.stat); const access = promisify(fs.access) module.exports = function (dir) { return async (ctx, next) => { // 将访问的路由处理成绝对路径,这里要使用 join 因为有可能是 / let realPath = path.join(dir, ctx.path); try { // 获取 stat 对象 let statObj = await stat(realPath); // 如果是文件,则设置文件类型并直接响应内容,否则当作文件夹寻找 index.html if (statObj.isFile()) { ctx.set("Content-Type", `${mime.getType()};charset=utf8`); ctx.body = fs.createReadStream(realPath); } else { let filename = path.join(realPath, "index.html"); // 如果不存在该文件则执行 catch 中的 next 交给其他中间件处理 await access(filename); // 存在设置文件类型并响应内容 ctx.set("Content-Type", "text/html;charset=utf8"); ctx.body = fs.createReadStream(filename); } } catch (e) { await next(); } } }</code></pre><h2>三、总结</h2><p>在实现中间件时候,单个中间件应该足够简单,职责单一,中间件的代码编写应该高效,必要的时候通过缓存重复获取数据</p><p><code>koa</code>本身比较简洁,但是通过中间件的机制能够实现各种所需要的功能,使得<code>web</code>应用具备良好的可拓展性和组合性</p><p>通过将公共逻辑的处理编写在中间件中,可以不用在每一个接口回调中做相同的代码编写,减少了冗杂代码,过程就如装饰者模式</p>
<h2>一、是什么</h2><p>中间件(Middleware)是介于应用系统和系统软件之间的一类软件,它使用系统软件所提供的基础服务(功能),衔接网络上应用系统的各个部分或不同的应用,能够达到资源共享、功能共享的目的</p><p>在<code>NodeJS</code>中,中间件主要是指封装<code>http</code>请求细节处理的方法</p><p>例如在<code>express</code>、<code>koa</code>等<code>web</code>框架中,中间件的本质为一个回调函数,参数包含请求对象、响应对象和执行下一个中间件的函数</p><p><img src="https://static.ecool.fun//article/a76490e1-1167-476a-aadd-cbe205d6e962.png" alt="" data-href="" style=""/></p><p>在这些中间件函数中,我们可以执行业务逻辑代码,修改请求和响应对象、返回响应数据等操作</p><h2>二、封装</h2><p><code>koa</code>是基于<code>NodeJS</code>当前比较流行的<code>web</code>框架,本身支持的功能并不多,功能都可以通过中间件拓展实现。通过添加不同的中间件,实现不同的需求,从而构建一个 <code>Koa</code> 应用</p><p><code>Koa</code> 中间件采用的是洋葱圈模型,每次执行下一个中间件传入两个参数:</p><ul><li>ctx :封装了request 和 response 的变量</li><li>next :进入下一个要执行的中间件的函数</li></ul><p>下面就针对<code>koa</code>进行中间件的封装:</p><p><code>Koa</code>的中间件就是函数,可以是<code>async</code> 函数,或是普通函数</p><pre><code class="language-js">// async 函数 app.use(async (ctx, next) => { const start = Date.now(); await next(); const ms = Date.now() - start; console.log(`${ctx.method} ${ctx.url} - ${ms}ms`); }); // 普通函数 app.use((ctx, next) => { const start = Date.now(); return next().then(() => { const ms = Date.now() - start; console.log(`${ctx.method} ${ctx.url} - ${ms}ms`); }); });</code></pre><p>下面则通过中间件封装<code>http</code>请求过程中几个常用的功能:</p><h3>token校验</h3><pre><code class="language-js">module.exports = (options) => async (ctx, next) { try { // 获取 token const token = ctx.header.authorization if (token) { try { // verify 函数验证 token,并获取用户相关信息 await verify(token) } catch (err) { console.log(err) } } // 进入下一个中间件 await next() } catch (err) { console.log(err) } }</code></pre><h3>日志模块</h3><pre><code class="language-js">const fs = require('fs') module.exports = (options) => async (ctx, next) => { const startTime = Date.now() const requestTime = new Date() await next() const ms = Date.now() - startTime; let logout = `${ctx.request.ip} -- ${requestTime} -- ${ctx.method} -- ${ctx.url} -- ${ms}ms`; // 输出日志文件 fs.appendFileSync('./log.txt', logout + '\n') }</code></pre><p><code>Koa</code>存在很多第三方的中间件,如<code>koa-bodyparser</code>、<code>koa-static</code>等</p><p>下面再来看看它们的大体的简单实现:</p><h3>koa-bodyparser</h3><p><code>koa-bodyparser</code> 中间件是将我们的 <code>post</code> 请求和表单提交的查询字符串转换成对象,并挂在 <code>ctx.request.body</code> 上,方便我们在其他中间件或接口处取值</p><pre><code class="language-js">// 文件: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(); }; };</code></pre><h3>koa-static</h3><p><code>koa-static</code> 中间件的作用是在服务器接到请求时,帮我们处理静态文件</p><pre><code class="language-js">const fs = require("fs"); const path = require("path"); const mime = require("mime"); const { promisify } = require("util"); // 将 stat 和 access 转换成 Promise const stat = promisify(fs.stat); const access = promisify(fs.access) module.exports = function (dir) { return async (ctx, next) => { // 将访问的路由处理成绝对路径,这里要使用 join 因为有可能是 / let realPath = path.join(dir, ctx.path); try { // 获取 stat 对象 let statObj = await stat(realPath); // 如果是文件,则设置文件类型并直接响应内容,否则当作文件夹寻找 index.html if (statObj.isFile()) { ctx.set("Content-Type", `${mime.getType()};charset=utf8`); ctx.body = fs.createReadStream(realPath); } else { let filename = path.join(realPath, "index.html"); // 如果不存在该文件则执行 catch 中的 next 交给其他中间件处理 await access(filename); // 存在设置文件类型并响应内容 ctx.set("Content-Type", "text/html;charset=utf8"); ctx.body = fs.createReadStream(filename); } } catch (e) { await next(); } } }</code></pre><h2>三、总结</h2><p>在实现中间件时候,单个中间件应该足够简单,职责单一,中间件的代码编写应该高效,必要的时候通过缓存重复获取数据</p><p><code>koa</code>本身比较简洁,但是通过中间件的机制能够实现各种所需要的功能,使得<code>web</code>应用具备良好的可拓展性和组合性</p><p>通过将公共逻辑的处理编写在中间件中,可以不用在每一个接口回调中做相同的代码编写,减少了冗杂代码,过程就如装饰者模式</p>
评论 (
0
)
登录
后才可以发表评论
状态
待办的
待办的
进行中
已完成
已关闭
负责人
未设置
标签
Node
未设置
标签管理
里程碑
未关联里程碑
未关联里程碑
Pull Requests
未关联
未关联
关联的 Pull Requests 被合并后可能会关闭此 issue
分支
未关联
未关联
master
开始日期   -   截止日期
-
置顶选项
不置顶
置顶等级:高
置顶等级:中
置顶等级:低
优先级
不指定
严重
主要
次要
不重要
参与者(1)
1
https://gitee.com/DreamCoders/CoderGuide.git
git@gitee.com:DreamCoders/CoderGuide.git
DreamCoders
CoderGuide
CoderGuide
点此查找更多帮助
搜索帮助
Git 命令在线学习
如何在 Gitee 导入 GitHub 仓库
Git 仓库基础操作
企业版和社区版功能对比
SSH 公钥设置
如何处理代码冲突
仓库体积过大,如何减小?
如何找回被删除的仓库数据
Gitee 产品配额说明
GitHub仓库快速导入Gitee及同步更新
什么是 Release(发行版)
将 PHP 项目自动发布到 packagist.org
评论
仓库举报
回到顶部
登录提示
该操作需登录 Gitee 帐号,请先登录后再操作。
立即登录
没有帐号,去注册