路由系统与URL处理
Express框架的路由系统是处理HTTP请求的核心机制,它通过URL路径与HTTP方法的组合,将客户端请求映射到特定的处理函数。URL处理涉及路径匹配、参数提取、查询字符串解析等关键功能,直接影响Web应用的API设计与资源定位效率。
路由基础结构
Express路由由HTTP方法、路径模式和回调函数三部分组成。基本路由定义采用app.METHOD(path, handler)
形式,例如:
const express = require('express');
const app = express();
// 基础GET路由
app.get('/products', (req, res) => {
res.send('产品列表');
});
// 带参数的路由
app.post('/users/:id', (req, res) => {
console.log(req.params.id);
res.status(201).send();
});
路由方法支持所有HTTP动词,包括get
、post
、put
、delete
等,也提供all
方法处理任意方法的请求:
app.all('/secure', (req, res) => {
console.log(`收到${req.method}请求`);
res.sendStatus(403);
});
路由路径匹配规则
Express支持多种路径匹配模式:
- 字符串路径:
/about
- 字符串模式:
/ab?cd
(匹配acd或abcd) - 正则表达式:
/.*fly$/
- 参数化路径:
/users/:userId
复杂路径匹配示例:
// 多段参数
app.get('/books/:category/:id', (req, res) => {
const { category, id } = req.params;
res.json({ category, id });
});
// 正则约束
app.get('/user/:id(\\d+)', (req, res) => {
res.send(`用户ID: ${req.params.id}`);
});
// 通配路径
app.get('/files/*', (req, res) => {
res.send(`请求文件路径: ${req.params[0]}`);
});
路由参数处理
路径参数通过req.params
对象暴露,支持可选参数和多个参数段:
// 可选参数
app.get('/archive/:year?/:month?', (req, res) => {
const { year = '2023', month = '01' } = req.params;
res.send(`${year}-${month}归档`);
});
// 参数验证中间件
const validateId = (req, res, next) => {
if (!/^\d+$/.test(req.params.id)) {
return res.status(400).send('无效ID格式');
}
next();
};
app.get('/api/items/:id', validateId, (req, res) => {
// 处理有效ID
});
查询字符串解析
URL查询参数自动解析到req.query
对象,支持复杂结构:
// 处理/search?q=express&page=2
app.get('/search', (req, res) => {
const { q, page = 1 } = req.query;
res.send(`搜索"${q}",第${page}页`);
});
// 数组参数 /filters?color=red&color=blue
app.get('/filters', (req, res) => {
console.log(req.query.color); // ['red', 'blue']
});
路由模块化组织
Express.Router类实现路由模块化:
// routes/users.js
const router = require('express').Router();
router.get('/', (req, res) => {
res.send('用户列表');
});
router.get('/:id', (req, res) => {
res.send(`用户详情: ${req.params.id}`);
});
module.exports = router;
// 主文件
const userRoutes = require('./routes/users');
app.use('/users', userRoutes);
支持路由级中间件和路径前缀:
const apiRouter = express.Router();
// 应用API鉴权中间件
apiRouter.use(authenticate);
apiRouter.get('/data', (req, res) => {
res.json({ data: '敏感信息' });
});
app.use('/api/v1', apiRouter);
路由中间件系统
路由处理支持多个中间件函数,形成处理链:
const logRequest = (req, res, next) => {
console.log(`${req.method} ${req.path}`);
next();
};
const validateData = (req, res, next) => {
if (!req.body) return res.status(400).send('需要请求体');
next();
};
app.post('/orders', logRequest, validateData, (req, res) => {
// 处理有效订单
res.status(201).json({ id: Date.now() });
});
错误处理中间件示例:
app.get('/danger', (req, res, next) => {
try {
throw new Error('模拟错误');
} catch (err) {
next(err);
}
});
// 错误处理中间件
app.use((err, req, res, next) => {
console.error(err.stack);
res.status(500).send('服务器错误');
});
路由加载顺序
Express按路由定义顺序匹配,需要注意优先级:
// 这个路由会拦截所有/admin路径
app.get('/admin*', (req, res) => {
res.send('管理员区域');
});
// 这个路由永远不会被触发
app.get('/admin/settings', (req, res) => {
res.send('管理员设置');
});
正确做法应把具体路径放在前面:
app.get('/admin/settings', (req, res) => {
res.send('管理员设置');
});
app.get('/admin*', (req, res) => {
res.send('其他管理员页面');
});
动态路由加载
实现按需加载路由模块的示例:
const fs = require('fs');
const path = require('path');
const routesDir = path.join(__dirname, 'routes');
fs.readdirSync(routesDir).forEach(file => {
const routePath = path.join(routesDir, file);
const route = require(routePath);
const baseName = path.basename(file, '.js');
app.use(`/${baseName}`, route);
console.log(`加载路由: /${baseName}`);
});
路由性能优化
大规模路由表的优化策略:
// 使用路由表代替连续注册
const routes = [
{ method: 'GET', path: '/api/v1/users', handler: getUsers },
{ method: 'POST', path: '/api/v1/users', handler: createUser }
];
routes.forEach(({ method, path, handler }) => {
app[method.toLowerCase()](path, handler);
});
// 路径缓存中间件
const routeCache = {};
app.use((req, res, next) => {
const cacheKey = `${req.method}:${req.path}`;
if (routeCache[cacheKey]) {
return routeCache[cacheKey](req, res);
}
next();
});
高级路径匹配技巧
实现RESTful资源路由的完整示例:
const resources = ['posts', 'comments', 'likes'];
resources.forEach(resource => {
const controller = require(`./controllers/${resource}`);
app.route(`/api/${resource}`)
.get(controller.index)
.post(controller.create);
app.route(`/api/${resource}/:id`)
.get(controller.show)
.put(controller.update)
.delete(controller.destroy);
});
支持内容协商的路由处理:
app.get('/data', (req, res) => {
res.format({
'text/plain': () => {
res.send('文本数据');
},
'application/json': () => {
res.json({ data: 'JSON数据' });
},
default: () => {
res.status(406).send('不支持的格式');
}
});
});
路由与视图系统集成
动态路由结合模板引擎的典型应用:
app.set('view engine', 'ejs');
app.get('/products/:slug', async (req, res) => {
try {
const product = await db.findProductBySlug(req.params.slug);
res.render('product-detail', { product });
} catch (err) {
res.status(404).render('404');
}
});
现代化路由实践
使用async/await处理异步路由:
app.get('/async-data', async (req, res, next) => {
try {
const data = await fetchRemoteData();
const processed = await processData(data);
res.json(processed);
} catch (err) {
next(err);
}
});
基于Promise的路由错误处理:
const asyncHandler = fn => (req, res, next) => {
Promise.resolve(fn(req, res, next))
.catch(next);
};
app.get('/promise-route', asyncHandler(async (req, res) => {
const result = await complexOperation();
res.send(result);
}));
路由调试技巧
开发阶段的路由调试方法:
// 显示所有注册路由
app._router.stack.forEach(layer => {
if (layer.route) {
const methods = Object.keys(layer.route.methods)
.filter(method => layer.route.methods[method])
.join(', ').toUpperCase();
console.log(`${methods} ${layer.route.path}`);
}
});
// 路由追踪中间件
app.use((req, res, next) => {
console.log(`[${new Date().toISOString()}] ${req.method} ${req.path}`);
next();
});
本站部分内容来自互联网,一切版权均归源网站或源作者所有。
如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn
下一篇:中间件机制与执行流程