内容简介: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源码分析
- 如何阅读源码?
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。