在 Node 应用中,路由是一个很重要的概念。
路由用于确定应用程序如何响应对特定端点的客户机请求,包含一个 URI(或路径)和一个特定的 HTTP 请求方法(GET、POST 等)。
每个路由可以具有一个或多个处理程序函数,这些函数在路由匹配时执行。
在 koa 中, koa-router
是一个功能丰富支持 restful
的路由中间件。
koa-router
有两个构造函数分别是 Router
和 Layer
。
函数 Router
是用来创建路由。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| function Router(opts) { if (!(this instanceof Router)) { return new Router(opts); } this.opts = opts || {}; this.methods = this.opts.methods || [ 'HEAD', 'OPTIONS', 'GET', 'PUT', 'PATCH', 'POST', 'DELETE' ];
this.params = {}; this.stack = []; };
|
然后是将每个 http 方法挂载到构造函数 Router 的原型上
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| methods.forEach(function (method) { Router.prototype[method] = function (name, path, middleware) { var middleware; if (typeof path === 'string' || path instanceof RegExp) { middleware = Array.prototype.slice.call(arguments, 2); } else { middleware = Array.prototype.slice.call(arguments, 1); path = name; name = null; } this.register(path, [method], middleware, { name: name });
return this; }; });
|
这样可以通过 route.get('/',...)
来实现对该路径的注册。
在 koa-router
中使用路由中间件也是很简单的,跟 koa
的使用方式一致,即通过 use
。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| Router.prototype.use = function () { var router = this; var middleware = Array.prototype.slice.call(arguments); var path; if (Array.isArray(middleware[0]) && typeof middleware[0][0] === 'string') { middleware[0].forEach(function (p) { router.use.apply(router, [p].concat(middleware.slice(1))); });
return this; }
... ...
return this; };
|
koa-router
也提供两个中间件分别为 routes
和 allowedMethods
。
函数 routes
用来匹配 request url
。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| Router.prototype.routes = Router.prototype.middleware = function () { var router = this; var dispatch = function dispatch(ctx, next) { debug('%s %s', ctx.method, ctx.path); var path = router.opts.routerPath || ctx.routerPath || ctx.path; var matched = router.match(path, ctx.method); var layerChain, layer, i;
... ...
if (!matched.route) return next();
... ...
layerChain = matchedLayers.reduce(function(memo, layer) { memo.push(function(ctx, next) { ctx.captures = layer.captures(path, ctx.captures); ctx.params = layer.params(path, ctx.captures, ctx.params); ctx.routerName = layer.name; return next(); }); return memo.concat(layer.stack); }, []); return compose(layerChain)(ctx, next); };
dispatch.router = this;
return dispatch; };
|
函数 allowedMethods
是用来确认请求方法,以及当不满足时自定义错误处理
函数 register
是用来注册路由。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| Router.prototype.register = function (path, methods, middleware, opts) { opts = opts || {}; var router = this; var stack = this.stack; if (Array.isArray(path)) { path.forEach(function (p) { router.register.call(router, p, methods, middleware, opts); }); return this; } var route = new Layer(path, methods, middleware, { end: opts.end === false ? opts.end : true, name: opts.name, sensitive: opts.sensitive || this.opts.sensitive || false, strict: opts.strict || this.opts.strict || false, prefix: opts.prefix || this.opts.prefix || "", ignoreCaptures: opts.ignoreCaptures }); ... ...
stack.push(route); return route; };
|
函数 Layer
用来初始化路由层。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| function Layer(path, methods, middleware, opts) { this.opts = opts || {}; this.name = this.opts.name || null; this.methods = []; this.paramNames = []; this.stack = Array.isArray(middleware) ? middleware : [middleware]; methods.forEach(function(method) { var l = this.methods.push(method.toUpperCase()); if (this.methods[l-1] === 'GET') { this.methods.unshift('HEAD'); } }, this);
this.stack.forEach(function(fn) { var type = (typeof fn); if (type !== 'function') { throw new Error( methods.toString() + " `" + (this.opts.name || path) +"`: `middleware` " + "must be a function, not `" + type + "`" ); } }, this); this.path = path; this.regexp = pathToRegExp(path, this.paramNames, this.opts);
debug('defined route %s %s', this.methods, this.opts.prefix + this.path); };
|
除此之外, 构造函数 Layer
提供 setPrefix
, param
, url
, captures
, params
, match
等方法,用来处理实际的请求路径及对应方法。
总结一下, koa-router
提供了较为丰富的实际使用方式,可以对应很多不同的场景,是值得在 koa 应用中使用的。