Express框架入门:从基础搭建到路由系统详解
1. Express基础环境搭建与服务器创建
作为Node.js最流行的Web框架之一,Express让服务器开发变得异常简单。我们先从最基础的服务器搭建开始:
1.1 项目初始化与Express安装
首先创建一个空目录作为项目根目录,执行npm初始化命令:
mkdir express-demo && cd express-demo npm init -y接着安装Express核心包:
npm install express --save提示:使用--save参数会将依赖自动写入package.json,这是现代Node.js项目的标准做法。如果是老版本npm(5.0之前),需要显式添加这个参数。
1.2 创建基础服务器
新建server.js作为入口文件,写入以下代码:
const express = require('express'); const app = express(); app.get('/', (req, res) => { res.send('欢迎来到Express世界!'); }); app.listen(3000, () => { console.log('服务器已启动,访问地址:http://localhost:3000'); });这个最简单的Express服务器已经可以处理GET请求。相比原生Node.js的http模块,Express用几行代码就实现了路由功能。
1.3 开发工具配置
为了提高开发效率,建议安装nodemon工具:
npm install -g nodemon然后使用nodemon启动服务器:
nodemon server.jsnodemon会监控文件变化自动重启服务,这在开发阶段非常实用。如果入口文件不是server.js而是app.js,则需指定文件名:
nodemon app.js2. Express路由系统深度解析
2.1 基础路由配置
Express的路由定义非常直观,基本语法是:
app.METHOD(PATH, HANDLER)其中:
- METHOD可以是get/post/put/delete等HTTP方法
- PATH是URL路径
- HANDLER是处理函数
示例:
app.get('/api/users', (req, res) => { res.json([{id: 1, name: '张三'}, {id: 2, name: '李四'}]); });2.2 动态路由参数
路由可以包含动态参数,通过req.params获取:
app.get('/users/:userId/posts/:postId', (req, res) => { console.log(req.params); // { userId: '123', postId: '456' } res.send(`用户${req.params.userId}的文章${req.params.postId}`); });2.3 路由正则匹配
Express路由支持正则表达式,提供更灵活的匹配方式:
// 匹配/acd或/abcd app.get('/ab?cd', (req, res) => { res.send('匹配成功'); }); // 匹配/1xx2,其中x可以是任意字符 app.get('/1.*2', (req, res) => { res.send('通配符匹配'); });2.4 查询字符串处理
对于URL中的查询参数(?key=value),Express自动解析并存入req.query:
app.get('/search', (req, res) => { // 访问/search?q=express&page=2 console.log(req.query); // { q: 'express', page: '2' } res.send(`搜索关键词:${req.query.q},第${req.query.page}页`); });3. 请求处理与数据获取
3.1 GET请求处理
GET请求是最常见的HTTP方法,Express已经内置支持:
app.get('/products', (req, res) => { const products = [ {id: 1, name: '手机', price: 3999}, {id: 2, name: '笔记本', price: 5999} ]; res.json(products); });3.2 POST请求与body-parser
处理POST请求需要body-parser中间件来解析请求体:
npm install body-parser --save使用示例:
const bodyParser = require('body-parser'); // 解析application/x-www-form-urlencoded app.use(bodyParser.urlencoded({ extended: false })); // 解析application/json app.use(bodyParser.json()); app.post('/login', (req, res) => { console.log(req.body); // 获取POST数据 res.send(`欢迎,${req.body.username}`); });注意:extended: false表示使用querystring库解析URL编码数据,true表示使用qs库。对于简单数据false即可。
3.3 不同内容类型的处理
可以为不同路由指定不同的解析器:
const jsonParser = bodyParser.json(); const urlencodedParser = bodyParser.urlencoded({ extended: false }); // 只处理JSON数据 app.post('/api/users', jsonParser, (req, res) => { // ... }); // 只处理表单数据 app.post('/contact', urlencodedParser, (req, res) => { // ... });4. 文件上传与multer中间件
4.1 multer基础配置
处理文件上传需要multer中间件:
npm install multer --save基本使用:
const multer = require('multer'); const upload = multer({ dest: 'uploads/' }); app.post('/upload', upload.single('avatar'), (req, res) => { console.log(req.file); // 上传文件信息 res.send('上传成功'); });对应的HTML表单:
<form action="/upload" method="post" enctype="multipart/form-data"> <input type="file" name="avatar"> <button type="submit">上传</button> </form>4.2 自定义存储配置
multer允许自定义文件名和存储路径:
const storage = multer.diskStorage({ destination: (req, file, cb) => { cb(null, 'uploads/'); }, filename: (req, file, cb) => { const uniqueSuffix = Date.now() + '-' + Math.round(Math.random() * 1E9); cb(null, file.fieldname + '-' + uniqueSuffix + path.extname(file.originalname)); } }); const upload = multer({ storage: storage });4.3 文件过滤与限制
可以添加文件类型和大小限制:
const fileFilter = (req, file, cb) => { if (file.mimetype === 'image/jpeg' || file.mimetype === 'image/png') { cb(null, true); } else { cb(new Error('只支持JPEG/PNG格式'), false); } }; const upload = multer({ storage: storage, limits: { fileSize: 1024 * 1024 * 5 // 限制5MB }, fileFilter: fileFilter });5. EJS模板引擎实战
5.1 EJS基础配置
安装EJS模板引擎:
npm install ejs --save配置Express使用EJS:
app.set('views', path.join(__dirname, 'views')); app.set('view engine', 'ejs');5.2 基本模板渲染
创建views/index.ejs:
<!DOCTYPE html> <html> <head> <title><%= title %></title> </head> <body> <h1><%= message %></h1> </body> </html>路由中渲染模板:
app.get('/', (req, res) => { res.render('index', { title: 'Express首页', message: '欢迎使用EJS模板引擎' }); });5.3 模板逻辑控制
EJS支持JavaScript逻辑:
<% if (user) { %> <h2>欢迎, <%= user.name %></h2> <% } else { %> <a href="/login">请登录</a> <% } %> <ul> <% items.forEach(item => { %> <li><%= item %></li> <% }) %> </ul>5.4 模板布局与包含
创建公共头部views/partials/header.ejs:
<header> <nav> <a href="/">首页</a> <a href="/about">关于</a> </nav> </header>在其他模板中包含:
<%- include('partials/header') %> <main> <!-- 页面主要内容 --> </main>6. 项目优化与最佳实践
6.1 路由模块化
将路由拆分到单独文件routes/users.js:
const express = require('express'); const router = 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);6.2 错误处理中间件
自定义错误处理:
// 404处理 app.use((req, res, next) => { res.status(404).render('404'); }); // 错误处理 app.use((err, req, res, next) => { console.error(err.stack); res.status(500).render('500'); });6.3 安全最佳实践
重要安全措施:
const helmet = require('helmet'); app.use(helmet()); // 防止跨站请求伪造 const csrf = require('csurf'); app.use(csrf({ cookie: true })); // 限流保护 const rateLimit = require('express-rate-limit'); const limiter = rateLimit({ windowMs: 15 * 60 * 1000, // 15分钟 max: 100 // 每个IP限制100次请求 }); app.use(limiter);6.4 性能优化技巧
提升Express性能的方法:
// 启用gzip压缩 const compression = require('compression'); app.use(compression()); // 静态文件缓存 app.use(express.static('public', { maxAge: '1y', immutable: true })); // 集群模式 const cluster = require('cluster'); const numCPUs = require('os').cpus().length; if (cluster.isMaster) { for (let i = 0; i < numCPUs; i++) { cluster.fork(); } } else { // 启动Express应用 }7. 常见问题与解决方案
7.1 跨域问题处理
使用cors中间件解决跨域:
npm install cors --save配置示例:
const cors = require('cors'); // 允许所有来源 app.use(cors()); // 自定义配置 app.use(cors({ origin: 'https://example.com', methods: ['GET', 'POST'], allowedHeaders: ['Content-Type'] }));7.2 静态文件服务
托管静态文件目录:
app.use(express.static('public')); // 可以添加多个静态目录 app.use('/uploads', express.static('uploads'));7.3 会话管理
使用express-session管理会话:
npm install express-session --save基本配置:
const session = require('express-session'); app.use(session({ secret: 'your-secret-key', resave: false, saveUninitialized: true, cookie: { secure: true } }));7.4 数据库集成
连接MongoDB示例:
const mongoose = require('mongoose'); mongoose.connect('mongodb://localhost:27017/express-demo', { useNewUrlParser: true, useUnifiedTopology: true }); const User = mongoose.model('User', { name: String, email: String }); app.post('/users', async (req, res) => { const user = new User(req.body); await user.save(); res.status(201).send(user); });8. 项目部署与生产环境配置
8.1 环境变量管理
使用dotenv管理环境变量:
npm install dotenv --save创建.env文件:
PORT=3000 DB_URL=mongodb://localhost:27017/prod-db SECRET_KEY=your-production-secret应用配置:
require('dotenv').config(); const port = process.env.PORT || 3000; app.listen(port, () => { console.log(`服务器运行在${port}端口`); });8.2 PM2进程管理
使用PM2部署Node.js应用:
npm install pm2 -g pm2 start server.js --name "express-app"常用PM2命令:
pm2 list # 查看进程列表 pm2 logs # 查看日志 pm2 restart all # 重启所有应用 pm2 delete all # 删除所有应用8.3 Nginx反向代理
配置Nginx作为反向代理:
server { listen 80; server_name example.com; location / { proxy_pass http://localhost:3000; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection 'upgrade'; proxy_set_header Host $host; proxy_cache_bypass $http_upgrade; } }8.4 HTTPS配置
使用Let's Encrypt免费SSL证书:
sudo apt install certbot python3-certbot-nginx sudo certbot --nginx -d example.comExpress中强制HTTPS:
app.use((req, res, next) => { if (!req.secure && req.get('X-Forwarded-Proto') !== 'https' && process.env.NODE_ENV === 'production') { return res.redirect('https://' + req.get('Host') + req.url); } next(); });在实际项目中,Express的灵活性和丰富的中间件生态系统使其成为Node.js Web开发的首选框架。通过合理组织代码结构、选择适当的中间件和遵循最佳实践,可以构建出高性能、易维护的Web应用。