当前位置: 首页 > 后端技术 > Node.js

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

时间:2023-04-04 01:04:38 Node.js

现在node的web框架有很多,另外还有express和koaegg等等。但它们本质上都是基于原生node框架的http。其实框架也不错,主要是观察学习。本文主要记录我在爬节点路上的经验和收获~本文主要实现了express的其中一个功能,匹配路由匹配简单静态路由匹配动态路由先来看express:constexpress=require('express');letapp=newexpress();app.get('/',(req,res)=>{res.end('homePage.');});app.get('/center',(req,res)=>{res.end('centerPage.');});/**匹配动态路由获取路由参数并返回*/app.get('/product/:id/:name',(req,res)=>{res.end(JSON.stringify(req.params));});/**以上路径都不匹配时返回404*/app.all('*',(req,res)=>{res.end('404');});letport=3000;app.listen(port,()=>{console.log(`服务器在端口${port}`上启动);});好的。代码非常简单。引入express,新建一个express实例,写几条路由,最后启动本地服务。第二行代码,我们把导入的express给new了,说明express里面返回的是一个函数。好的。麻雀虽小,五脏俱全。今天我们就来实现express的这些功能。让http=require('http');/**express基于http*/leturl=require('url');/**用于解析请求的路径*//**express引入方法,其作用是返回各种请求方法*/letmethods=require('methods');functionapplication(){/**1express返回一个function*这个函数是http.createServer的监听函数*/letapp=(req,res)=>{/**1.1url模块解析得到请求路径,比如/user*/let{pathname}=url.parse(req.url);/**1.2获取请求方法method为大写,记得转为小写*/letrequestMethod=req.method.toLowerCase();/**1.3循环遍历之前定义的路由数组routes中得到的路径和方法进行匹配*/for(leti=0;i{app[method]=function(path,cb){/**2.2首先保存每次请求的路由地址方法和回调*path:pathmethod:methodcb:callback*/letlayer={path,method,cb};app.routes.push(layer);}});/**3个监听端口*/app.listen=function(...arguments){/**3.1在app中使用http的createServer方法设置Pass*/letserver=http.createServer(app);server.listen(...参数);}returnapp;}/**4导出方法*/module.exports=application;上面的代码注意观看序号是有标注的,1.2.3...大家可以按顺序观看。我们手写的整个express是一个函数,函数中返回一个函数。通过node原生框架的http方法对函数进行包装,最后导出整个函数module.exports。最后我们启动项目,通过浏览器或者postman调用接口,发现确实可以实现一些express功能,但是有一点,这个时候我们能实现的就只有静态路由了。如果有路由参数,比如/product/:id/:name。结果不如预期。修改:代码上仔细标注了观看序号,1.2.3...按顺序观看即可。lethttp=require('http');leturl=require('url');letmethods=require('methods');functionapplication(){letapp=(req,res)=>{let{路径名}=url.parse(req.url);让requestMethod=req.method.toLowerCase();for(leti=0;i(memo[key]=其他参数[索引],备忘录),{});/**11返回匹配的动态路由*/returncb(req,res);}}if((pathname===path||path==='*')&&(requestMethod===method)||method==='all'){returncb(req,res);}}res.end(`找不到${pathname}/${requestMethod}`);}app.routes=[];[...methods,'all'].forEach((method)=>{app[method]=function(path,cb){letlayer={path,method,cb};/**1定义一个空数组存放动态路由参数*/letparams=[];/**2如果路径包含:说明该路由是动态路由*/if(path.includes(':')){/**3改变path动态路由到正则的路径表达式*的作用是匹配动态路由,在真正请求到来时获取路由参数*/layer.path=newRegExp(path.replace(/:([^\/]*)/g,function(){/**4将动态路由参数的key放入params数组中*/params.push(arguments[1]);/**5返回一个regex来匹配真正的动态路由参数注意这里没有:*/返回'??([^\/]*)';}));/**6将解析出的动态路由放在路由路径path的params上*/layer.path.params=params;应用程序路由。推(层);}});app.listen=function(...arguments){letserver=http.createServer(app);server.listen(...参数);}returnapp;}module.exports=application;先通过正则匹配动态路由,将动态路由的路径换成正则,放入数组中,等待真正的动态路由到达,从路由数组中获取动态路由的路径,即刚才替换为匹配动态路由后的参数的正则可以通过上面的实现获取动态路由的参数上面:代码在gitmock-express