模块介绍
express-autoload-router
模块用于自动加载并注册路由。
模块使用
基本用法
基本使用步骤如下。
安装模块
$ npm install express-autoload-router
构建路由程序
将路由程序文件放在专门的目录app/controllers
下面。格式上有两点需要注意:
- 路由程序文件名称必须为
xxx_controller.js
; - 路由程序中的action函数/对象名称必须为
yyyAction
;
$ cat app/controllers/user_controller.js
/**
* 对象方式,多个路由放在一起
*/
module.exports = {
listAction: {
method: ["GET"],
middlewares: [],
handler: function(req, res) {
return res.send("user list action");
}
},
getAction: {
method: ["GET"],
middlewares: [],
handler: function(req, res) {
return res.send("user get action");
}
}
};
在主程序中引入并加载路由
$ cat app.js
const express = require("express");
const path = require('path');
const loadRouter = require('express-autoload-router');
const app = express();
loadRouter(app, "/demo", path.join(__dirname, "app/controllers"));
app.listen(3000, function() {
console.log("Listening on port 3000!");
});
其中,loadRouter()
函数的第二个参数指定一级路由,第三个参数指定路由程序文件所在的目录。
该函数会调用app.METHOD()
(METHOD在这里替换为get、post等HTTP方法)注册相应的路由,路由URI为:/demo/xxx/yyy
。
使用示例
$ curl -X GET http://localhost:3000/demo/user/list
user list action
路由程序文件的写法
路由程序文件中的yyyAction
可以写成函数,也可以写成对象,两者是等价的。
使用函数
基本写法如下:
// 普通路由,访问方式:/demo/product/list
module.exports.listAction = function(req, res) {
return res.send("product list action");
};
由于在node.js
当中,module.exports
和exports
等价,所以也可以写成:
// module.exports和exports等价
exports.getAction = function(req, res) {
return res.send("product get action");
}
另外,函数也可以简化:
// 简化函数:function 改成 =>
exports.simpleAction = (req, res) => {
return res.send("product simple action");
};
函数再简化一点:
// 更简化函数:function 改成 =>,省略大括号
// URL使用大小写均可:/demo/product/moreSimple 或 /demo/product/moresimple
exports.moreSimpleAction = (req, res) => res.send("product moreSimple action");
使用对象
基本写法:
module.exports.buyAction = {
method: ["GET", "POST"],
middlewares: [],
handler: function(req, res) {
return res.send("product buy action");
}
};
等价写法:
// handler的另一种写法
exports.sellAction = {
method: ["GET", "POST"],
middlewares: [],
handler(req, res) {
return res.send("product sell action");
}
};
注意事项
indexAction
的处理
indexAction
中的index
不会作为组成路由的一部分。比如,对于路由文件product_controller.js
,有:
// 默认路由,访问方式:/demo/product
module.exports.indexAction = function(req, res) {
return res.send("product index action");
};
// 普通路由,访问方式:/demo/product/list
module.exports.listAction = function(req, res) {
return res.send("product list action");
};
其中,listAction
对应的路由为/demo/product/list
,而indexAction
对应的路由为/demo/product
。
路由子目录(子路由)
假设controllers
目录下有一个子目录subdir
,其中有一个路由程序文件subroute_controller.js
,如下:
$ cat app/controllers/subdir
module.exports.listAction = function(req, res) {
return res.send("subdir/subroute list action");
};
listAction
对应的路由为/demo/subdir/subroute/list
。由此可见,子目录也会作为路由的一部分。
Action名称大小写问题
Action函数/对象名称中的可以使用驼峰写法,对应的URL可以使用驼峰也可以使用全部小写的形式。示例如下:
// URL使用大小写均可:/demo/product/moreSimple 或 /demo/product/moresimple
module.exports.moreSimpleAction = function(req, res) {
return res.send("product moreSimple action");
}
源码分析
实现原理
遍历各控制器文件,将其中的Action函数或对象通过express对象的app[method]()
方法进行注册,实现路由绑定。
不管Action是函数还是对象,都会构造三个参数:method
、middlewares
、handler
,然后通过下面的方法进行注册。
app[method](url, compose(middlewares, modifiedUrl), handler);
基础语法
for...in
和for...of
用于数组迭代
示例:for...in
用于数组迭代
> let members = ["Jay", "Angela", "Henry"];
> typeof(members)
'object'
> for (let i in members) {
... console.log(i);
... }
0
1
2
示例:for...of
用于数组迭代
> let members = ["Jay", "Angela", "Henry"];
> for (let member of members) {
... console.log(member);
... }
Jay
Angela
Henry
for...in
与for...of
的区别:for...in
只能用于遍历一个类型为Object
的对象(包括数组)的属性;for...of
可以用于任意可迭代对象,如:数组、字符串等。需要注意的是,Object不是一个可迭代对象。
示例:for...in
用于对象
> let henry = {"age": 6, "height": 120};
> for (let i in henry) {
... console.log(i);
... }
age
height
示例:for...of
无法用于对象
> let henry = {"age": 6, "height": 120};
> for (let i of henry) {
... console.log(i);
... }
TypeError: henry is not iterable
(function iterate() {})();
定义并调用函数
示例:
> (function f(req, res) {
... console.log(req);
... console.log(res);
... })("req", "res");
req
res
注:此例中,函数名称f
可以省略。
内置对象 - String
String.prototype.replace() - 替换
作用:使用字符串字面量或者正则表达式进行子字符串替换。使用正则表达式的方式进行替换比较常用。
示例:字符串字面量替换
注:该种方法只会替换第一个匹配的子字符串。
> "Jay, Jay! What are you doing?".replace("Jay", "Henry")
'Henry, Jay! What are you doing?'
示例:正则表达式替换(无global和ignore选项)
注:该种方法只会替换第一个匹配的子字符串。
> "Jay, Jay! What are you doing?".replace(/Jay/, "Henry")
'Henry, Jay! What are you doing?'
示例:正则表达式替换(有global和ignore选项)
注:global
选项表示替换所有匹配的子字符串,ignore
选项表示匹配子字符串时忽略大小写。
> "Jay, Jay! What are you doing?".replace(/Jay/gi, "Henry")
'Henry, Henry! What are you doing?'
示例:将\
替换为/
注:此例中,C:\\Windows\\System32
里用\\
表示一个真实存储的\
是因为node解释器会做一次转换,在实际的node程序中会写成url.replace(/\\/gi, "/");
。
> "C:\\Windows\\System32".replace(/\\/gi, "/");
'C:/Windows/System32'
String.prototype.match() - 查找
作用:使用正则表达式进行子字符串匹配,并返回匹配结果数组。
说明:正则表达式的global
选项会决定匹配结果数组中是否会携带附加属性,附加属性包括:index
- 匹配结果的开始位置,input
- 原始字符串,groups
- 捕获组数组。
示例:正则表达式不使用global
选项时,会返回附加属性
> "listAction".match(/Action$/)
[ 'Action', index: 4, input: 'listAction', groups: undefined ]
示例:正则表达式使用global
选项时,不会返回附加属性
> "listAction".match(/Action$/gi)
[ 'Action' ]
示例:正则表达式无匹配结果时,返回null
> "list".match(/Action$/gi)
null
内置对象 - Array
Array.isArray() - 判断
作用:判断给定的值是否为数组。该函数为静态函数。
示例:数组返回true
> Array.isArray([1, 2, 3]);
true
示例:对象返回false
> Array.isArray({"name": "Henry"});
false
Array.prototype.indexOf() - 查找
作用:查找指定元素,返回第一个索引。如果没找到,返回-1。
示例:查找到指定元素,返回索引
> ["Jay", "Angela", "Henry"].indexOf("Henry");
2
示例:未查找到指定元素,返回-1
> ["Jay", "Angela", "Henry"].indexOf("Cobe");
-1
内置对象 - Function
Function.prototype.bind()
标准库 - os
os.platform()
作用:返回Node编译时的操作系统平台名称。
示例:Mac系统上执行
> let os = require("os");
> os.platform();
'darwin'
示例:CentOS系统上执行
> let os = require("os");
> os.platform()
'linux'
第三方库 - glob.sync()
库的作用:使用pattern
模式匹配文件,支持shell下的pattern
模式。
glob.sync()
作用:同步方式遍历文件夹下的所有文件,返回一个匹配文件路径数组。
示例:遍历app/controllers/
目录下的所有controller
文件,不递归遍历子目录
> let glob = require("glob");
> glob.sync(`app/controllers/*_controller.js`)
[ 'app/controllers/product_controller.js',
'app/controllers/user_controller.js' ]
示例:遍历app/controllers/
目录下的所有controller
文件,递归遍历子目录(加一层**
目录)
> let glob = require("glob");
> glob.sync(`app/controllers/**/*_controller.js`)
[ 'app/controllers/product_controller.js',
'app/controllers/subdir/subroute_controller.js',
'app/controllers/user_controller.js' ]
参考资料
- express-autoload-router 模块源码
- 本文源码