深入理解express框架的匹配路由机制

news/2024/4/24 4:25:30/

现在node的web框架有很多,除express 还有koa egg等等。 但它们本质上还是基于原生node框架的http。其实框架都大差不差,主要是观摩和学习。本篇文章主要记录下自己在node爬坑之路上的经历和收获~

本文主要实现express的功能之一, 匹配路由

  • 匹配简单静态路由
  • 匹配动态路由

首先我们看一下express:

const express = require('express');let app = new express();app.get('/',(req,res)=>{res.end('home Page.');
});app.get('/center',(req,res)=>{res.end('center Page.');
});/** 匹配到动态路由 获取路由参数并返回 */app.get('/product/:id/:name',(req,res)=>{res.end(JSON.stringify(req.params));});/** 当以上路径都没有匹配成功时 返回404 */
app.all('*',(req,res)=>{res.end('404');
});let port = 3000;app.listen(port,()=>{console.log(`Server is start on port ${port}`);
});

ok.代码很简单。引入express,new了个express实例,写了几个路由,最后启了本地服务。

代码第二行 我们把引入的express 给new出来,说明express内部返回的是一个function。

好.麻雀虽小 五脏俱全,我们今天就来实现express的这些功能。

let http = require('http'); /** express基于http */
let url = require('url'); /** 用来解析请求的路径 */
/** express引入了methods 它的作用是返回各种的请求方法 */
let methods = require('methods');function application(){/** 1 express返回了一个函数 * 这个函数就是http.createServer的监听函数*/let app = (req,res) => {/** 1.1 url模块解析 拿到请求路径 比如 /user */let { pathname } = url.parse(req.url);/** 1.2 拿到请求方法 方法是大写 记得转换为小写 */let requestMethod = req.method.toLowerCase();/** 1.3 通过拿到的路径和方法 在之前定义好的路由数组routes中 循环去匹配 */for(let i = 0; i < app.routes.length; i++){/** 1.4 解构 拿到每一个路由的 路径 方法 回调 */let { path, method, cb } = app.routes[i];if((pathname===path||path==='*') && (requestMethod===method)||method==='all'){/** 1.5 如果匹配到 返回回调并执行 */return cb(req,res);}}/** 1.6 没有匹配到任何路由 */res.end(`Cannot found ${pathname}/${requestMethod}`);}/** 2 定义一个存放所有路由的数组 */app.routes = [];/** 2.1 往methods数组中添加一个方法 all  并循环数组 */[...methods,'all'].forEach((method)=>{app[method] = function(path,cb){/** 2.2 先将每个请求的路由地址 方法和回调保存起来 * path:路径  method:方法   cb:回调*/let layer = { path, method, cb };app.routes.push(layer);}});/** 3 监听端口 */app.listen = function(...arguments){/** 3.1 利用http的createServer方法 将app传进去 */let server = http.createServer(app);server.listen(...arguments);}return app;
}/** 4 将方法导出出去 */
module.exports = application;

代码上面都仔细的标注了观看序号,1.2.3... 按照顺序观看即可。

我们手写的整个express就是一个函数 函数里面return了一个函数。通过node原生框架http的方法 包装了该函数,最后再将整个函数module.exports导出出去。
最后我们启动项目,通过浏览器或者postman调用接口,发现确实能实现部分的express功能,但是有一点,此时我们能实现的仅仅是静态的路由,如果有路由参数的情况下,比如/product/:id/:name。结果就不符合预期。 改造:

代码上面都仔细的标注了观看序号,1.2.3... 按照顺序观看即可。

let http = require('http');
let url = require('url');
let methods = require('methods');function application(){let app = (req,res) => {let { pathname } = url.parse(req.url);let requestMethod = req.method.toLowerCase();for(let i = 0; i < app.routes.length; i++){let { path, method, cb } = app.routes[i];/** 7 如果请求路径path中 就说明该路由是动态的 */if(path.params){/** 8 匹配该动态路由后面的动态参数 匹配成功返回true */if(path.test(pathname)){/** 9 解构赋值 拿到动态路由的参数 */let [, ...otherParams] = pathname.match(path);/** 10 通过reduce()方法 将路由参数转换为对象形式* 并放到req.params中*/req.params = path.params.reduce((memo,key,index)=>(memo[key]=otherParams[index],memo),{});/** 11 返回匹配到的动态路由 */return cb(req,res);}}if((pathname===path||path==='*') && (requestMethod===method)||method==='all'){return cb(req,res);}}res.end(`Cannot found ${pathname}/${requestMethod}`);}app.routes = [];[...methods,'all'].forEach((method)=>{app[method] = function(path,cb){let layer = { path, method, cb };/** 1 定义一个空数组 来存放动态路由的参数 */let params = [];/** 2 如果路径中包含: 说明该路由是动态路由 */if(path.includes(':')){/** 3 更改该动态路由的路径path为一个正则表达式* 目的是为了等真正请求到来时 匹配到该动态路由 并拿到路由参数*/layer.path = new RegExp(path.replace(/:([^\/]*)/g,function(){/** 4 将动态路由参数的key 放入params数组中 */params.push(arguments[1]);/** 5 返回了一个正则来匹配真正的动态路由参数 注意此处没有: */return '([^\/]*)';}));/** 6 把解析到的动态路由放到该路由路径path的params上 */layer.path.params = params;}app.routes.push(layer);}});app.listen = function(...arguments){let server = http.createServer(app);server.listen(...arguments);}return app;
}module.exports = application;

