作为一个使用Node.js多年的开发者,我已经习惯于用Node.js写一些web应用来为工作服务,因为实现快速、部署简单、自定义强。今天我们一起来学习一个全栈工程师必备技能:web路由。(观看此文的前提是默认你已经装好nonde.js了)
Node.js 中的路由是构建 Web 应用的核心概念之一,帮助我们根据用户请求的 URL 和 HTTP 方法(GET、POST 等)做出不同的响应。
下面我们由浅入深,循序渐进地讲解 Node.js 的路由实现:
1. 什么是路由?
【路由(Routing)】是指确定服务器响应用户请求的方式,也就是你访问网站的哪个路径,服务器根据路径响应你相应的内容。
比如某网站 www.duniang.com/路由1 、 www.duniang.com/路由1/路由1的儿子
路由主要根据两个要素:
- 请求的 URL(如
/home
或/about
) - 请求的方法(如
GET
、POST
、PUT
、DELETE
)
关于这一点网上有很多讨论,很多人基本就是 GET / POST 一招吃遍天下,这也无可厚非。不过建议开发者还是老老实实 规规矩矩的把习惯养好,该PUT就PUT 该DELETE就DELETE。
2. 用原生 Node.js 实现简单路由
用原生的 http
模块,我们可以通过检查请求的 URL 和方法来实现路由。以下是一个简单的例子:
const http = require('http');
const server = http.createServer((req, res) => {
const { url, method } = req;
if (url === '/' && method === 'GET') {
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.end('Welcome to Home Page');
} else if (url === '/about' && method === 'GET') {
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.end('This is the About Page');
} else {
res.writeHead(404, { 'Content-Type': 'text/plain' });
res.end('Page Not Found');
}
});
server.listen(3000, () => {
console.log('Server running at http://localhost:3000/');
});
特点:
- 手动判断
url
和method
。 - 适合学习,但不适合复杂应用,代码难以维护。
http是node内核自带的网络服务模块,可以快速创建http服务。
将上面代码命名为 app.js ,使用 node app.js 命令 启动
创建了一个端口为3000的 webServer,我我们可以通过 http://127.0.0.1:3000访问:
3. 使用 Express 简化路由
http 这个模块使用起来还是不太方便,观感不太好,代码量也比较大,如果路由复杂,要写很多代码。所以就有了 Express
Express 是一个轻量级、流行的 Node.js 框架,它对路由的处理非常直观。我们可以用它创建清晰、可扩展的路由。
安装 Express:
npm install express
简单路由示例:
const express = require('express');
const app = express();
app.get('/', (req, res) => {
res.send('Welcome to Home Page');
});
app.get('/about', (req, res) => {
res.send('This is the About Page');
});
// 404 handler
app.use((req, res) => {
res.status(404).send('Page Not Found');
});
app.listen(3000, () => {
console.log('Server running at http://localhost:3000/');
});
优点:
- 简洁的 API,如
app.get()
、app.post()
。 - 支持中间件,可以扩展功能(如验证、日志)。
上面这个代码就清楚多了。 Express在现实中的应用非常广泛,它简单、直观,使用起来也很灵活。
4. 动态路由
动态路由允许我们为具有相似结构的 URL 创建单一路由。例如,处理用户的 ID:
app.get('/user/:id', (req, res) => {
const userId = req.params.id; // 获取动态参数
res.send(`User ID is ${userId}`);
});
访问 http://localhost:3000/user/123
,响应为:
User ID is 123
请注意,这里的网址是 /user/minstbe 后面这个/minstbe 本来应该是一个路由,但是这里被定义为一个参数 ,它等效于 /user?id=minstbe 。很多时候我们如果不希望网页带有问号、等于号,就可以用这种方式来定义传参。
req.params
:存储动态 URL 参数。
如果我们在服务器端 console.log(req.params) 就会看到打印出了这个Object类型的参数:
5. 路由模块化
当路由逻辑增多时,我们可以将路由分离到单独的文件中,提高代码的可读性和可维护性。实际生产过程中,还会用到更加高级的分离方式 这个我们以后再介绍。
1. 创建路由模块(routes/user.js
):
在webServer的主目录下创建 文件夹 routes ,并创建user.js,用来对 /user做出响应
const express = require('express');
const router = express.Router();
router.get('/:id', (req, res) => {
const userId = req.params.id;
res.send(`User Profile of ID: ${userId}`);
});
module.exports = router;
2. 在主文件中引入路由模块(index.js
):
const express = require('express');
const app = express();
const userRoutes = require('./routes/user'); // 这里引入了这个路由模块
app.use('/user', userRoutes); // 访问路径为 /user/:id
app.listen(3000, () => {
console.log('Server running at http://localhost:3000/');
});
上面两个文件结合使用,启动index.js 后如下:
6. 中间件与路由结合
中间件是在路由处理前或后执行的一段代码,用于实现日志、验证、解析等功能。
中间件在Node.js的webServer中非常重要,很多功能都是在中间件实现的,通过中间件处理获取到的内容,再交给后面的api来处理。
示例:
const logger = (req, res, next) => {
console.log(`${req.method} ${req.url}`);
next(); // 必须调用 next() 才能进入下一个中间件或路由
};
app.use(logger);
app.get('/', (req, res) => {
res.send('Home Page with Logger');
});
下面是在/user 之前 加入了一个中间件,也就是显示 访问方式 和 路径,然后再继续 交给 /user判断处理:(请注意控制台的输出)
7. 高级路由功能
- 处理多种 HTTP 方法:
app.route('/book')
.get((req, res) => res.send('Get a book'))
.post((req, res) => res.send('Add a book'))
.put((req, res) => res.send('Update a book'));
app.route()
是 Express 提供的一个链式路由定义方法,用于处理针对 同一路径 的多个 HTTP 方法(如 GET
、POST
、PUT
等)的请求。
工作原理
app.route(path)
会创建一个单独的路由路径对象,可以通过链式调用分别为该路径定义不同的 HTTP 方法处理逻辑。这种写法能使代码更加清晰、结构更紧凑。
具体解析
app.route('/book')
.get((req, res) => res.send('Get a book'))
.post((req, res) => res.send('Add a book'))
.put((req, res) => res.send('Update a book'));
-
app.route('/book')
定义了一个路径/book
的路由。 -
.get()
- 处理
GET /book
请求。 - 回调函数
(req, res)
用于返回一个响应,当前返回内容是'Get a book'
。
- 处理
-
.post()
- 处理
POST /book
请求。 - 回调函数
(req, res)
返回'Add a book'
。
- 处理
-
.put()
- 处理
PUT /book
请求。 - 回调函数
(req, res)
返回'Update a book'
。
- 处理
等价写法
上述代码的功能可以用多个单独的路由来实现:
app.get('/book', (req, res) => res.send('Get a book'));
app.post('/book', (req, res) => res.send('Add a book'));
app.put('/book', (req, res) => res.send('Update a book'));
对比:
- 使用
app.route()
方法:- 逻辑集中在一个地方,便于管理和阅读。
- 适合路径相同但处理方法不同的场景。
- 使用分开定义的路由:
- 路径重复,代码冗长,不够直观。
扩展:何时使用 app.route()
?
- 当一个路径需要支持多种 HTTP 方法时,比如
/book
需要处理GET
、POST
和PUT
。 - 希望减少路径的重复书写,提高代码可读性和组织性。
注意:
app.route()
仅适用于同一个路径。如果处理的路径不同(比如/book
和/author
),仍需单独定义路由。- 它仅是 Express 提供的一种语法糖,本质上和单独写多个路由的逻辑一样。
- 正则匹配路由:
app.get(/.*fly$/, (req, res) => {
res.send('Route matched with /.*fly$/');
});
// 例如,访问 /butterfly、/dragonfly 都可以匹配
好了 今天,就简单介绍一下,下次再继续!
- 原生实现:适合理解底层原理。
- Express:简化路由逻辑,是构建 Web 应用的主流选择。
- 动态路由:为类似的请求模式处理提供便利。
- 模块化路由:适合复杂项目。
- 中间件支持:增强功能的可扩展性。
可以从简单的例子开始尝试,然后逐渐增加复杂度,适应实际的开发场景!你想尝试哪种实现?