路由配置
Express框架中的路由用来匹配HTTP请求方法和路径,并将请求交给对应的处理函数进行处理和响应,本篇笔记我们学习Express框架中路由的用法。
创建Express路由
Express中创建路由有两种方式,直接使用express对象创建路由,或是使用Router中间件。实际开发中我们通常选择第二种,这种方式更利于代码拆分,将路由单独抽离成中间件也更利于我们对所有中间件在处理优先级上的定制安排。下面例子代码演示了使用Router中间件创建路由。
import express from "express";
const router = express.Router();
const app = express();
router.get("/", (req, res) => {
res.send("Hello, world!");
});
app.use(router);
app.listen(8080, () => {
console.log("Express server listen on 8080");
});
代码中,我们创建了匹配GET /路径的路由。
路由匹配
Express路由使用了path-to-regexp库实现路径匹配,它支持精确匹配、通配符匹配以及Restful风格的路径参数。此外Express也支持正则表达式匹配。
基础路由匹配
下面展示一些Express基础路由的写法。
router.get("/", (req, res, next) => {
res.status(200).send("Match /");
});
router.get("/zh-cn/about", (req, res, next) => {
res.status(200).send("Match /zh-cn/about");
});
router.get("/data.txt", (req, res, next) => {
res.status(200).send("Match /data.txt");
});
上面3种是最常用的精确匹配模式,Express会准确匹配路由字符串并交给对应的处理函数。
router.get("/ab*cd", (req, res, next) => {
res.status(200).send("Match /abcd /abxcd /abxyzcd ...");
});
路由中的*通配符表示该位置可以是任意内容,也可以为空。这种方式可以实现前缀或后缀的通配符匹配,比如/*、/*.do等。
router.get("/ab?cd", (req, res, next) => {
res.status(200).send("Match /acd /abcd");
});
router.get("/ab(cd)?e", (req, res, next) => {
res.status(200).send("Match /abe /abcde");
});
路由中的?通配符表示前一个字符或括号包含的内容可能不存在。
router.get("/ab+cd", (req, res, next) => {
res.status(200).send("Match /abcd /abbcd /abbbcd ...");
});
路由中的+通配符表示前一个字符可能重复1次或多次,+通配符不支持括号。
注意:如果使用通配符导致同一个请求路径可以被多个路由匹配,此时会按照定义路由的顺序从上到下进行匹配,前面的路由匹配后就不会再执行后面的路由了。
Restful路径参数路由
Express路由中使用:符号表示一个路径参数。
router.get("/student/:studentId", (req, res, next) => {
res.status(200).send("Student ID [" + req.params.studentId + "]");
});
上面代码可以匹配/student/1,并将对应的路径参数解析后传入处理函数,而不能匹配/student。
正则表达式路由匹配
Express还支持正则表达式路由。
router.get(/.*fly$/, (req, res, next) => {
res.status(200).send("Match");
});
例子中使用了正则表达式/.*fly$/,因此这条路由能够匹配/butterfly、/dragonfly等,不能匹配/flyaaa等。
HTTP方法
Express支持HTTP协议的所有方法,常用的有GET、POST、PUT、DELETE等,这些HTTP方法对应路由的同名函数。下面例子代码中,我们针对GET /和POST /两种请求方法进行了不同的响应处理。
import express from "express";
const router = express.Router();
const app = express();
router.get("/", (req, res) => {
res.send("这是GET方法");
});
router.post("/", (req, res) => {
res.send("这是POST方法");
});
app.use(router);
app.listen(8080, () => {
console.log("Express server listen on 8080");
});
代码中,虽然两条路由匹配的都是/路径,但它们匹配不同的HTTP请求方法,因此不会相互冲突。
路由模块化
实际项目中,我们通常将路由按功能模块拆分到不同文件中,然后在主文件中统一挂载。
routes/user.js
import express from "express";
const router = express.Router();
router.get("/list", (req, res) => {
res.send("用户列表");
});
router.get("/:id", (req, res) => {
res.send("用户详情: " + req.params.id);
});
export default router;
app.js
import express from "express";
import userRouter from "./routes/user.js";
const app = express();
app.use("/user", userRouter);
app.listen(8080, () => {
console.log("Express server listen on 8080");
});
代码中,我们将用户相关的路由抽离到routes/user.js文件中,然后在主文件中通过app.use("/user", userRouter)挂载到/user路径前缀下。此时访问/user/list会匹配到用户列表路由,访问/user/123会匹配到用户详情路由。