92 # express 中的中间件的实现

上一节实现 express 的优化处理,这一节来实现 express 的中间件

中间件的特点:

  • 可以决定是否向下执行
  • 可以拓展属性和方法
  • 可以权限校验
  • 中间件的放置顺序在路由之前

中间件基于路由,只针对路径拦截,下面是中间件的匹配规则:

  1. 路径为 / 表示任何路径都能匹配到
  2. 如果以这个路径开头,则匹配
  3. 和路由的路径一样,也可以匹配

先看 express 的中间件 demo

const express = require("express");
const app = express();app.use("/", (req, res, next) => {if (req.query.kaimo == "313") {next();} else {res.send("没有权限访问");}
});app.get("/", (req, res, next) => {res.end("get okk end");
});
app.post("/", (req, res, next) => {res.end("post okk end");
});app.listen(3000, () => {console.log(`server start 3000`);console.log(`在线访问地址:http://localhost:3000/`);
});

控制台执行下面命令:

curl -v -X POST http://localhost:3000/

然后去访问:http://localhost:3000/

在这里插入图片描述
在这里插入图片描述
下面实现 express 中间件如图:我们需要在 Router 的前面添加中间件,它没有 route 属性,有路径跟 handler
在这里插入图片描述
application.js

const http = require("http");
const Router = require("./router");
const methods = require("methods");
console.log("methods----->", methods);function Application() {}// 调用此方法才开始创建,不是创建应用时直接装载路由
Application.prototype.lazy_route = function () {if (!this._router) {this._router = new Router();}
};methods.forEach((method) => {Application.prototype[method] = function (path, ...handlers) {this.lazy_route();this._router[method](path, handlers);};
});Application.prototype.use = function () {this.lazy_route();this._router.use(...arguments);
};Application.prototype.listen = function () {const server = http.createServer((req, res) => {function done() {res.end(`kaimo-express Cannot ${req.method} ${req.url}`);}this.lazy_route();this._router.handle(req, res, done);});server.listen(...arguments);
};module.exports = Application;

router/index.js

