Express 是 Node.js 生态中最主流的 Web 框架,POST 请求是前端与后端交互的核心方式(用于提交数据、上传文件等)。本教程从 POST 请求的核心原理、数据解析、不同场景处理(表单、JSON、文件)、安全防护到实战案例,全面解析 Express 处理 POST 请求的方法。
一、POST 请求核心原理
1. POST 请求的本质
POST 请求是 HTTP 方法的一种,用于向服务器提交数据,数据存储在请求体(Request Body)中,而非 URL 拼接(区别于 GET)。特点:
- 无数据长度限制(理论上,实际受服务器配置限制);
- 数据隐藏在请求体中,比 GET 更安全;
- 支持多种数据格式(JSON、表单、文件等);
- 无法被浏览器缓存,适合提交/修改数据。
2. Express 处理 POST 的核心流程
- 前端发送 POST 请求(携带请求体);
- Express 接收请求,通过中间件解析请求体数据;
- 路由处理解析后的数据(验证、业务逻辑);
- 后端返回响应(JSON/HTML 等)。
⚠️ 关键:Express 原生不解析请求体,必须通过第三方中间件(如 express.json()、express.urlencoded()、multer)解析不同格式的 POST 数据。
二、Express 解析 POST 数据的核心中间件
Express 提供内置中间件 + 第三方中间件,覆盖所有 POST 数据格式的解析需求:
| 数据格式 | 中间件 | 适用场景 |
|---|---|---|
| JSON | express.json() |
前端提交 JSON 数据(AJAX/Fetch 常用) |
| 表单(urlencoded) | express.urlencoded() |
前端提交普通表单(application/x-www-form-urlencoded) |
| 表单(multipart) | multer |
上传文件/混合数据(multipart/form-data) |
| 纯文本 | express.text() |
提交纯文本数据(text/plain) |
| 二进制 | raw-body |
提交二进制数据(如图片、视频流) |
1. 基础配置:注册中间件
中间件需在路由之前注册(全局生效),确保所有路由都能解析 POST 数据:
javascript
const express = require('express');
const app = express();
// 解析 JSON 格式的 POST 数据
app.use(express.json({
limit: '1mb' // 限制请求体大小(防止超大数据攻击)
}));
// 解析 urlencoded 格式的表单数据
app.use(express.urlencoded({
extended: true, // 启用 qs 库解析复杂对象(false 用 querystring 库)
limit: '1mb'
}));
// 解析纯文本数据(可选)
app.use(express.text({ type: 'text/plain' }));
// 路由定义(必须在中间件之后)
app.post('/api/submit', (req, res) => {
// 解析后的数据存放在 req.body 中
console.log(req.body);
res.send('POST 数据已接收');
});
app.listen(3000, () => console.log('服务器启动:http://localhost:3000'));
三、不同场景的 POST 请求处理
1. 场景1:处理 JSON 格式的 POST 数据
前端通过 Fetch/AJAX 提交 JSON 数据,是最常用的场景(前后端分离首选)。
(1)前端代码(Fetch 示例)
javascript
// 前端提交 JSON 数据
fetch('http://localhost:3000/api/json', {
method: 'POST',
headers: {
'Content-Type': 'application/json' // 必须指定 JSON 格式
},
body: JSON.stringify({
username: '张三',
age: 20,
hobby: ['reading', 'coding']
})
})
.then(res => res.text())
.then(data => console.log(data));
(2)Express 后端代码
javascript
// 已注册 express.json() 中间件
app.post('/api/json', (req, res) => {
// 解析后的 JSON 数据在 req.body 中
const { username, age, hobby } = req.body;
// 数据验证
if (!username) {
return res.status(400).json({ code: -1, msg: '用户名不能为空' });
}
// 业务逻辑(如存入数据库)
res.json({
code: 0,
msg: 'JSON 数据接收成功',
data: req.body
});
});
2. 场景2:处理普通表单(urlencoded)
前端提交传统表单(application/x-www-form-urlencoded),适用于非文件上传的表单场景。
(1)前端 HTML 表单
html
<form action="http://localhost:3000/api/form" method="POST">
<input type="text" name="username" placeholder="用户名">
<input type="password" name="pwd" placeholder="密码">
<input type="checkbox" name="hobby" value="reading"> 阅读
<input type="checkbox" name="hobby" value="coding"> 编码
<button type="submit">提交</button>
</form>
(2)Express 后端代码
javascript
// 已注册 express.urlencoded() 中间件
app.post('/api/form', (req, res) => {
// 表单数据解析后存放在 req.body
const { username, pwd, hobby } = req.body;
// hobby 是数组(多个复选框)
res.send(`
<h3>表单数据接收成功</h3>
<p>用户名:${username}</p>
<p>密码:${pwd}</p>
<p>爱好:${hobby || '无'}</p>
`);
});
3. 场景3:处理文件上传(multipart/form-data)
上传文件是 POST 请求的高频场景,Express 需借助 multer 中间件解析 multipart/form-data 格式的请求体。
(1)安装 multer
bash
npm install multer --save
(2)单文件上传
javascript
const multer = require('multer');
// 配置文件存储路径和文件名
const storage = multer.diskStorage({
// 存储目录(需手动创建 uploads 文件夹)
destination: (req, file, cb) => {
cb(null, './uploads');
},
// 自定义文件名(避免重名)
filename: (req, file, cb) => {
// 格式:时间戳-原文件名
const fileName = Date.now() + '-' + file.originalname;
cb(null, fileName);
}
});
// 过滤文件类型(仅允许图片)
const fileFilter = (req, file, cb) => {
const allowedTypes = ['image/jpeg', 'image/png', 'image/gif'];
if (allowedTypes.includes(file.mimetype)) {
cb(null, true); // 允许上传
} else {
cb(new Error('仅允许上传 jpg/png/gif 格式的图片'), false); // 拒绝
}
};
// 创建 multer 实例
const upload = multer({
storage: storage,
limits: {
fileSize: 5 * 1024 * 1024 // 限制文件大小 5MB
},
fileFilter: fileFilter
});
// 处理单文件上传(字段名:avatar)
app.post('/api/upload/single', upload.single('avatar'), (req, res) => {
// req.file:上传的文件信息
// req.body:其他表单文本数据
if (!req.file) {
return res.status(400).json({ msg: '请选择文件' });
}
res.json({
msg: '单文件上传成功',
file: {
name: req.file.filename,
path: req.file.path,
size: req.file.size + ' bytes'
},
body: req.body
});
});
(3)多文件上传
javascript
// 处理多文件上传(字段名:files,最多 5 个文件)
app.post('/api/upload/multi', upload.array('files', 5), (req, res) => {
// req.files:文件数组
if (!req.files || req.files.length === 0) {
return res.status(400).json({ msg: '请选择文件' });
}
res.json({
msg: `成功上传 ${req.files.length} 个文件`,
files: req.files.map(file => ({
name: file.filename,
path: file.path
}))
});
});
(4)前端上传文件(FormData 示例)
html
<input type="file" id="fileInput" multiple>
<button onclick="uploadFile()">上传</button>
<script>
function uploadFile() {
const fileInput = document.getElementById('fileInput');
const files = fileInput.files;
if (files.length === 0) return;
const formData = new FormData();
// 多文件循环添加
for (let i = 0; i < files.length; i++) {
formData.append('files', files[i]);
}
// 额外添加文本数据
formData.append('username', '张三');
fetch('http://localhost:3000/api/upload/multi', {
method: 'POST',
body: formData // 无需设置 Content-Type,浏览器自动处理
})
.then(res => res.json())
.then(data => console.log(data));
}
</script>
4. 场景4:处理纯文本/二进制数据
(1)纯文本数据(text/plain)
javascript
// 已注册 express.text() 中间件
app.post('/api/text', (req, res) => {
// 纯文本数据在 req.body 中
res.send(`接收到纯文本:<pre>${req.body}</pre>`);
});
(2)二进制数据(如图片流)
需借助 raw-body 包解析二进制请求体:
bash
npm install raw-body --save
javascript
const rawBody = require('raw-body');
const fs = require('fs');
app.post('/api/raw', async (req, res) => {
try {
// 解析二进制数据
const buffer = await rawBody(req);
// 将二进制数据写入文件(如图片)
fs.writeFileSync('./uploads/raw-image.jpg', buffer);
res.json({ msg: '二进制数据接收成功' });
} catch (err) {
res.status(500).json({ msg: '解析失败', error: err.message });
}
});
四、POST 请求的高级处理
1. 数据验证
POST 请求必须验证前端提交的数据(防止非法数据),推荐使用 joi/express-validator 等库:
示例:使用 express-validator 验证
bash
npm install express-validator --save
javascript
const { body, validationResult } = require('express-validator');
// 注册带验证的路由
app.post(
'/api/validate',
// 验证规则
[
body('username').notEmpty().withMessage('用户名不能为空').isLength({ min: 2 }).withMessage('用户名至少2位'),
body('age').isInt({ min: 18 }).withMessage('年龄必须是大于18的整数'),
body('email').isEmail().withMessage('邮箱格式错误').optional() // 可选字段
],
(req, res) => {
// 获取验证错误
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({ errors: errors.array() });
}
// 验证通过,处理业务逻辑
res.json({ msg: '数据验证通过', data: req.body });
}
);
2. 处理跨域 POST 请求
前端跨域发送 POST 请求时,需配置 CORS 中间件(cors):
bash
npm install cors --save
javascript
const cors = require('cors');
// 全局启用 CORS
app.use(cors({
origin: 'http://localhost:8080', // 允许指定域名跨域
methods: ['GET', 'POST', 'PUT', 'DELETE'], // 允许的方法
allowedHeaders: ['Content-Type', 'Authorization'], // 允许的请求头
credentials: true // 允许携带 Cookie
}));
// 或针对单个路由启用
app.post('/api/cors', cors(), (req, res) => {
res.json({ msg: '跨域 POST 请求成功' });
});
3. 自定义请求体解析限制
防止超大请求体攻击,可在中间件中配置 limit:
javascript
// 限制 JSON 数据最大 1MB
app.use(express.json({ limit: '1mb' }));
// 限制表单数据最大 500KB
app.use(express.urlencoded({ extended: true, limit: '500kb' }));
// 限制文件大小 2MB
const upload = multer({ limits: { fileSize: 2 * 1024 * 1024 } });
4. 处理 POST 请求的错误
(1)解析错误捕获
javascript
// 捕获 JSON 解析错误
app.use((err, req, res, next) => {
if (err.type === 'entity.parse.failed') {
return res.status(400).json({ msg: 'JSON 格式错误' });
}
next(err);
});
// 捕获文件上传错误
app.post('/api/upload', (req, res) => {
upload.single('avatar')(req, res, (err) => {
if (err) {
if (err.code === 'LIMIT_FILE_SIZE') {
return res.status(400).json({ msg: '文件大小超过 5MB' });
}
return res.status(400).json({ msg: err.message });
}
// 正常处理逻辑
res.json({ msg: '上传成功' });
});
});
(2)全局错误处理中间件
javascript
// 全局错误处理(必须是 4 个参数)
app.use((err, req, res, next) => {
console.error('POST 请求错误:', err.stack);
res.status(500).json({
code: -1,
msg: '服务器内部错误',
error: process.env.NODE_ENV === 'development' ? err.message : '' // 开发环境返回错误详情
});
});
五、POST 请求的安全防护
1. 防止 CSRF 攻击
CSRF(跨站请求伪造)是 POST 请求的常见安全风险,可通过 csurf 中间件防护:
bash
npm install csurf cookie-parser --save
javascript
const cookieParser = require('cookie-parser');
const csrf = require('csurf');
app.use(cookieParser());
// 启用 CSRF 保护
const csrfProtection = csrf({ cookie: true });
// 生成 CSRF Token
app.get('/api/csrf-token', csrfProtection, (req, res) => {
res.json({ csrfToken: req.csrfToken() });
});
// 受保护的 POST 路由(需携带 CSRF Token)
app.post('/api/protected', csrfProtection, (req, res) => {
res.json({ msg: 'POST 请求通过 CSRF 验证' });
});
前端需在 POST 请求头中携带 X-CSRF-Token:
javascript
fetch('/api/protected', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRF-Token': '获取到的 CSRF Token'
},
body: JSON.stringify({ data: 'test' })
});
2. 防止 XSS 攻击
XSS(跨站脚本攻击):过滤用户输入的危险字符,推荐使用 xss 库:
bash
npm install xss --save
javascript
const xss = require('xss');
app.post('/api/xss', (req, res) => {
// 过滤用户输入的 HTML/JS 代码
const safeContent = xss(req.body.content);
res.json({ msg: 'XSS 过滤完成', safeContent });
});
3. 速率限制(防止暴力请求)
限制同一 IP 的 POST 请求频率,使用 express-rate-limit:
bash
npm install express-rate-limit --save
javascript
const rateLimit = require('express-rate-limit');
// 配置速率限制:10分钟内最多 100 次请求
const postLimiter = rateLimit({
windowMs: 10 * 60 * 1000,
max: 100,
message: '请求过于频繁,请稍后再试'
});
// 应用到所有 POST 路由
app.use('/api/', postLimiter);
六、完整实战案例:用户注册接口
javascript
const express = require('express');
const cors = require('cors');
const { body, validationResult } = require('express-validator');
const multer = require('multer');
const fs = require('fs');
const app = express();
const port = 3000;
// 1. 基础中间件
app.use(cors());
app.use(express.json({ limit: '1mb' }));
app.use(express.urlencoded({ extended: true }));
// 2. 创建上传目录
if (!fs.existsSync('./uploads/avatars')) {
fs.mkdirSync('./uploads/avatars', { recursive: true });
}
// 3. 配置 multer
const upload = multer({
storage: multer.diskStorage({
destination: './uploads/avatars',
filename: (req, file, cb) => {
cb(null, Date.now() + '-' + file.originalname);
}
}),
limits: { fileSize: 2 * 1024 * 1024 }, // 2MB
fileFilter: (req, file, cb) => {
const allowed = ['image/jpeg', 'image/png'];
if (allowed.includes(file.mimetype)) {
cb(null, true);
} else {
cb(new Error('仅支持 jpg/png 格式头像'), false);
}
}
});
// 4. 用户注册接口(含文件上传 + 数据验证)
app.post(
'/api/register',
upload.single('avatar'), // 解析头像文件
// 数据验证规则
[
body('username').notEmpty().withMessage('用户名不能为空').isLength({ min: 2, max: 20 }).withMessage('用户名长度 2-20 位'),
body('email').isEmail().withMessage('邮箱格式错误'),
body('password').isLength({ min: 6 }).withMessage('密码至少 6 位')
],
(req, res) => {
// 验证错误处理
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({ code: -1, errors: errors.array() });
}
// 解析数据
const { username, email, password } = req.body;
const avatar = req.file ? `/uploads/avatars/${req.file.filename}` : '';
// 模拟数据库存储(实际项目中替换为真实数据库操作)
const user = {
id: Date.now(),
username,
email,
password: '******', // 实际项目中需加密存储
avatar,
createTime: new Date().toLocaleString()
};
// 返回响应
res.json({
code: 0,
msg: '注册成功',
data: user
});
}
);
// 5. 全局错误处理
app.use((err, req, res, next) => {
console.error(err.stack);
res.status(500).json({
code: -2,
msg: '服务器内部错误',
error: process.env.NODE_ENV === 'development' ? err.message : ''
});
});
// 启动服务器
app.listen(port, () => {
console.log(`服务器运行在 http://localhost:${port}`);
});
七、总结
Express 处理 POST 请求的核心要点:
- 中间件是关键 :根据数据格式选择对应的解析中间件(
express.json()/express.urlencoded()/multer); - 数据解析后存于
req.body(文件存于req.file/req.files); - 必须验证数据 :防止非法数据提交,推荐使用
express-validator; - 文件上传用
multer:配置存储路径、文件大小、类型过滤; - 安全防护不可少:CORS、CSRF、XSS、速率限制;
- 错误处理要完善:捕获解析错误、业务错误,返回友好提示。