router源码学习小结

koa 框架一直都保持着简洁性, 它只对 node 的 HTTP 模块进行了封装, 而在真正实际使用, 我们还需要更多地像路由这样的模块来构建我们的应用, 而 koa-router 是常用的 koa 的路由库. 这里通过解析 koa-router 的源码来达到深入学习的目的.

源码架构图

router源码学习小结

调用链路-routes()

HTTP请求调用流程

Usage

const Koa = require('koa'); const Router = require('koa-router'); const app = new Koa(); const router = new Router(); router.get('https://www.jb51.net/', async (ctx, next) => { console.log('index'); ctx.body = 'index'; }); app.use(router.routes()).use(router.allowedMethods()); app.listen(3000);

Router

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' ]; // 存放router.param方法指定的参数的中间件 this.params = {}; // 存放layer实例 this.stack = []; };

Layer

function Layer(path, methods, middleware, opts) { this.opts = opts || {}; this.name = this.opts.name || null; this.methods = []; // 存放path路径参数的一些属性,eg: /test/:str => { name: str, prefix: 'https://www.jb51.net/' ....} this.paramNames = []; // 存放该路由的中间件 this.stack = Array.isArray(middleware) ? middleware : [middleware]; methods.forEach(function(method) { var l = this.methods.push(method.toUpperCase()); // 如果支持get请求,一并支持head请求 if (this.methods[l-1] === 'GET') { this.methods.unshift('HEAD'); } }, this); // ensure middleware is a function 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); };

给Router实例挂载HTTP方法

/** * Create `router.verb()` methods, where *verb* is one of the HTTP verbs such * as `router.get()` or `router.post()`. * * Match URL patterns to callback functions or controller actions using `router.verb()`, * where **verb** is one of the HTTP verbs such as `router.get()` or `router.post()`. * * Additionaly, `router.all()` can be used to match against all methods. * * ```javascript * router * .get('https://www.jb51.net/', (ctx, next) => { * ctx.body = 'Hello World!'; * }) * .post('/users', (ctx, next) => { * // ... * }) * .put('/users/:id', (ctx, next) => { * // ... * }) * .del('/users/:id', (ctx, next) => { * // ... * }) * .all('/users/:id', (ctx, next) => { * // ... * }); * ``` * * When a route is matched, its path is available at `ctx._matchedRoute` and if named, * the name is available at `ctx._matchedRouteName` * * Route paths will be translated to regular expressions using * [path-to-regexp](https://github.com/pillarjs/path-to-regexp). * * Query strings will not be considered when matching requests. * * #### Named routes * * Routes can optionally have names. This allows generation of URLs and easy * renaming of URLs during development. * * ```javascript * router.get('user', '/users/:id', (ctx, next) => { * // ... * }); * * router.url('user', 3); * // => "/users/3" * ``` * * #### Multiple middleware * * Multiple middleware may be given: * * ```javascript * router.get( * '/users/:id', * (ctx, next) => { * return User.findOne(ctx.params.id).then(function(user) { * ctx.user = user; * next(); * }); * }, * ctx => { * console.log(ctx.user); * // => { id: 17, name: "Alex" } * } * ); * ``` * * ### Nested routers * * Nesting routers is supported: * * ```javascript * var forums = new Router(); * var posts = new Router(); * * posts.get('https://www.jb51.net/', (ctx, next) => {...}); * posts.get('/:pid', (ctx, next) => {...}); * forums.use('/forums/:fid/posts', posts.routes(), posts.allowedMethods()); * * // responds to "/forums/123/posts" and "/forums/123/posts/123" * app.use(forums.routes()); * ``` * * #### Router prefixes * * Route paths can be prefixed at the router level: * * ```javascript * var router = new Router({ * prefix: '/users' * }); * * router.get('https://www.jb51.net/', ...); // responds to "/users" * router.get('/:id', ...); // responds to "/users/:id" * ``` * * #### URL parameters * * Named route parameters are captured and added to `ctx.params`. * * ```javascript * router.get('/:category/:title', (ctx, next) => { * console.log(ctx.params); * // => { category: 'programming', title: 'how-to-node' } * }); * ``` * * The [path-to-regexp](https://github.com/pillarjs/path-to-regexp) module is * used to convert paths to regular expressions. * * @name get|put|post|patch|delete|del * @memberof module:koa-router.prototype * @param {String} path * @param {Function=} middleware route middleware(s) * @param {Function} callback route callback * @returns {Router} */ var methods = require('methods'); methods.forEach(function (method) { Router.prototype[method] = function (name, path, middleware) { var middleware; // 如果指定了路由name属性 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; }; });

Router.prototype.register

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:http://www.heiqu.com/1690e3f2cd69f10ca1d18c4f9e7fcd5f.html