先通过正则匹配到该动态路由,并把该动态路由的path替换为一个正则,放到数组中,等待真正的动态路由到来时,从路由数组中拿到该动态路由的路径,也就是刚才替换的正则,来匹配该动态路由后的参数即可。

通过以上就能实现获取动态路由的参数 上图:

clipboard.png

代码在git mock-express


http://www.ppmy.cn/news/514831.html

相关文章

浏览器访问ingress-nginx

一、总体演示 1. 部署ingress-nginx 方法. wget https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.3.0/deploy/static/provider/cloud/deploy.yaml 但是需要更改里面的image 或者复制下面 apiVersion: v1 kind: Namespace metadata:name: ingress-…

(3)华为ensp--静态路由

1.什么是静态路由&#xff1f; 静态路由是由用户管理员在路由器中手动配置的固定路由&#xff0c;因为是人工配置的&#xff0c;所以当网络的拓扑结构或链路的状态发生变化时&#xff0c;需要我们手动修改路由表里面的相关信息。 是一种路由的方式&#xff0c;路由项&#xf…

react 的 路由传参

1、路由传参 1、动态路由传参 使用prop进行接收 props.match.params.参数名 2、query传参 this.props.history.push({ pathname: ‘路由’, query:{参数名&#xff1a;参数值} }) 接收: this.props.location.query.参数名 3、state传参 this.props.history.push({ pathname…

Oracle中动态SQL详解

一&#xff1a;静态SQL与动态SQL   Oracle编译PL/SQL程序块分为两种&#xff1a;一种为前期联编&#xff08;early binding&#xff09;&#xff0c;即SQL语句在程序编译期间就已经确定&#xff0c;大多数的编译情况属于这种类型&#xff1b;另外一种是后期联编&#xff08;l…

H3C配置静态路由、下一跳地址,使PC相通

一、网络拓扑 实验目的&#xff1a;配置静态地址&#xff0c;下一跳&#xff0c;使PC相通 概念解释&#xff1a; 1、路由器的作用 路由器的核心作用是实现网络互连&#xff0c;数据转发&#xff0c;路由器工作时需要建立和更新路由表&#xff0c;因为路由器互联的是不同网段&…

OSPF多区域概述以及配置命令

OSPF多区域的的原理和配置 目录 一、生成OSPF多区域的原因二、OSPF三种通信量1、域内通信量2、域间通信量3、外部通信量三、路由器类型1、OSPF的区域类型-1二、OSPF的区域类型-2四、OSPF链路状态通告五、路由器对路由条目的选择六、末梢区域和完全末梢区域(一)、作用(二)、…

vue跳转相同路由强制刷新该相同路由组件

平时开发的时候可能遇到这种需求&#xff0c;在打开该菜单页面的情况下&#xff0c;再次点击菜单需要刷新该组件&#xff08;销毁再创建&#xff09;。 而vue自身如果路由不变的情况下是不会这样做的&#xff0c;就是说跳转的路由和当前的路由相同&#xff0c;vue不会进行任何操…

手把手带你做一个react-router-dom v6路由守卫

我们先创建一个react项目 然后 引入路由 npm install react-router-dom --save然后在src下创建一个文件夹 叫 components 在components下创建两个组件 index.jsx 参考代码如下 import React from "react"; import {useNavigate, } from "react-router-dom&quo…

0019-【测序平台】-illunima中国总部-03-上海路游

上海老街 陶瓷艺术馆 艺术街 世博会 交大 复旦 夜上海 金融中心

Vue 路由的使用 ( 动态传参 动态路由 路游接参 导航守卫)

路由的使用 命名路由 给路由起个名字&#xff0c;这样可以方便我们在使用路由的时候&#xff0c; 简写路径 命名视图 给出了一级视图以外的其他视图起名字&#xff0c; 这样可以区分不同级别的路由使用不同级别的视图 动态路由 & 路由传参 & 路由接参 vue cli3 配置反…

悬赏一个关于离散线性时变系统的稳定性证明问题

对于离散的线性时变系统 Δ k 1 ( I − α k G k ) Δ k \Delta_{k1}(I-\alpha_kG_k)\Delta_{k} Δk1​(I−αk​Gk​)Δk​&#xff0c;其中 α k ∈ R \alpha_k \in R αk​∈R&#xff0c; G k G_k Gk​未知但 ∣ ∣ G k ∣ ∣ ≤ c ||G_k|| \leq c ∣∣Gk​∣∣≤c&#…

[unity]Pico VR unity开发笔记(一)

Pico VR 开发笔记&#xff08;一&#xff09; XR Interaction Tooikit 版本 2.3.2 一、环境搭建 其实官方文档已经写的很详细了&#xff0c;这里只是不废话快速搭建&#xff0c;另外有一项官方说明有误的&#xff0c;补充说明一下&#xff0c;在开发工具部分说明 插件安装——…

生化危机暗黑历代记汉化终于完成了

生化危机暗黑历代记汉化终于完成了&#xff0c;500多K的文本&#xff0c;哎&#xff0c;射击游戏还这么多文本...... 不过总算是有始有终&#xff0c;第一个完全自己搞定的游戏汉化&#xff0c;付出了很多&#xff0c;也学到了很多&#xff0c;希望以后有精力再搞几个。 汉化游…

该死的Word——修复Doc文档的灵异错误

杀死鬼行 关于鬼行&#xff0c;其实是我个人的一种描述。很多人都遇到这样的问题&#xff0c;当编辑完(尤其是一个特别大的或者带有格式的)文档后&#xff0c;不知不觉再次打开时发现&#xff0c;有些明明是文档正文的部分&#xff0c;偏偏变成了标题样式&#xff0c;不管你…

调戏木马病毒的正确姿势-基础篇

本文参与i春秋社区原创文章奖励计划,未经许可禁止转载! 目录 ----------------------理论基础篇------------------- 从科幻小说说起: 危险的潘多拉盒子 来说说应用程序编程接口 工欲善其事必先利其器 (*)正确地作死 (*)被劫…

调戏木马病毒的正确姿势——上

目录----------------------理论基础篇------------------- 从科幻小说说起&#xff1a; 危险的潘多拉盒子 来说说应用程序编程接口 工欲善其事必先利其器 &#xff08;*&#xff09;正确地作死 &#xff08;*&#xff09;被劫持的应用程…

2019上半年勒索病毒专题报告

2019上半年由勒索病毒攻击造成的国内外大小事件&#xff0c;可发现伴随着传统行业逐渐数字化、网络化、智能化、逐步拥抱产业互联网化的大浪潮中&#xff0c;暴露出一系列网络安全问题。勒索病毒也乘机发难&#xff0c;疯狂敛财&#xff0c;影响日渐扩大。全球范围内的交通、能…

勒索病毒资料(腾讯管家整理)

目录 一、勒索病毒概述 二、勒索病毒发展史 三、勒索病毒感染数据 四、活跃勒索病毒家族 五、主要攻击特征 六、重点勒索事件回顾 七、勒索病毒未来趋势 八、勒索病毒应急处置手册 2017年5月12日&#xff0c;全球爆发的勒索病毒WannaCry借助高危漏洞“永恒之蓝”在世界…

勒索病毒应急措施及防护方案

本文共7916字 阅读时间大概15分钟 随着国家在政府、企业、教育及医疗等行业“互联网”战略的不断推进&#xff0c;各个行业在逐渐数字化、网络化、智能化、逐步拥抱产业互联网化的大浪潮过程中&#xff0c;也逐渐暴露出一系列网络安全问题。勒索病毒也乘机发难&#xff0c;疯狂…

Linux系统使用steamplay完美运行巫师之昆特牌:王权的陨落(Thronebreaker:The Witcher Tales)

问题描述 使用steamplay运行巫师之昆特牌&#xff1a;王权的陨落&#xff0c;打开游戏会出现黑屏&#xff0c;无法进行游戏的情况。 注意&#xff1a; 此问题不仅在linux上存在&#xff0c;部分Windows系统也会出现此问题&#xff0c;是由于游戏依赖了Media Foundation DLL。因…