打造属于你自己的 Express 脚手架(二)

上一篇中,我们成功的启动了一个 node 服务,然而并没有什么卵用,你在浏览器打开配置中的 url 是会报错的,这叫怎么一回事,所以在这一篇中,我们将在上一篇的基础上增加路由控制,实现浏览器访问时能看到服务器返回的信息

接下来,擦亮眼睛理清思路,上车啦

Router

路由是指如何定义应用的端点(URIs)以及如何响应客户端的请求。

关于路由的介绍,可以参考 Express 路由 上的介绍,这里就不再多说,我们要做的就是合理安排项目结构,更加 优雅 的编码

结构

首先,我们在项目根目录下新建一个 routes 文件夹,下面新建一个 index.js ,相信大家也看出来我的文件命名调性了,使用 index 作为一个文件夹下的入口文件名,第一个好处,命名统一、优雅,第二个好处,引用的时候只需要把路径定位到文件夹就可以了, node 会自动使用文件夹下名为 index 的文件

然后,先不急着写代码,我们思考一下对于路由的规划:

我们要设计 RESTful API,首先就得确定分类的方法,用请求类型 (get,post…)来分类肯定是不妥当的,对于很多模块来说,最好每个模块的 api 能分别进行管理,假如我们有 cats,dodgs,birds 三个模块,那么最合理的分类方法就是按照模块进行划分:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
cats : {
'/cats/getcats',
'/cats/getcat/:id',
'/cats/addcat/:id',
'/cats/updatecat/:id',
'/cats/delcat/:id',
}
dodgs : {
'/dodgs/getdodgs',
'/dodgs/getdodg/:id',
'/dodgs/adddog/:id',
'/dodgs/updatedog/:id',
'/dodgs/deldog/:id',
}
...

这样将每个模块单独作为一个 route 文件的话,管理起来就很方便,但同样会存在一个问题,没增加一个模块就要手动引入一次,很麻烦,一点也不优雅,不过解决办法也很简单,只需要做一个文件夹循环遍历就行了,把指定文件夹下的文件循环遍历一遍,每一个文件中的 route 信息都暴露出来汇总到一个 route 下就可以了

设置路由

首先,我们在 routes 文件夹下 新建一个 api 文件夹,在 api 下新建 v1 文件夹,表示我们的 api 接口是第一个版本的,然后,在 v1 文件夹下新建 cats.js

1
2
3
4
5
6
7
8
9
10
//@ routes/api/v1/cats.js
'use strict';
const catsCtrl = require('../../../controllers/api/v1/cats');
module.exports = (router) => {
router.get('/getcats', catsCtrl.getCats);
router.get('/addcat/:name', catsCtrl.addCat);
};

这里我们使用了 controller 的概念来作为路由的控制器,用来控制响应路由后的返回值

在根目录下新建 controller 文件夹,对应 routes 文件夹的结构新建 api/v1 的目录,
v1 下新建对应的 cats.js 文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//@ controllers/api/v1/cats.js
'use strict';
module.exports = {
getCats,
addCat
};
async function getCats (req, res, next) {
res.send(req.path);
}
async function addCat (req, res, next) {
res.send(req.params.name);
}

这里我们向外暴露了一组 cats 模块的响应方法,用到的async 主要是用来在后面对数据库操作时用 async/await 解决异步回调,这就要求 node 版本不能低于 7.6

然后回到 routes/api/v1/cats.js

1
2
router.get('/getcats', catsCtrl.getCats);
router.get('/addcat/:name', catsCtrl.addCat);

这里注册了两个路由,调用 /getcats 时的返回值由 controllers 中的 getCats 方法控制,以此类推

把所有路由汇总导出

routes/index.js 中,我们要把 routes/api/v1 下的所有路由都汇总,那就需要循环遍历这些文件并获得这些暴露出来的路由

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
//@ routes/index.js
'use strict';
const express = require('express');
const path = require('path');
const fs = require('fs');
function createRouter (versionDir) {
let router = express.Router();
fs.readdirSync(versionDir).forEach(function (file) {
require(path.join(versionDir, file))(router);
});
return router;
}
const router = module.exports = express.Router();
const v1 = createRouter(path.join(__dirname, 'api/v1'));
router.use('/', v1);
router.use('/api/v1', v1);

createRouter 方法中,对传入的目录下的文件进行了循环遍历
require(path.join(versionDir, file))(router);将遍历结果分别 require 并添加到 router,这样我们就获得了 /api/v1 目录下的全部路由

1
2
router.use('/', v1);
router.use('/api/v1', v1);

将路由分别添加到根路径 / 和版本路径 /api/v1 下,这样就可以同时通过根路径或自定义路径进行接口的访问

最后,将刚完成的路由管理部分添加到server

1
2
3
4
5
6
7
//@ server/index.js
...
const router = require('../routes');
...
app.use(router);
...

重启服务,打开浏览器,分别输入

1
http://127.0.0.1:10086/getcats


1
http://127.0.0.1:10086/addcat/123

如果看到类似上面截图的内容,说明我们的路由管理成功运行了,如果有问题,可以根据错误日志去寻找答案

关于路由的各种参数的使用这里不再多说,如果需要的话可以自己去寻找答案,因为再写多一点就显得不那么 优雅

小结

到这里基本就完成了一个 express 框架的搭建

什么?你说还不够?

恩,要优雅,下一篇中将会加入 Mysql 数据库的管理和 RESTful API 的返回值的统一处理,我们下一站再见,下车了