本文档是基于express3.4.6

express 的路由是自己去实现的,没有使用connect中的路由中间件模块。

1、在如何创建一个app那篇中,我们提到了路由,

  1. //router
  2. //路由
  3. this._router = new Router(this);
  4. this.routes = this._router.map;
  5. this.__defineGetter__('router', function(){
  6. this._usedRouter = true;
  7. this._router.caseSensitive = this.enabled('case sensitive routing');
  8. this._router.strict = this.enabled('strict routing');
  9. return this._router.middleware;
  10. });

可以看到,是将Router这个类存储到了app上的_rounter这个属性上去了,然后将所有得路由映射存储到了routes 这个属性上了。最后在给app定义一个router属性,直接调用了这个模块的

middleware模块。我们来看看Router类

  1. function Router(options) {
  2. options = options || {};
  3. var self = this;
  4. this.map = {};
  5. this.params = {};
  6. this._params = [];
  7. this.caseSensitive = options.caseSensitive;
  8. this.strict = options.strict;
  9. this.middleware = function router(req, res, next){
  10. self._dispatch(req, res, next);
  11. };
  12. }

从上面的代码可以看出,middleware调用了自身的_dispatch 函数 ,这个函数的作用就是路由分发

  1. Router.prototype._dispatch = function(req, res, next){
  2. var params = this.params
  3. , self = this;
  4.  
  5. debug('dispatching %s %s (%s)', req.method, req.url, req.originalUrl);
  6.  
  7. // route dispatch
  8. (function pass(i, err){
  9. var paramCallbacks
  10. , paramIndex = 0
  11. , paramVal
  12. , route
  13. , keys
  14. , key;
  15.  
  16. // match next route
  17. function nextRoute(err) {
  18. pass(req._route_index + 1, err);
  19. }
  20.  
  21. // match route
  22. req.route = route = self.matchRequest(req, i);
  23. // implied OPTIONS
  24. if (!route && 'OPTIONS' == req.method) return self._options(req, res);
  25.  
  26. // no route
  27. if (!route) return next(err);
  28. debug('matched %s %s', route.method, route.path);
  29.  
  30. // we have a route
  31. // start at param 0
  32. req.params = route.params;
  33. keys = route.keys;
  34. i = 0;
  35.  
  36. // param callbacks
  37. function param(err) {
  38. paramIndex = 0;
  39. key = keys[i++];
  40. paramVal = key && req.params[key.name];
  41. paramCallbacks = key && params[key.name];
  42.  
  43. try {
  44. if ('route' == err) {
  45. nextRoute();
  46. } else if (err) {
  47. i = 0;
  48. callbacks(err);
  49. } else if (paramCallbacks && undefined !== paramVal) {
  50. paramCallback();
  51. } else if (key) {
  52. param();
  53. } else {
  54. i = 0;
  55. callbacks();
  56. }
  57. } catch (err) {
  58. param(err);
  59. }
  60. };
  61.  
  62. param(err);
  63.  
  64. // single param callbacks
  65. function paramCallback(err) {
  66. var fn = paramCallbacks[paramIndex++];
  67. if (err || !fn) return param(err);
  68. fn(req, res, paramCallback, paramVal, key.name);
  69. }
  70.  
  71. // invoke route callbacks
  72. function callbacks(err) {
  73. var fn = route.callbacks[i++];
  74.  
  75. try {
  76. if ('route' == err) {
  77. nextRoute();
  78. } else if (err && fn) {
  79. if (fn.length < 4) return callbacks(err);
  80. fn(err, req, res, callbacks);
  81. } else if (fn) {
  82. if (fn.length < 4) return fn(req, res, callbacks);
  83. callbacks();
  84. } else {
  85. nextRoute(err);
  86. }
  87. } catch (err) {
  88. callbacks(err);
  89. }
  90. }
  91. })(0);
  92. };

这个函数是通过pass 这个自动执行的函数进行路由转发的,

首先通过请求, req.route = route = self.matchRequest(req, i); 来配置路由,返回需要的信息

