请求体解析与 bodyParser 使用
请求体解析与 bodyParser 使用
Koa2 作为新一代的 Node.js Web 框架,处理 HTTP 请求时经常需要解析请求体。请求体可能包含表单数据、JSON 或其他格式的内容。原生 Koa2 并不直接提供请求体解析功能,需要借助中间件来实现。
为什么需要 bodyParser
HTTP 请求的 body 部分通常用于传输客户端发送的数据。例如,POST 请求的表单数据或 JSON 数据都放在 body 中。Koa2 的 ctx.request.body 默认是 undefined,需要中间件来解析并填充这个属性。如果没有合适的解析器,就无法获取这些数据。
// 没有 bodyParser 的情况
app.use(async ctx => {
console.log(ctx.request.body); // undefined
ctx.body = 'Hello World';
});
常用 bodyParser 中间件
Koa-bodyparser 是最常用的请求体解析中间件。它支持 JSON、表单和文本等格式的请求体。
安装方法:
npm install koa-bodyparser
基本使用:
const Koa = require('koa');
const bodyParser = require('koa-bodyparser');
const app = new Koa();
app.use(bodyParser());
app.use(async ctx => {
// 现在可以获取解析后的请求体
console.log(ctx.request.body);
ctx.body = ctx.request.body;
});
app.listen(3000);
配置选项详解
koa-bodyparser 提供了多个配置选项来满足不同需求:
app.use(bodyParser({
enableTypes: ['json', 'form'], // 只解析 JSON 和表单
encode: 'utf-8', // 编码格式
jsonLimit: '1mb', // JSON 最大体积
formLimit: '56kb', // 表单数据最大体积
textLimit: '56kb', // 文本最大体积
strict: true, // 是否只接受数组和对象
onerror: function(err, ctx) { // 错误处理
ctx.throw(422, 'body parse error');
}
}));
处理不同内容类型
koa-bodyparser 会自动根据 Content-Type 头部选择解析方式:
- application/json → 解析为 JavaScript 对象
- application/x-www-form-urlencoded → 解析为对象
- text/plain → 解析为字符串
示例处理 JSON:
// 客户端发送
fetch('/api', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ name: 'Koa' })
});
// 服务端接收
app.use(async ctx => {
const { name } = ctx.request.body;
ctx.body = { receivedName: name };
});
文件上传处理
koa-bodyparser 不支持文件上传,需要改用 koa-body 中间件:
const koaBody = require('koa-body');
app.use(koaBody({
multipart: true,
formidable: {
uploadDir: './uploads', // 上传目录
keepExtensions: true // 保留扩展名
}
}));
app.use(async ctx => {
if (ctx.request.files) {
const file = ctx.request.files.file;
console.log('File received:', file.name);
}
ctx.body = 'File uploaded';
});
自定义解析器
在某些特殊情况下,可能需要自定义 body 解析逻辑:
app.use(async (ctx, next) => {
if (ctx.is('text/xml')) {
ctx.request.body = await parseXML(ctx.req);
}
await next();
});
async function parseXML(req) {
return new Promise((resolve, reject) => {
let data = '';
req.on('data', chunk => data += chunk);
req.on('end', () => {
try {
// 简单示例,实际应使用xml解析库
resolve(data);
} catch (e) {
reject(e);
}
});
});
}
性能与安全考虑
- 限制请求体大小防止DoS攻击:
app.use(bodyParser({
jsonLimit: '100kb', // 限制JSON大小
formLimit: '50kb' // 限制表单大小
}));
- 禁用特定内容类型:
app.use(bodyParser({
enableTypes: ['json'] // 只允许JSON
}));
- 生产环境应考虑添加请求体解析超时:
const timeout = require('koa-connect-timeout');
app.use(timeout('5s'));
app.use(bodyParser());
常见问题排查
- 获取不到 body 数据:
- 检查是否正确使用了 bodyParser 中间件
- 确认请求的 Content-Type 头部正确
- 确保中间件顺序正确(bodyParser 应在路由前)
- 大文件上传失败:
- 使用 koa-body 替代 koa-bodyparser
- 调整大小限制配置
- 检查服务器磁盘空间
- 特殊字符乱码:
- 设置正确的编码格式
app.use(bodyParser({
encode: 'utf-8'
}));
与其他中间件的协作
bodyParser 通常需要与其他中间件配合使用,顺序很重要:
const Koa = require('koa');
const bodyParser = require('koa-bodyparser');
const router = require('koa-router')();
const app = new Koa();
// 正确的中间件顺序
app.use(errorHandler()); // 错误处理最先
app.use(logger()); // 然后是日志
app.use(bodyParser()); // 接着是body解析
app.use(router.routes()); // 最后是路由
// 错误示例:bodyParser在路由之后
// 这样路由将无法获取解析后的body
替代方案比较
除了 koa-bodyparser,还有其他 body 解析方案:
- koa-body:
- 支持文件上传
- 功能更全面
- 但配置更复杂
- co-body:
- 更底层的解析器
- 需要手动处理请求
- 灵活性更高
- raw-body:
- 只获取原始Buffer
- 需要完全自定义处理
选择依据:
- 简单JSON/表单 → koa-bodyparser
- 文件上传 → koa-body
- 特殊需求 → co-body 或 raw-body
实际应用场景
- RESTful API 开发:
app.use(bodyParser());
router.post('/users', async ctx => {
const userData = ctx.request.body;
// 验证和处理数据...
ctx.status = 201;
ctx.body = { id: 123, ...userData };
});
- 表单处理:
<!-- 前端表单 -->
<form action="/submit" method="post">
<input name="username">
<input name="password" type="password">
</form>
// 后端处理
app.use(bodyParser());
app.use(async ctx => {
if (ctx.method === 'POST' && ctx.path === '/submit') {
const { username, password } = ctx.request.body;
// 验证逻辑...
}
});
- 微信小程序回调:
app.use(bodyParser({ enableTypes: ['json'] }));
app.post('/wechat/callback', async ctx => {
const wechatData = ctx.request.body;
// 处理微信服务器推送...
ctx.body = { code: 0 };
});
高级用法
- 动态解析策略:
app.use(async (ctx, next) => {
// 根据路径决定是否解析body
if (ctx.path.startsWith('/api')) {
await bodyParser()(ctx, next);
} else {
await next();
}
});
- 内容协商处理:
app.use(async (ctx, next) => {
await bodyParser({
onerror: (err, ctx) => {
ctx.status = 400;
ctx.body = { error: 'Invalid body' };
}
})(ctx, next);
});
- 流式处理大型JSON:
const ndjson = require('ndjson');
app.use(async ctx => {
if (ctx.is('application/x-ndjson')) {
const parser = ctx.req.pipe(ndjson.parse());
for await (const obj of parser) {
// 逐行处理大型NDJSON
processObject(obj);
}
}
});
本站部分内容来自互联网,一切版权均归源网站或源作者所有。
如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn
上一篇:HTTP 请求方法全面解析
下一篇:排序、分页与聚合