const url = require("url");
const Route = require("./route");
const Layer = require("./layer");
const methods = require("methods");function Router() {// 维护所有的路由this.stack = [];
}Router.prototype.route = function (path) {// 产生 routelet route = new Route();// 产生 layer 让 layer 跟 route 进行关联let layer = new Layer(path, route.dispatch.bind(route));// 每个路由都具备一个 route 属性,稍后路径匹配到后会调用 route 中的每一层layer.route = route;// 把 layer 放到路由的栈中this.stack.push(layer);return route;
};methods.forEach((method) => {Router.prototype[method] = function (path, handlers) {// 1.用户调用 method 时,需要保存成一个 layer 当道栈中// 2.产生一个 Route 实例和当前的 layer 创造关系// 3.要将 route 的 dispatch 方法存到 layer 上let route = this.route(path);// 让 route 记录用户传入的 handler 并且标记这个 handler 是什么方法route[method](handlers);};
});Router.prototype.use = function (path, ...handlers) {// 默认第一个是路径,后面是一个个的方法,路径可以不传if (typeof path === "function") {handlers.unshift(path);path = "/";}// 如果是多个函数需要循环添加层for (let i = 0; i < handlers.length; i++) {let layer = new Layer(path, handlers[i]);// 中间件不需要 route 属性layer.route = undefined;this.stack.push(layer);}
};Router.prototype.handle = function (req, res, out) {console.log("请求到了");// 需要取出路由系统中 Router 存放的 layer 依次执行const { pathname } = url.parse(req.url);let idx = 0;let next = () => {// 遍历完后没有找到就直接走出路由系统if (idx >= this.stack.length) return out();let layer = this.stack[idx++];// 需要判断 layer 上的 path 和当前请求路由是否一致,一致就执行 dispatch 方法if (layer.match(pathname)) {// 中间件没有方法可以匹配if (!layer.route) {layer.handle_request(req, res, next);} else {// 将遍历路由系统中下一层的方法传入// 加速匹配,如果用户注册过这个类型的方法在去执行if (layer.route.methods[req.method.toLowerCase()]) {layer.handle_request(req, res, next);} else {next();}}} else {next();}};next();
};module.exports = Router;

layer.js

function Layer(path, handler) {this.path = path;this.handler = handler;
}Layer.prototype.match = function (pathname) {if (this.path === pathname) {return true;}// 如果是中间件,进行中间件的匹配规则if (!this.route) {if (this.path == "/") {return true;}// /aaaa/b 需要 /aaaa/ 才能匹配上return pathname.startsWith(this.path + "/");}return false;
};
Layer.prototype.handle_request = function (req, res, next) {this.handler(req, res, next);
};
module.exports = Layer;

测试demo

const express = require("./kaimo-express");
const app = express();app.use((req, res, next) => {console.log(1);next();
});
app.use((req, res, next) => {console.log(2);next();
});
app.use((req, res, next) => {console.log(3);next();
});app.get("/", (req, res, next) => {res.end("get okk end");
});
app.post("/", (req, res, next) => {res.end("post okk end");
});app.listen(3000, () => {console.log(`server start 3000`);console.log(`在线访问地址:http://localhost:3000/`);
});

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/139916.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【观察】数字化转型的“下半场”,华为加速行业智能化升级

过去几年数字化转型席卷全球&#xff0c;随着新技术的广泛应用&#xff0c;新的机会和价值正在不断被发现和创造。从某种程度上说&#xff0c;数字化转型不再是“可选项”&#xff0c;而变成了“必选项”。 目前&#xff0c;已经有超过170多个国家和地区制定了各自的数字化相关…

如何使用固态硬盘+硬盘盒子+U盘创造移动双系统

本文背景 这学期上了一节鸟水课《大数据实践》&#xff0c;老师要求扩展硬盘盒&#xff0c;以部署大数据工具进行 机器挖掘等大数据领域工作 参考视频链接&#xff1a;无需启动盘&#xff0c;用虚拟机将ubuntu安装到移动硬盘上_哔哩哔哩_bilibili 项目使用设备 1.绿联&#…

Centos7安装wps无法打开及字体缺失的问题解决

在centos7上安装了最新的wps2019版本的wps-office-11.1.0.11704-1.x86_64.rpm&#xff0c;生成了桌面图标并信任&#xff0c;可以新建文件&#xff0c;但是软件无法打开。在终端执行如下命令&#xff0c;用命令行启动wps&#xff1a; cd /opt/kingsoft/wps-office/office6/ ./…

银行家算法——C语言实现

算法思路 将操作系统看作是银行家&#xff0c;操作系统所拥有的资源就相当于银行家所拥有的资产&#xff0c;进程向操作系统申请资源就相当于资产家向银行贷款&#xff0c;规定资产家在向银行贷款之前&#xff0c;先申明其所贷数额的最大值&#xff0c;申明之后其贷款的数额不…

Vue3+Ts+Vite项目(第十五篇)——tailwindcss安装及使用详解,css原子化如何实现

文章目录 一、装包二、初始化2.1 终端执行如下命令2.2 postcss.config.js 文件中2.3 tailwind.config.js 文件中 三、样式文件3.1 新建 tailwind.css 文件3.2 main.ts 中引入 四、使用4.1 写入类名即可4.2 简单讲解 五、插件5.1 安装 Tailwind CSS IntelliSense5.2 使用效果 六…

执行命令行程序测试自动化

这几天有一个小工具需要做测试&#xff0c;是一个命令行工具&#xff0c;这个命令行工具有点类似mdbg等命令行工具&#xff0c;即程序运行后&#xff0c;在命令行等待用户敲入的命令&#xff0c;处理命令并显示结果&#xff0c;再继续等待用户敲入新的命令。 原来的测试用例都…

技术对比:Flutter vs. 传统桌面应用开发框架

在移动应用开发领域&#xff0c;Flutter已经赢得了广泛的认可和采用&#xff0c;成为了跨平台移动应用开发的瑞士军刀。然而&#xff0c;Flutter的魅力并不仅限于移动平台&#xff0c;它还可以用于开发桌面应用程序&#xff0c;为开发人员提供了一种全新的选择。本文将深入探讨…

2023华为杯数学建模竞赛E题

一、前言 颅内出血&#xff08;ICH&#xff09;是由多种原因引起的颅腔内出血性疾病&#xff0c;既包括自发性出血&#xff0c;又包括创伤导致的继发性出血&#xff0c;诊断与治疗涉及神经外科、神经内科、重症医学科、康复科等多个学科&#xff0c;是临床医师面临的重要挑战。…

只需4步使用Redis缓存优化Node.js应用

介绍 通过API获取数据时&#xff0c;会向服务器发出网络请求&#xff0c;收到响应数据。但是&#xff0c;此过程可能非常耗时&#xff0c;并且可能会导致程序响应时间变慢。 我们使用缓存来解决这个问题&#xff0c;客户端程序首先向API发送请求&#xff0c;将返回的数据存储…

【CANoe/CANalyzer脚本】通过CAPL发送NM帧报文测试网络管理

优质博文推荐阅读&#xff08;单击下方链接&#xff0c;即可跳转&#xff09;&#xff1a; Vector工具链 CAN Matrix DBC CAN Matrix Arxml 一、在CANoe/CANalyzer中创建Program Node 在Measurement Setup中&#xff0c;创建Program Node&#xff1b; 接着导入下面的Demo:N…

go 语言 负载均衡 为反向代理添加负载均衡 拓展ReverseProxy

随机负载 package randomimport ("errors""math/rand" )type RandomBalance struct {//当前索引curIndex int//存储负载均衡的地址rss []string//观察主体//conf LoadBalanceConf }func (r *RandomBalance) Add(params ...string) error {if len(params) …

什么是WhatsApp群发,WhatsApp协议,WhatsApp云控

那么WhatsApp群控云控可以做什么呢&#xff1f; 1、获客 自动化引流&#xff0c;强大的可控性&#xff0c;产品快速拓客 2、导流 一键式傻瓜化自动加好友&#xff0c;群发&#xff0c;朋友圈营销 3、群控 一键式拉群好友&#xff0c;建群&#xff0c;进群 …

Android 12修改usb tp触摸唤醒

前言 Android 12系统休眠时&#xff0c;需要不管接什么型号usb tp都能够触摸唤醒。 Android12系统中&#xff0c;usb tp要能够触摸唤醒&#xff0c;需要在frameworks/native/services/inputflinger/reader/mapper/TouchInputMapper.cpp中将mParameters.wake赋值为true&#xff…

Spring Boot2.7生成用于登录的图片验证码

先在 pom.xml 注入依赖 <dependency><groupId>com.github.penggle</groupId><artifactId>kaptcha</artifactId><version>2.3.2</version> </dependency>然后 需要在配置文件中声明一下DefaultKaptcha 的 bean对象 然后 我们…

Python之网络编程

一、网络编程 互联网时代,现在基本上所有的程序都是网络程序,很少有单机版的程序了。 网络编程就是如何在程序中实现两台计算机的通信。 Python语言中,提供了大量的内置模块和第三方模块用于支持各种网络访问,而且Python语言在网络通信方面的优点特别突出,远远领先其他语…

解决MySQL8.0本地计算机上的MySQL服务启动后停止没有报告任何错误

1.启动MySQL的错误信息如下 &#xff08;1&#xff09;“本地计算机上的MySQL服务启动后停止。某些服务在未由其他服务或程序使用时将自动停止。” &#xff08;2&#xff09;又在PowerShell中运行"net start MySQL"&#xff0c;服务启动失败。“MySQL 服务无法启…

数据分享|R语言生态学种群空间点格局分析:聚类泊松点过程对植物、蚂蚁巢穴分布数据可视化...

全文链接 :https://tecdat.cn/?p33676 点模式分析&#xff08;点格局分析&#xff09;是一组用于分析空间点数据的技术。在生态学中&#xff0c;这种类型的分析可能在客户的几个情境下出现&#xff0c;但对数据生成方式做出了特定的假设&#xff0c;因此让我们首先看看哪些生态…

【LeetCode】——双指针(快慢指针)/多指针

个人主页 代码仓库 C语言专栏 初阶数据结构专栏 Linux专栏 前言 大家好&#xff01;这是新开的LeetCode刷题专栏&#xff0c;这个专栏不只是随便的拿一些我练过的题讲解&#xff0c;而是总结我在刷题中的一些方法适用于一大类的题&#xff0c;是给大家提供这一大类题的解题…

爬虫使用Selenium生成Cookie

在爬虫的世界中&#xff0c;有时候我们需要模拟登录来获取特定网站的数据&#xff0c;而使用Selenium登录并生成Cookie是一种常见且有效的方法。本文将为你介绍如何使用Selenium进行登录&#xff0c;并生成Cookie以便后续的爬取操作。让我们一起探索吧&#xff01; 一、Seleni…