大家好,我是山月。在上一篇文章中,我介绍了HTTP消息和简单的服务器端框架元素,例如如何接受请求参数。本文涵盖另一个常见主题:路由。简单路由最简单的路由可以使用req.url分发不同的逻辑,代码如下。但是对于一个非Demo风格的页面,业务逻辑堆在一起,显得过于简单。consthttp=require('http')constserver=http.createServer((req,res)=>{console.log(req.url)letdata=''if(req.url==='/'){data='你好,世界'res.end(data)}elseif(req.url==='/json'){res.setHeader('Content-Type','application/json;charset=utf-8');data=JSON.stringify({username:'MountainMoon'})res.end(data)}elseif(req.url==='/input'){letbody=''req.on('data',chunk=>body+=chunk)req.on('end',()=>{data=bodyres.end(data)})}})server.listen(3000)作为可以在生产环境中使用的复杂路由,越复杂路由至少能够解析下面的路由,并为各个路由配置单独的业务逻辑处理函数。Method:app.post('/',handler)ParamPath:app.post('/users/:userId',handler)基于正则路由目前,绝大部分服务端框架的路由都是基于正则匹配的,比如如koa、express等。另外,前端框架的路由vue-router和react-router也是基于正则匹配的。而这些框架基于正则匹配的路由都离不开一个库:path-to-regexp,它将/user/:name这样的路由转换成正则表达式。https://github.com/pillarjs/path-to-regexp标题:path-to-regexp它的API很简单:pathToRegexp:可以将路由转换成正则表达式match:可以匹配参数const{pathToRegexp,match,parse,compile}=require('path-to-regexp')pathToRegexp('/api/users/:userId')//=>/^\/api\/users(?:\/([^\/#\?]+?))[\/#\?]?$/iconsttoParams=match('/api/users/:userId')toParams('/api/users/10')//=>{//index:0//params:{userId:"12"}//path:"/api/users/12"//}这些Node服务器框架基于正则路由的原理是什么?注册路由。每条路由作为一个Layer(在express和koa中),path-to-regexp用于将路由路径转换为正则表达式作为Layer的属性。匹配路线。当有请求到来时,比较路由表中的每条路由,找出符合规则的多条路由,执行多条路由对应的业务处理逻辑。从上面可以看出它并没有进行一次路由匹配时间复杂度:"O(n)X正则匹配复杂度"基于正则路由性能问题的一些问题先不说性能问题,先看一道题:"当我们请求/api/users/10086时,有两条路由可以选择:/api/users/10086和/api/users/:userId,此时会匹配哪条路由?"下面是koa/koa-router写的,》因为是正则匹配,所以这个时候很容易出现路由冲突,路由的匹配跟顺序有很大关系。"constKoa=require("koa");constRouter=require("@koa/router");constapp=newKoa();constrouter=newRouter();router.get("/api/users/10086",(ctx,next)=>{console.log(ctx.router);ctx.body={userId:10086,direct:true};});router.get("/api/users/:userId",(ctx,next)=>{console.log(ctx.router);ctx.body={userId:ctx.params.userId};});基于前缀树路由(Trie,RadixTree,PrefixTree)相对于正则匹配路由,Matching基于前缀树效率更高,不存在上面提到的路由冲突问题find-my-wayhttps://github.com/delvedor/find-my-wayheader:find-my-wayconsthttp=require('http')constrouter=require('find-my-way')()constserver=http.createServer((req,res)=>{router.lookup(req,res)})router.on('GET','/api',()=>{})router.on('GET','/api/users/:id',(req,res)=>{res.end('id')})路由器。on('GET','/api/users/10086',(req,res)=>{res.end('10086')})router.on('GET','/api/users-friends',()=>{})console.log(router.prettyPrint())server.listen(3000)在上面的code、将所有路由路径组成一棵前缀树。顾名思义,前缀树将提取字符串的公共前缀。└──/api(GET)└──/users├──/│├──10086(GET)│└──:id(GET)└──-friends(GET)可以看出前缀树routing匹配时间复杂度明显小于O(n),没有每次正则匹配的正则路由复杂度。这决定了它比常规路由具有更高的性能。Fastify是Node中最快的框架,内置了基于前缀树的路由。constfastify=require('fastify')()fastify.get('/api/users/10086',async(request,reply)=>{return{userId:10086,direct:true}})fastify.get('/api/users/:id',async(request,reply)=>{constid=request.params.idreturn{userId:id}})fastify.listen(3000)405HTTP状态码中,路由相关的状态码对于404和405,作为一个专业的路由库,实现一个405也是工作的一部分。301302307308404:NotFound405:MethodNotAllowed嗯,代码就不放了……本文转载自微信公众号“全栈成长之路”,可关注下方二维码。转载本文请联系全栈成长之路公众号。
