处理表单数据 #
一、表单数据类型 #
1.1 常见表单类型 #
| 类型 | Content-Type | 说明 |
|---|---|---|
| URL编码 | application/x-www-form-urlencoded | 默认表单类型 |
| JSON | application/json | API常用 |
| 多部分表单 | multipart/form-data | 文件上传 |
1.2 HTML表单示例 #
html
<form action="/submit" method="POST">
<input type="text" name="username">
<input type="email" name="email">
<input type="password" name="password">
<button type="submit">提交</button>
</form>
二、URL编码表单 #
2.1 基本处理 #
javascript
const express = require('express');
const app = express();
app.use(express.urlencoded({ extended: true }));
app.post('/submit', (req, res) => {
const { username, email, password } = req.body;
res.json({ username, email, password: '***' });
});
2.2 extended选项 #
javascript
app.use(express.urlencoded({ extended: true }));
app.use(express.urlencoded({ extended: false }));
| extended | 解析库 | 说明 |
|---|---|---|
| true | qs | 支持嵌套对象 |
| false | querystring | 不支持嵌套对象 |
2.3 嵌套对象 #
使用 extended: true:
javascript
app.use(express.urlencoded({ extended: true }));
app.post('/user', (req, res) => {
console.log(req.body);
res.json(req.body);
});
表单数据 user[name]=张三&user[email]=test@example.com:
json
{
"user": {
"name": "张三",
"email": "test@example.com"
}
}
2.4 数组数据 #
表单数据 tags[]=node&tags[]=express:
json
{
"tags": ["node", "express"]
}
三、JSON数据 #
3.1 基本处理 #
javascript
app.use(express.json());
app.post('/api/users', (req, res) => {
const { name, email, age } = req.body;
res.status(201).json({ name, email, age });
});
3.2 配置选项 #
javascript
app.use(express.json({
limit: '10kb',
strict: true,
type: 'application/json'
}));
3.3 处理不同内容类型 #
javascript
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
app.post('/data', (req, res) => {
const contentType = req.headers['content-type'];
res.json({
contentType,
body: req.body
});
});
四、表单验证 #
4.1 手动验证 #
javascript
app.post('/register', (req, res) => {
const { username, email, password, confirmPassword } = req.body;
const errors = [];
if (!username || username.length < 3) {
errors.push('用户名至少3个字符');
}
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (!email || !emailRegex.test(email)) {
errors.push('请输入有效的邮箱地址');
}
if (!password || password.length < 6) {
errors.push('密码至少6个字符');
}
if (password !== confirmPassword) {
errors.push('两次密码不一致');
}
if (errors.length > 0) {
return res.status(400).json({ errors });
}
res.status(201).json({ username, email });
});
4.2 使用express-validator #
bash
npm install express-validator
javascript
const { body, validationResult } = require('express-validator');
const validateRegister = [
body('username')
.notEmpty().withMessage('用户名不能为空')
.isLength({ min: 3, max: 20 }).withMessage('用户名3-20个字符')
.trim().escape(),
body('email')
.isEmail().withMessage('请输入有效的邮箱地址')
.normalizeEmail(),
body('password')
.isLength({ min: 6 }).withMessage('密码至少6个字符')
.matches(/\d/).withMessage('密码必须包含数字')
.matches(/[a-z]/).withMessage('密码必须包含小写字母')
.matches(/[A-Z]/).withMessage('密码必须包含大写字母'),
body('confirmPassword')
.custom((value, { req }) => {
if (value !== req.body.password) {
throw new Error('两次密码不一致');
}
return true;
}),
(req, res, next) => {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({ errors: errors.array() });
}
next();
}
];
app.post('/register', validateRegister, (req, res) => {
res.status(201).json({
message: '注册成功',
user: { username: req.body.username, email: req.body.email }
});
});
4.3 自定义验证器 #
javascript
const { body } = require('express-validator');
const isEmailExist = async (email) => {
const user = await User.findOne({ email });
if (user) {
throw new Error('邮箱已被注册');
}
};
app.post('/register', [
body('email')
.isEmail()
.custom(isEmailExist),
body('username')
.custom(async (username) => {
const user = await User.findOne({ username });
if (user) {
throw new Error('用户名已被使用');
}
})
], (req, res) => {
res.status(201).json({ message: '注册成功' });
});
五、动态表单 #
5.1 动态字段 #
javascript
app.post('/dynamic', (req, res) => {
const formData = req.body;
Object.keys(formData).forEach(key => {
console.log(`${key}: ${formData[key]}`);
});
res.json({ received: Object.keys(formData).length, data: formData });
});
5.2 数组字段 #
javascript
app.post('/items', (req, res) => {
const { items } = req.body;
if (!Array.isArray(items)) {
return res.status(400).json({ error: 'items必须是数组' });
}
items.forEach((item, index) => {
console.log(`Item ${index}:`, item);
});
res.json({ count: items.length, items });
});
5.3 嵌套对象 #
javascript
app.post('/order', (req, res) => {
const { customer, items, shipping } = req.body;
res.json({
customer: {
name: customer.name,
email: customer.email
},
itemCount: items.length,
shipping: {
address: shipping.address,
city: shipping.city
}
});
});
六、文件上传表单 #
6.1 单文件上传 #
javascript
const multer = require('multer');
const upload = multer({ dest: 'uploads/' });
app.post('/upload', upload.single('avatar'), (req, res) => {
res.json({
file: req.file,
body: req.body
});
});
6.2 多文件上传 #
javascript
app.post('/photos', upload.array('photos', 5), (req, res) => {
res.json({
count: req.files.length,
files: req.files
});
});
6.3 混合表单 #
javascript
app.post('/profile', upload.single('avatar'), (req, res) => {
const { name, email, bio } = req.body;
const avatar = req.file;
res.json({
name,
email,
bio,
avatar: avatar ? {
originalname: avatar.originalname,
size: avatar.size,
mimetype: avatar.mimetype
} : null
});
});
七、CSRF保护 #
7.1 安装csurf #
bash
npm install csurf
7.2 基本配置 #
javascript
const csrf = require('csurf');
const csrfProtection = csrf({ cookie: true });
app.get('/form', csrfProtection, (req, res) => {
res.render('form', { csrfToken: req.csrfToken() });
});
app.post('/submit', csrfProtection, (req, res) => {
res.json({ message: '表单提交成功' });
});
7.3 前端表单 #
html
<form action="/submit" method="POST">
<input type="hidden" name="_csrf" value="<%= csrfToken %>">
<input type="text" name="username">
<button type="submit">提交</button>
</form>
7.4 AJAX请求 #
javascript
app.use(csrf({ cookie: true }));
app.get('/csrf-token', (req, res) => {
res.json({ csrfToken: req.csrfToken() });
});
app.post('/api/submit', (req, res) => {
res.json({ message: '成功' });
});
前端:
javascript
fetch('/api/submit', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRF-Token': csrfToken
},
body: JSON.stringify(data)
});
八、完整示例 #
8.1 注册表单 #
javascript
const express = require('express');
const { body, validationResult } = require('express-validator');
const app = express();
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
const validate = (req, res, next) => {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({ errors: errors.array() });
}
next();
};
app.post('/register', [
body('username')
.notEmpty().withMessage('用户名不能为空')
.isLength({ min: 3, max: 20 }).withMessage('用户名3-20个字符')
.matches(/^[a-zA-Z0-9_]+$/).withMessage('用户名只能包含字母、数字和下划线')
.trim(),
body('email')
.isEmail().withMessage('请输入有效的邮箱地址')
.normalizeEmail(),
body('password')
.isLength({ min: 8 }).withMessage('密码至少8个字符')
.matches(/^(?=.*\d)(?=.*[a-z])(?=.*[A-Z])/).withMessage('密码必须包含大小写字母和数字'),
body('confirmPassword')
.custom((value, { req }) => {
if (value !== req.body.password) {
throw new Error('两次密码不一致');
}
return true;
}),
body('phone')
.optional()
.matches(/^1[3-9]\d{9}$/).withMessage('请输入有效的手机号'),
body('age')
.optional()
.isInt({ min: 1, max: 150 }).withMessage('年龄必须在1-150之间'),
validate
], async (req, res) => {
try {
const { username, email, password, phone, age } = req.body;
const user = {
id: Date.now(),
username,
email,
phone,
age
};
res.status(201).json({
success: true,
message: '注册成功',
user
});
} catch (error) {
res.status(500).json({ error: '服务器错误' });
}
});
app.listen(3000);
8.2 联系表单 #
javascript
app.post('/contact', [
body('name')
.notEmpty().withMessage('姓名不能为空')
.trim().escape(),
body('email')
.isEmail().withMessage('请输入有效的邮箱地址')
.normalizeEmail(),
body('subject')
.notEmpty().withMessage('主题不能为空')
.isLength({ max: 100 }).withMessage('主题最多100个字符'),
body('message')
.notEmpty().withMessage('消息不能为空')
.isLength({ min: 10, max: 1000 }).withMessage('消息10-1000个字符'),
validate
], async (req, res) => {
try {
const { name, email, subject, message } = req.body;
console.log('联系表单:', { name, email, subject, message });
res.json({
success: true,
message: '消息已发送,我们会尽快回复'
});
} catch (error) {
res.status(500).json({ error: '发送失败' });
}
});
九、总结 #
表单处理要点:
| 类型 | Content-Type | 处理方法 |
|---|---|---|
| URL编码 | application/x-www-form-urlencoded | express.urlencoded() |
| JSON | application/json | express.json() |
| 文件上传 | multipart/form-data | multer |
下一步,让我们学习文件上传!
最后更新:2026-03-28