内容简介:HttpHash对象维护这样一个routerNode树,结点结构定义如下:并提供了set,get两个方法插入结点逻辑:
var http = require('http'); var HttpHashRouter = require('http-hash-router'); var router = HttpHashRouter(); router.set('/health', function health(req, res) { res.end('OK'); }); var server = http.createServer(function handler(req, res) { router(req, res, {}, onError); function onError(err) { if (err) { // use your own custom error serialization. res.statusCode = err.statusCode || 500; res.end(err.message); } } }); server.listen(3000);
http-hash
HttpHash对象维护这样一个routerNode树,结点结构定义如下:
function RouteNode(parent, segment, isSplat) { this.parent = parent || null; // 上级结点 this.segment = segment || null; // 当前结点元素 this.handler = null; // handler方法 this.staticPaths = {}; // 静态路径 this.variablePaths = null; // 可变路径 this.isSplat = !!isSplat; // 是否带*号路径 this.src = null; // 完整path }
并提供了set,get两个方法
- router.set(pathname, handler), 根据pathname, 遍历每层路径元素,不断在hash中插入路由结点
- router.get(pathname), 同样通过可变路径优先找到访问的路径,调用相应结点的handler
set(pathname, handler)
function set(pathname, handler) { var pathSegments = pathname.split('/'); var hash = this._hash; var lastIndex = pathSegments.length - 1; var splatIndex = pathname.indexOf('*'); var hasSplat = splatIndex >= 0; if (hasSplat && splatIndex !== pathname.length - 1) { throw SplatError(pathname); } for (var i = 0; i < pathSegments.length; i++) { var segment = pathSegments[i]; // 路径元素为空,遍历下一个 if (!segment) { continue; } // 如果该路径元素是*号 且是最后一个元素 则增加可变路径结点 if (hasSplat && i === lastIndex) { // 同一路径下如果已经有了可变路径,维持之前的可变路径不变, 否则增加可变结点 hash = ( hash.variablePaths || (hash.variablePaths = new RouteNode(hash, segment, true)) ); // 如果该可变路径不是* 而是:,则抛出冲突的异常,即不可以同时在同一路径下分别设置*和:两种通配符可变路径 if (!hash.isSplat) { throw RouteConflictError(pathname, hash); } } else if (segment.indexOf(':') === 0) { // 如果是:通配符,增加可变路径结点 segment = segment.slice(1); // 获取:后的元素 // 同一路径下如果已经有了可变路径,维持之前的可变路径不变, 否则增加可变结点 hash = ( hash.variablePaths || (hash.variablePaths = new RouteNode(hash, segment)) ); // 如果设置的可变路径元素跟之前的不一致 或者之前设置是*可变路径,则抛出冲突异常 if (hash.segment !== segment || hash.isSplat) { throw RouteConflictError(pathname, hash); } } else if (segment === '__proto__') { hash = ( ( hash.hasOwnProperty('proto') && hash.proto ) || (hash.proto = new RouteNode(hash, segment)) ); } else { // 设置静态路径 查找当前has下的静态路径是否已经存在该路径元素 如果存在,直接复制结点,否则创建新的结点 hash = ( ( hash.staticPaths.hasOwnProperty(segment) && hash.staticPaths[segment] ) || (hash.staticPaths[segment] = new RouteNode(hash, segment)) ); } } // 判断是否设置了重复路径,重复则抛出冲突异常 if (!hash.handler) { hash.src = pathname; hash.handler = handler; } else { throwRouteConflictError(pathname, hash); } }
插入结点逻辑:
- 将整个路径以/分割成一个数组,遍历每层路径元素,通过判断*和:[key]插入可变路径结点,否则插入静态路径结点
- 同一层路径结点的静态路径结点,即兄弟结点可以有多个
- 同一层路径结点的可变路径结点只有一个
抛出冲突异常的几个地方
- 重复set了相同的路径
-
同一层路径下set了两个不一样的通配符(如:一个是
*
,一个是:[key]
),避免同时出现/api/*
和/api/:name
get(pathname)
function get(pathname) { var pathSegments = pathname.split('/'); // 当前路由hash结点树 var hash = this._hash; var splat = null; var params = {}; var variablePaths; for (var i = 0; i < pathSegments.length; i++) { var segment = pathSegments[i]; if (!segment && !hash.isSplat) { continue; } else if ( segment === '__proto__' && hash.hasOwnProperty('proto') ) { hash = hash.proto; } else if (hash.staticPaths.hasOwnProperty(segment)) { hash = hash.staticPaths[segment]; } else if ((variablePaths = hash.variablePaths)) { if (variablePaths.isSplat) { splat = pathSegments.slice(i).join('/'); hash = variablePaths; break; } else { params[variablePaths.segment] = segment; hash = variablePaths; } } else { hash = null; break; } } return new RouteResult(hash, params, splat); }
查找逻辑:
-
优先从静态路径中查找,
__proto__ > staticPaths > variablePaths
因此可以通过设置静态路由覆盖可变路径,如api/review
覆盖api/:name
-
splat保存指代
*
的路径内容,params保存:[key]
的路径内容
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- 【源码阅读】AndPermission源码阅读
- 【源码阅读】Gson源码阅读
- 如何阅读Java源码 ,阅读java的真实体会
- 我的源码阅读之路:redux源码剖析
- JDK源码阅读(六):HashMap源码分析
- 如何阅读源码?
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。