可以看到matchRequest 函数返回了(我访问了下http://localhost:3000)

  1. { path: '/',
  2. method: 'get',
  3. callbacks: [ [Function] ],
  4. keys: [],
  5. regexp: /^\/\/?$/i,
  6. params: []
  7. }

看看 matchRequest 这个函数

  1. Router.prototype.matchRequest = function(req, i, head){
  2. var method = req.method.toLowerCase()
  3. , url = parse(req)
  4. , path = url.pathname
  5. , routes = this.map
  6. , i = i || 0
  7. , route;
  8.  
  9. // HEAD support
  10. if (!head && 'head' == method) {
  11. route = this.matchRequest(req, i, true);
  12. if (route) return route;
  13. method = 'get';
  14. }
  15.  
  16. // routes for this method
  17. if (routes = routes[method]) {
  18.  
  19. // matching routes
  20. for (var len = routes.length; i < len; ++i) {
  21. route = routes[i];
  22. if (route.match(path)) {
  23. req._route_index = i;
  24. return route;
  25. }
  26. }
  27. }
  28. };

它返回一个路由的处理结果。

后面根据参数执行了param() 函数。这个函数主要是处理callback回调函数的。

2.给路由注册各种函数:

  1. methods.forEach(function(method){
  2. app[method] = function(path){
  3. if ('get' == method && 1 == arguments.length) return this.set(path);
  4.  
  5. // deprecated
  6. if (Array.isArray(path)) {
  7. console.trace('passing an array to app.VERB() is deprecated and will be removed in 4.0');
  8. }
  9.  
  10. // if no router attached yet, attach the router
  11. if (!this._usedRouter) this.use(this.router);
  12.  
  13. // setup route
  14. console.log(method,'test2');
  15. this._router[method].apply(this._router, arguments);
  16. return this;
  17. };
  18. });

这个函数直接添加了开始的注册函数,下面的这个methods.foreach 依次为每一个app.get,app.post 等等 添加路由和 callback 函数

  1. Router.prototype.route = function(method, path, callbacks){
  2. var method = method.toLowerCase()
  3. , callbacks = utils.flatten([].slice.call(arguments, 2));
  4.  
  5. // ensure path was given
  6. if (!path) throw new Error('Router#' + method + '() requires a path');
  7.  
  8. // ensure all callbacks are functions
  9. callbacks.forEach(function(fn){
  10. if ('function' == typeof fn) return;
  11. var type = {}.toString.call(fn);
  12. var msg = '.' + method + '() requires callback functions but got a ' + type;
  13. throw new Error(msg);
  14. });
  15.  
  16. // create the route
  17. debug('defined %s %s', method, path);
  18. var route = new Route(method, path, callbacks, {
  19. sensitive: this.caseSensitive,
  20. strict: this.strict
  21. });
  22.  
  23. // add it
  24. (this.map[method] = this.map[method] || []).push(route);
  25. return this;
  26. };
  27.  
  28. Router.prototype.all = function(path) {
  29. var self = this;
  30. var args = [].slice.call(arguments);
  31. methods.forEach(function(method){
  32. self.route.apply(self, [method].concat(args));
  33. });
  34. return this;
  35. };
  36.  
  37. methods.forEach(function(method){
  38. Router.prototype[method] = function(path){
  39. var args = [method].concat([].slice.call(arguments));
  40. this.route.apply(this, args);
  41. return this;
  42. };
  43. });

最后一个函数,直接执行,给路由添加get,post等函数。

在Router.prototype.route  函数中,我们调用了一个:

  1. var route = new Route(method, path, callbacks, {
  2. sensitive: this.caseSensitive,
  3. strict: this.strict
  4. });

Route 是一个路由的基本单元,包含2个方法:

  1. function Route(method, path, callbacks, options) {
  2. options = options || {};
  3. this.path = path;
  4. this.method = method;
  5. this.callbacks = callbacks;
  6. this.regexp = utils.pathRegexp(path
  7. , this.keys = []
  8. , options.sensitive
  9. , options.strict);
  10. }
  11.  
  12. /**
  13. * Check if this route matches `path`, if so
  14. * populate `.params`.
  15. *
  16. * @param {String} path
  17. * @return {Boolean}
  18. * @api private
  19. */
  20.  
  21. Route.prototype.match = function(path){
  22. var keys = this.keys
  23. , params = this.params = []
  24. , m = this.regexp.exec(path);
  25.  
  26. if (!m) return false;
  27.  
  28. for (var i = 1, len = m.length; i < len; ++i) {
  29. var key = keys[i - 1];
  30.  
  31. var val = 'string' == typeof m[i]
  32. ? utils.decode(m[i])
  33. : m[i];
  34.  
  35. if (key) {
  36. params[key.name] = val;
  37. } else {
  38. params.push(val);
  39. }
  40. }
  41.  
  42. return true;
  43. }; 

nodejs express 框架解密4-路由的更多相关文章

  1. nodejs express 框架解密1-总体结构

    本文是基于express3.4.6的. 1.express 代码结构为: bin/express 是在命令行下的生成express 框架目录文件用的 lib/express 是框架的入口文件 lib/ ...

  2. nodejs express 框架解密2-如何创建一个app

    本文是基于express 3.4.6 的 1.在我们的app.js 文件里面有这么几行 http.createServer(app).listen(app.get('port'), function( ...

  3. nodejs express 框架解密5-视图

    本文档是基于express 3.4.6 的 在我们的代码中,渲染模板大致是这样写的 exports.index = function(req, res){ res.render('index', { ...

  4. nodejs express 框架解密3-中间件模块

    本文档是基于express 3.4.6 的 在上篇中我们提到了中间件,这篇主要解释这个模块,middleware.js 为: var utils = require('./utils'); /** * ...

  5. Express框架(http服务器 + 路由)

    index.js 使用express框架搭建http服务器,和实现路由功能. var express = require('express'); var app = express(); // 主页输 ...

  6. React第一篇: 搭建React + nodejs + express框架

    前提: 需要安装Node.js (>6)版本 1.cmd进到本地某个目录, 逐行输入以下指令(以下括号为注释) npm install -g create-react-app   (全局安装cr ...

  7. NodeJs笔记 : express框架创建工程 ----- 路由设计

    一.搭建工程 1 .安装 express-generator $ npm install -g express-generator 2 .本地创建express项目 $ express -e blog ...

  8. nodejs express 框架 上传文件

    web 项目应用express4.0框架 html 表单post 文件上传失败,后端无法获取提交文件 express不支持文件上传. 方式一 若是图片,可以将图片转码为BASE64上传 前端框架ang ...

  9. nodeJS express框架 中文乱码解决办法

    最近在研究javascript 的服务端应用 node,之所以想要研究node,是因为前几个月一直在前端挣扎,从javascript入门到在项目中实际使用javascript,确实感悟颇深.javas ...

随机推荐

  1. JAVA动态加载JAR包执行程序

    入口代码 import java.io.File; import java.net.MalformedURLException; import java.net.URL; import java.ne ...

  2. log4j2自定义输出线程环境信息

    在配置日志的输出格式的时候,我们可以按照内置的规则输出日志,但是有时候需要及时输出我们自定义的信息,这时需要借助ThreadContext类. ThreadContext类类似于MDC和NDC,它是一 ...

  3. 50条LINUX命令整理

    1. find 基本语法参数如下: find [PATH] [option] [action] # 与时间有关的参数: -mtime n : n为数字,意思为在n天之前的“一天内”被更改过的文件: - ...

  4. IPD模式下开展敏捷开发的一些问题汇总

    1.      我们现在普遍用的是老系统情况下,什么时候把软件和硬件在敏捷项目里面集成? 答:有两种场景:第一种场景是把软件分几个迭代,最后把软件和硬件一起集成:第二种场景是更好的一种场景,每几个迭代 ...

  5. jexus ASP.NET开发注意项1

    Jexus@宇内(273766940) 9:09:02 linux上的文件名和文件路径是大小写敏感的. Jexus@宇内(273766940) 9:10:11 在win上,你文件名是 Login.as ...

  6. day7----面向对象编程进阶

    本节内容: 面向对象高级语法部分 静态方法.类方法.属性方法 类的特殊方法 反射 异常处理 Socket开发基础 静态方法 它与类唯一的关联就是需要通过类名来调用这个方法 #静态方法实际跟类没关系,不 ...

  7. 配置ubuntu 14.04.3 LTS odoo 9.0开发环境

    使用VMware Fusion 8.0.1创建ubuntu 64bit虚拟机:使用ubuntu-14.04.3-desktop-amd64.iso镜像缺省安装ubuntu,用户名odoo,密码1234 ...

  8. 修改开机启动等待时间(for Ubuntu12.10)

    Ubuntu的开机启动等待时间默认是10s,等待时间比较长,每次启动都得按一下回车,于是就想修改一下等待时间.我们可以找到Grub的配置文件(/boot/grub/grub.cfg),在其中进行个性化 ...

  9. 互联网上那些excel文件

    互联网上那些excel文件 文/玄魂 目录 互联网上那些excel文件 前言 1.1 查找包含指定值的excel文件 1.2 查找邮箱 1.3 查找身份证号 1.4  查找管理人员联系信息 1.5 获 ...

  10. NodeJS Hello world

    #2 NodeJS Hello world 打开 https://nodejs.org/api/synopsis.html 将上述代码保存至D:\NODEJS\example.js 打开CMD窗口,定 ...