nodejs express 框架解密4-路由
本文档是基于express3.4.6
express 的路由是自己去实现的,没有使用connect中的路由中间件模块。
1、在如何创建一个app那篇中,我们提到了路由,
- //router
- //路由
- this._router = new Router(this);
- this.routes = this._router.map;
- this.__defineGetter__('router', function(){
- this._usedRouter = true;
- this._router.caseSensitive = this.enabled('case sensitive routing');
- this._router.strict = this.enabled('strict routing');
- return this._router.middleware;
- });
可以看到,是将Router这个类存储到了app上的_rounter这个属性上去了,然后将所有得路由映射存储到了routes 这个属性上了。最后在给app定义一个router属性,直接调用了这个模块的
middleware模块。我们来看看Router类
- function Router(options) {
- options = options || {};
- var self = this;
- this.map = {};
- this.params = {};
- this._params = [];
- this.caseSensitive = options.caseSensitive;
- this.strict = options.strict;
- this.middleware = function router(req, res, next){
- self._dispatch(req, res, next);
- };
- }
从上面的代码可以看出,middleware调用了自身的_dispatch 函数 ,这个函数的作用就是路由分发
- Router.prototype._dispatch = function(req, res, next){
- var params = this.params
- , self = this;
- debug('dispatching %s %s (%s)', req.method, req.url, req.originalUrl);
- // route dispatch
- (function pass(i, err){
- var paramCallbacks
- , paramIndex = 0
- , paramVal
- , route
- , keys
- , key;
- // match next route
- function nextRoute(err) {
- pass(req._route_index + 1, err);
- }
- // match route
- req.route = route = self.matchRequest(req, i);
- // implied OPTIONS
- if (!route && 'OPTIONS' == req.method) return self._options(req, res);
- // no route
- if (!route) return next(err);
- debug('matched %s %s', route.method, route.path);
- // we have a route
- // start at param 0
- req.params = route.params;
- keys = route.keys;
- i = 0;
- // param callbacks
- function param(err) {
- paramIndex = 0;
- key = keys[i++];
- paramVal = key && req.params[key.name];
- paramCallbacks = key && params[key.name];
- try {
- if ('route' == err) {
- nextRoute();
- } else if (err) {
- i = 0;
- callbacks(err);
- } else if (paramCallbacks && undefined !== paramVal) {
- paramCallback();
- } else if (key) {
- param();
- } else {
- i = 0;
- callbacks();
- }
- } catch (err) {
- param(err);
- }
- };
- param(err);
- // single param callbacks
- function paramCallback(err) {
- var fn = paramCallbacks[paramIndex++];
- if (err || !fn) return param(err);
- fn(req, res, paramCallback, paramVal, key.name);
- }
- // invoke route callbacks
- function callbacks(err) {
- var fn = route.callbacks[i++];
- try {
- if ('route' == err) {
- nextRoute();
- } else if (err && fn) {
- if (fn.length < 4) return callbacks(err);
- fn(err, req, res, callbacks);
- } else if (fn) {
- if (fn.length < 4) return fn(req, res, callbacks);
- callbacks();
- } else {
- nextRoute(err);
- }
- } catch (err) {
- callbacks(err);
- }
- }
- })(0);
- };
这个函数是通过pass 这个自动执行的函数进行路由转发的,
首先通过请求, req.route = route = self.matchRequest(req, i); 来配置路由,返回需要的信息
可以看到matchRequest 函数返回了(我访问了下http://localhost:3000)
- { path: '/',
- method: 'get',
- callbacks: [ [Function] ],
- keys: [],
- regexp: /^\/\/?$/i,
- params: []
- }
看看 matchRequest 这个函数
- Router.prototype.matchRequest = function(req, i, head){
- var method = req.method.toLowerCase()
- , url = parse(req)
- , path = url.pathname
- , routes = this.map
- , i = i || 0
- , route;
- // HEAD support
- if (!head && 'head' == method) {
- route = this.matchRequest(req, i, true);
- if (route) return route;
- method = 'get';
- }
- // routes for this method
- if (routes = routes[method]) {
- // matching routes
- for (var len = routes.length; i < len; ++i) {
- route = routes[i];
- if (route.match(path)) {
- req._route_index = i;
- return route;
- }
- }
- }
- };
它返回一个路由的处理结果。
后面根据参数执行了param() 函数。这个函数主要是处理callback回调函数的。
2.给路由注册各种函数:
- methods.forEach(function(method){
- app[method] = function(path){
- if ('get' == method && 1 == arguments.length) return this.set(path);
- // deprecated
- if (Array.isArray(path)) {
- console.trace('passing an array to app.VERB() is deprecated and will be removed in 4.0');
- }
- // if no router attached yet, attach the router
- if (!this._usedRouter) this.use(this.router);
- // setup route
- console.log(method,'test2');
- this._router[method].apply(this._router, arguments);
- return this;
- };
- });
这个函数直接添加了开始的注册函数,下面的这个methods.foreach 依次为每一个app.get,app.post 等等 添加路由和 callback 函数
- Router.prototype.route = function(method, path, callbacks){
- var method = method.toLowerCase()
- , callbacks = utils.flatten([].slice.call(arguments, 2));
- // ensure path was given
- if (!path) throw new Error('Router#' + method + '() requires a path');
- // ensure all callbacks are functions
- callbacks.forEach(function(fn){
- if ('function' == typeof fn) return;
- var type = {}.toString.call(fn);
- var msg = '.' + method + '() requires callback functions but got a ' + type;
- throw new Error(msg);
- });
- // create the route
- debug('defined %s %s', method, path);
- var route = new Route(method, path, callbacks, {
- sensitive: this.caseSensitive,
- strict: this.strict
- });
- // add it
- (this.map[method] = this.map[method] || []).push(route);
- return this;
- };
- Router.prototype.all = function(path) {
- var self = this;
- var args = [].slice.call(arguments);
- methods.forEach(function(method){
- self.route.apply(self, [method].concat(args));
- });
- return this;
- };
- methods.forEach(function(method){
- Router.prototype[method] = function(path){
- var args = [method].concat([].slice.call(arguments));
- this.route.apply(this, args);
- return this;
- };
- });
最后一个函数,直接执行,给路由添加get,post等函数。
在Router.prototype.route 函数中,我们调用了一个:
- var route = new Route(method, path, callbacks, {
- sensitive: this.caseSensitive,
- strict: this.strict
- });
Route 是一个路由的基本单元,包含2个方法:
- function Route(method, path, callbacks, options) {
- options = options || {};
- this.path = path;
- this.method = method;
- this.callbacks = callbacks;
- this.regexp = utils.pathRegexp(path
- , this.keys = []
- , options.sensitive
- , options.strict);
- }
- /**
- * Check if this route matches `path`, if so
- * populate `.params`.
- *
- * @param {String} path
- * @return {Boolean}
- * @api private
- */
- Route.prototype.match = function(path){
- var keys = this.keys
- , params = this.params = []
- , m = this.regexp.exec(path);
- if (!m) return false;
- for (var i = 1, len = m.length; i < len; ++i) {
- var key = keys[i - 1];
- var val = 'string' == typeof m[i]
- ? utils.decode(m[i])
- : m[i];
- if (key) {
- params[key.name] = val;
- } else {
- params.push(val);
- }
- }
- return true;
- };
nodejs express 框架解密4-路由的更多相关文章
- nodejs express 框架解密1-总体结构
本文是基于express3.4.6的. 1.express 代码结构为: bin/express 是在命令行下的生成express 框架目录文件用的 lib/express 是框架的入口文件 lib/ ...
- nodejs express 框架解密2-如何创建一个app
本文是基于express 3.4.6 的 1.在我们的app.js 文件里面有这么几行 http.createServer(app).listen(app.get('port'), function( ...
- nodejs express 框架解密5-视图
本文档是基于express 3.4.6 的 在我们的代码中,渲染模板大致是这样写的 exports.index = function(req, res){ res.render('index', { ...
- nodejs express 框架解密3-中间件模块
本文档是基于express 3.4.6 的 在上篇中我们提到了中间件,这篇主要解释这个模块,middleware.js 为: var utils = require('./utils'); /** * ...
- Express框架(http服务器 + 路由)
index.js 使用express框架搭建http服务器,和实现路由功能. var express = require('express'); var app = express(); // 主页输 ...
- React第一篇: 搭建React + nodejs + express框架
前提: 需要安装Node.js (>6)版本 1.cmd进到本地某个目录, 逐行输入以下指令(以下括号为注释) npm install -g create-react-app (全局安装cr ...
- NodeJs笔记 : express框架创建工程 ----- 路由设计
一.搭建工程 1 .安装 express-generator $ npm install -g express-generator 2 .本地创建express项目 $ express -e blog ...
- nodejs express 框架 上传文件
web 项目应用express4.0框架 html 表单post 文件上传失败,后端无法获取提交文件 express不支持文件上传. 方式一 若是图片,可以将图片转码为BASE64上传 前端框架ang ...
- nodeJS express框架 中文乱码解决办法
最近在研究javascript 的服务端应用 node,之所以想要研究node,是因为前几个月一直在前端挣扎,从javascript入门到在项目中实际使用javascript,确实感悟颇深.javas ...
随机推荐
- JAVA动态加载JAR包执行程序
入口代码 import java.io.File; import java.net.MalformedURLException; import java.net.URL; import java.ne ...
- log4j2自定义输出线程环境信息
在配置日志的输出格式的时候,我们可以按照内置的规则输出日志,但是有时候需要及时输出我们自定义的信息,这时需要借助ThreadContext类. ThreadContext类类似于MDC和NDC,它是一 ...
- 50条LINUX命令整理
1. find 基本语法参数如下: find [PATH] [option] [action] # 与时间有关的参数: -mtime n : n为数字,意思为在n天之前的“一天内”被更改过的文件: - ...
- IPD模式下开展敏捷开发的一些问题汇总
1. 我们现在普遍用的是老系统情况下,什么时候把软件和硬件在敏捷项目里面集成? 答:有两种场景:第一种场景是把软件分几个迭代,最后把软件和硬件一起集成:第二种场景是更好的一种场景,每几个迭代 ...
- jexus ASP.NET开发注意项1
Jexus@宇内(273766940) 9:09:02 linux上的文件名和文件路径是大小写敏感的. Jexus@宇内(273766940) 9:10:11 在win上,你文件名是 Login.as ...
- day7----面向对象编程进阶
本节内容: 面向对象高级语法部分 静态方法.类方法.属性方法 类的特殊方法 反射 异常处理 Socket开发基础 静态方法 它与类唯一的关联就是需要通过类名来调用这个方法 #静态方法实际跟类没关系,不 ...
- 配置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 ...
- 修改开机启动等待时间(for Ubuntu12.10)
Ubuntu的开机启动等待时间默认是10s,等待时间比较长,每次启动都得按一下回车,于是就想修改一下等待时间.我们可以找到Grub的配置文件(/boot/grub/grub.cfg),在其中进行个性化 ...
- 互联网上那些excel文件
互联网上那些excel文件 文/玄魂 目录 互联网上那些excel文件 前言 1.1 查找包含指定值的excel文件 1.2 查找邮箱 1.3 查找身份证号 1.4 查找管理人员联系信息 1.5 获 ...
- NodeJS Hello world
#2 NodeJS Hello world 打开 https://nodejs.org/api/synopsis.html 将上述代码保存至D:\NODEJS\example.js 打开CMD窗口,定 ...