🔽 前言
在 Node.js | express 框架开篇 中我们说到express
为我们提供的最强大的能力就是一套超级简单且强大的路由系统,那到底什么是路由呢?
路由是指如何定义应用的端点(URIs
)以及如何响应客户端的请求。
路由是由一个 URI
、HTTP
请求(GET
、POST
等)和若干个句柄组成,它的结构如下:
app.METHOD(path, [callback...], callback)
app
是express
对象的一个实例METHOD
是一个HTTP
请求方法path
是服务器上的路径callback
是当路由匹配时要执行的函数
这篇文章我们将深入去学习express
的路由系统,让我们开始吧!
1️⃣ 基础路由
先定义一个基础的路由:
const express = require("express");
// app对象
const app = express();
app.get("/", (req, res) => {
res.send("匹配/");
});
上面的例子表示的是一个路径为/
的GET
请求路由,即当用户使用GET
请求访问/
时会执行(req, res) =>{res.send("匹配/");}
函数。
路由路径和请求方法一起定义了请求的端点:
// 匹配 / 路径的get请求
app.get("/", (req, res) => {
res.send("匹配 / 路径的get请求");
});
// 匹配 / 路径的post请求
app.post("/", (req, res) => {
res.send("匹配 / 路径的post请求");
});
// 匹配 / 路径的put请求
app.put("/", (req, res) => {
res.send("匹配 / 路径的put请求");
});
// 匹配 / 路径的delete请求
app.delete("/", (req, res) => {
res.send("匹配 / 路径的delete请求");
});
观察上面的路由你会发现它们的请求路径是一样的,只是请求方式不同,这是允许的,因为相同路径的请求只要请求方法不同最后就会进入到不同的处理函数中。
这种风格的写法称为RES Tful,在开发API
接口中比较推荐使用。
2️⃣ 路径匹配
在 Node.js | express 框架开篇 中我们已经提到过express
的路由可以进行模糊匹配,这是因为express
的路由路径可以是字符串、字符串模式或者正则表达式。
🔹 字符串匹配
当路径使用字符串时进行的就是普通的匹配,路径是什么就匹配什么:
// 匹配根路径的请求
app.get('/', function (req, res) {
res.send('root');
});
// 匹配 /about 路径的请求
app.get('/about', function (req, res) {
res.send('about');
});
🔹 字符串模式匹配
在字符串的基础上添加一些额外的语法(如下)称为字符串模式。
?
表示可选:id
表示使用id
字段占位+
表示可出现多次(至少一次)*
表示任意
使用字符串模式的路径:
// 匹配 /acd 和 /abcd:?前边的字符代表可选
app.get("/ab?cd", function (req, res) {
res.send("ok");
});
// 匹配 /ab/*******::id代表占位,这个id是自定义的(你也可以写:aa,:bb等),之后能用来获取/ab/后的参数
app.get("/ab/:id", function (req, res) {
res.send("/ab/*******");
});
// 匹配 /abcd,/abbcd,/abbbcd等,+号前的字符可以连续出现多个
app.get("/ab+cd", function (req, res) {
res.send("/ab+cd");
});
// 匹配 /abcd,/abxcd,/abBIHIHIcd,/ab1456cd等,*号代表任意字符
app.get("/ab*cd", function (req, res) {
res.send("/ab*cd");
});
// 匹配 /abe,/abcde,可以使用括号包裹多个字符形成一个整体
app.get("/ab(cd)?e", function (req, res) {
res.send("/ab(cd)?e");
});
🔹 正则匹配
express
路由匹配中最强大的一点就是它能够根据正则来进行匹配(虽然这在开发中用处不多)。
使用正则表达式的路由路径:
// 匹配任何路径中含有 a 的路径:
app.get(/a/, function (req, res) {
res.send("/a/");
});
// 匹配 butterfly、dragonfly,不匹配 butterflyman、dragonfly man等
app.get(/.*fly$/, function (req, res) {
res.send("/.*fly$/");
});
3️⃣ 处理函数
可以为请求处理提供多个回调函数,其行为类似 中间件。
🔹 多函数
使用多个回调函数处理路由(记得指定 next
对象):
// 直接添加多个回调函数
app.get(
"/",
(req, res, next) => {
console.log("第一个执行");
next();
},
(req, res, next) => {
console.log("第二个执行");
next();
// next('route') next中添加route参数时会跳过之后的回调函数
},
(req, res) => {
console.log("最后一个执行");
// 返回数据内容
res.send("home");
}
);
🔹 函数数组
还可以以数组的形式添加回调函数:
const f1 = (req, res, next) => {
console.log("第一个执行");
next();
};
const f2 = (req, res, next) => {
console.log("第二个执行");
next();
};
const f3 = (req, res) => {
console.log("最后一个执行");
res.send("login");
};
app.get("/login", [f1, f2, f3]);
🔹 混合使用
混合使用函数和函数数组处理路由:
const fn1 = function (req, res, next) {
console.log("fn1");
next();
};
var fn2 = function (req, res, next) {
console.log("fn2");
next();
};
app.get(
"/about",
[fn1, fn2],
function (req, res, next) {
console.log("fn3");
next();
},
function (req, res) {
res.send("about");
}
);
🔹 应用
可以利用该机制为路由定义前提条件,如果在现有路径上继续执行没有意义,则可将控制权交给剩下的路径。
例如在开发中经常使用到的token
验证,假如现在共有三个接口:
/login
用户登录的接口,不需要token
验证/home
只有token
验证通过才能访问/user
只有token
验证通过才能访问
这时我们就可以这样开发:
// 不需要token验证
app.get("/login", (req, res) => {
res.send("登录");
});
// 验证token的函数(可插拔,可复用的中间件)
const isToken = (req, res, next) => {
// 验证token是否过期,isVaild代表token是否有效
// 一些操作
const isVaild = true; // 假设验证通过
if (isVaild) {
// 验证通过了就调用next向下执行
console.log("token验证通过");
// 如果想要在回调函数之间传递数据,我们可以选择将数据挂载到res参数上
res.ailjx = "海底烧烤店ai";
next();
} else {
// 返回错误
// 这里res.send后后面的回调函数就不会再执行了
res.send("token验证失败!");
}
};
// 只有token有效时才生效的接口
app.get("/home", [isToken], (req, res) => {
// 返回数据内容
// res.ailjx:获取验证token的函数在res上绑定的数据
res.send("home" + res.ailjx);
});
app.get("/user", [isToken], (req, res) => {
// 返回数据内容
res.send("user");
});
上面我们将验证token
的函数抽离了出去,之后在需要该函数的路由中直接引用即可,这就避免了我们需要在每个路由中都写一遍token验证的繁琐过程。
4️⃣ 路由级中间件
上面我们定义的路由都是直接绑定到了app
对象上,这称为应用级中间件。
当我们的路由过多时,这势必会变得难以维护,于是就出现了路由级中间件用来将我们的路由抽离出来,路由级中间件和应用级中间件一样,只是它绑定的对象为 express.Router()
。
// apiRouter.js
const express = require("express");
// router对象
const router = express.Router();
// 路由级别中间件:api路由
router.get("/home", (req, res) => {
res.send({
list: [1, 2, 3, 4],
});
});
router.get("/about", (req, res) => {
res.send({
name: "ailjx",
age: 18,
});
});
// 导出
module.exports = router;
// server.js
const express = require("express");
const app = express();
// 导入api路由
const apiRouter = require("./route/apiRouter");
// 使用app.use将路由挂载至应用
app.use(apiRouter);
app.listen(3000, () => {
console.log("start");
});
上面我们使用路由级中间件定义路由,之后通过模块化导入的形式将路由导入,再使用app.use
将路由挂载至应用,这种代码结构就使得整个项目变得更加容易维护。
在挂载路由时我们也可以指定一级路径:
// 如果use具有第一个路径参数,如下面的/api,则/api相当于是一级路径,apiRouter里的路径就相当于二级路径了
app.use("/api", apiRouter);
这样挂载后的路由访问时就需要加上/api
,如:
/api/home
/api/about
🔼 结语
本篇文章详细介绍了express
路由的使用,并由此引入路由级中间件的使用(关于express中间件
博主之后会出单独的文章进行讲解)。
博主的Node.js从入门到精通专栏正在持续更新中,关注博主订阅专栏学习Node不迷路!
如果本篇文章对你有所帮助,还请客官一件四连!❤️