一、先搞懂:express.json() 是什么?
express.json() 是 Express.js(4.16.0 及以上版本内置)的内置中间件函数 ,核心作用是:解析 HTTP 请求体中格式为 JSON 的数据,将其转换为 JavaScript 对象,并挂载到 req.body 属性上,供后续路由/中间件使用。
简单说:客户端给服务器发 JSON 格式的请求数据(比如 POST/PUT 请求的请求体),如果不使用 express.json(),服务器的 req.body 会是 undefined;使用之后,就能直接通过 req.body 获取解析后的 JSON 数据。
补充:在 Express 4.16.0 之前,解析 JSON 请求体需要依赖第三方中间件(如 body-parser),之后 Express 将 body-parser 的核心功能内置,express.json() 本质就是 body-parser.json() 的内置封装。
二、为什么需要 express.json()?
HTTP 请求的请求体(body)本质是「二进制数据流」,即使客户端发送的是 JSON 字符串(比如 {"username":"zhangsan","age":20}),服务器接收到的原始数据也是一串无结构的字符,而非可直接操作的 JS 对象。
如果没有 express.json() 做解析:
- 你需要手动监听
req对象的data事件和end事件,拼接原始数据流; - 手动将拼接后的字符串转换为 JS 对象(
JSON.parse()); - 还要处理编码、异常(如无效 JSON 格式)等问题,极其繁琐。
express.json() 帮我们封装了这一系列操作,让我们可以直接通过 req.body 获取结构化数据,提升开发效率。
三、基础使用:怎么用 express.json()?
1. 全局使用(推荐,解析所有符合条件的请求)
在 Express 实例上通过 app.use() 注册,对所有后续路由生效:
javascript
// 1. 导入 Express
const express = require('express');
// 2. 创建 Express 实例
const app = express();
// 3. 注册 express.json() 中间件(全局生效)
app.use(express.json());
// 4. 定义路由,使用 req.body 获取解析后的 JSON 数据
app.post('/api/user', (req, res) => {
// 此时 req.body 已被解析为 JS 对象
console.log('解析后的 JSON 数据:', req.body);
console.log('用户名:', req.body.username);
console.log('年龄:', req.body.age);
res.status(200).json({
code: 200,
message: '请求成功',
data: req.body
});
});
// 5. 启动服务器
const port = 3000;
app.listen(port, () => {
console.log(`服务器运行在 http://localhost:${port}`);
});
2. 局部使用(仅对指定路由生效)
如果只需要解析某几个路由的 JSON 请求体,可以在路由定义时单独注册:
javascript
const express = require('express');
const app = express();
// 仅对 /api/order 这个 POST 路由生效
app.post('/api/order', express.json(), (req, res) => {
console.log('订单数据:', req.body);
res.json({ code: 200, data: req.body });
});
// 其他路由不解析 JSON 请求体(req.body 为 undefined)
app.post('/api/test', (req, res) => {
console.log('未解析的 req.body:', req.body); // undefined
res.send('测试路由');
});
app.listen(3000, () => console.log('服务器启动'));
3. 测试验证
可以用 Postman、curl 或 Axios 发送 JSON 请求测试:
(1)curl 命令测试
bash
curl -X POST \
http://localhost:3000/api/user \
-H 'Content-Type: application/json' \
-d '{
"username": "zhangsan",
"age": 20,
"gender": "male"
}'
(2)Axios 前端测试
javascript
axios.post('http://localhost:3000/api/user', {
username: 'zhangsan',
age: 20,
gender: 'male'
}, {
headers: {
'Content-Type': 'application/json' // 前端需指定该请求头(默认已带)
}
}).then(res => console.log(res.data));
(3)响应结果
服务器控制台会输出:
解析后的 JSON 数据: { username: 'zhangsan', age: 20, gender: 'male' }
用户名: zhangsan
年龄: 20
客户端会收到响应:
json
{
"code": 200,
"message": "请求成功",
"data": {
"username": "zhangsan",
"age": 20,
"gender": "male"
}
}
四、高级配置:express.json() 的可选参数
express.json() 支持传入一个配置对象,自定义解析行为,常用参数如下:
1. 核心配置参数
| 参数名 | 类型 | 默认值 | 说明 |
|---|---|---|---|
inflate |
Boolean | true |
是否解析「压缩格式」的请求体(如 gzip/deflate 压缩的 JSON 数据);设为 false 时,拒绝压缩的请求体 |
limit |
String/Number | '100kb' |
限制请求体的大小;可以是数字(单位:字节)或字符串(如 '1mb'、'2kb'),超过该大小会抛出 413 错误(Payload Too Large) |
reviver |
Function | null |
传递给 JSON.parse() 的第二个参数,用于自定义解析过程(如转换日期字符串为 Date 对象) |
strict |
Boolean | true |
是否开启「严格模式」;设为 true 时,仅解析「对象/数组」格式的 JSON(拒绝单个字符串/数字/布尔值);设为 false 时允许解析单个值 |
type |
String/Array/Function | 'application/json' |
指定要解析的请求头 Content-Type;可以是字符串、数组,或自定义判断函数 |
verify |
Function | undefined |
验证函数,用于校验请求体;接收 (req, res, buf, encoding) 四个参数,抛出错误则拒绝该请求 |
2. 配置示例
(1)自定义请求体大小限制
javascript
// 限制请求体最大为 2MB
app.use(express.json({ limit: '2mb' }));
(2)关闭严格模式,允许解析单个 JSON 值
javascript
app.use(express.json({ strict: false }));
// 此时可以解析 {"name":"zhangsan"}(对象),也可以解析 "hello"(字符串)/ 123(数字)
app.post('/api/test', (req, res) => {
console.log(req.body); // 若请求体是 "hello",req.body 就是 "hello"
res.send(req.body);
});
(3)用 reviver 转换日期字符串为 Date 对象
javascript
app.use(express.json({
reviver: (key, value) => {
// 匹配 ISO 格式的日期字符串(如 "2025-12-19T08:00:00.000Z")
if (typeof value === 'string' && /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/.test(value)) {
return new Date(value);
}
return value;
}
}));
app.post('/api/date', (req, res) => {
console.log('原始值:', req.body.birthday); // 原本是字符串,现在是 Date 对象
console.log('日期格式化:', req.body.birthday.toLocaleDateString());
res.json({ birthday: req.body.birthday });
});
(4)自定义解析的 Content-Type
默认只解析 Content-Type: application/json 的请求,若需要解析自定义类型(如 application/vnd.myapp+json),可以通过 type 参数指定:
javascript
// 方式1:字符串
app.use(express.json({ type: 'application/vnd.myapp+json' }));
// 方式2:数组(支持多个类型)
app.use(express.json({ type: ['application/json', 'application/vnd.myapp+json'] }));
// 方式3:自定义判断函数(返回 true 则解析)
app.use(express.json({
type: (req) => {
// 匹配所有以 "json" 结尾的 Content-Type
return req.headers['content-type']?.endsWith('json');
}
}));
(5)用 verify 校验请求体
javascript
app.use(express.json({
verify: (req, res, buf, encoding) => {
// buf 是请求体的原始 Buffer 数据,encoding 是编码格式
const bodyStr = buf.toString(encoding);
// 校验:用户名长度不能少于 3 位
const data = JSON.parse(bodyStr);
if (!data.username || data.username.length < 3) {
throw new Error('用户名长度不能少于 3 位'); // 抛出错误,请求会被拒绝
}
}
}));
// 错误处理中间件(捕获 verify 抛出的错误)
app.use((err, req, res, next) => {
res.status(400).json({ code: 400, message: err.message });
});
五、底层原理:express.json() 是如何工作的?
express.json() 本质是对 body-parser 模块的封装,其工作流程可以概括为 5 步:
-
判断是否需要解析
首先检查请求的
Content-Type是否匹配配置的type参数(默认application/json),不匹配则直接跳过,不解析。 -
处理压缩请求体
如果请求体是 gzip/deflate 压缩格式(且
inflate: true),先自动解压为原始字符串。 -
拼接请求体数据流
HTTP 请求体可能分块传输(TCP 粘包/拆包),
express.json()会监听req对象的data事件,逐步拼接所有数据块,直到end事件触发(表示数据传输完成)。 -
校验与解析
- 校验请求体大小是否超过
limit限制,超过则抛出 413 错误; - 若开启
strict: true,校验 JSON 格式是否为对象/数组,否则抛出 400 错误; - 调用
JSON.parse()(传入reviver参数)将 JSON 字符串转换为 JS 对象。
- 校验请求体大小是否超过
-
挂载到 req.body
将解析后的 JS 对象挂载到
req.body属性上,然后调用next()方法,将请求传递给下一个路由/中间件。
简化的伪代码实现:
javascript
function expressJson(options = {}) {
// 默认配置
const defaultOptions = {
inflate: true,
limit: '100kb',
strict: true,
type: 'application/json',
reviver: null
};
const opts = { ...defaultOptions, ...options };
return (req, res, next) => {
// 1. 判断是否需要解析
const contentType = req.headers['content-type'];
if (!isMatchContentType(contentType, opts.type)) {
return next();
}
let rawBody = Buffer.alloc(0);
// 2. 拼接数据流
req.on('data', (chunk) => {
rawBody = Buffer.concat([rawBody, chunk]);
// 3. 校验大小
if (rawBody.length > getLimitBytes(opts.limit)) {
return next(new Error('Payload Too Large'));
}
});
// 4. 数据传输完成后解析
req.on('end', () => {
try {
const bodyStr = rawBody.toString('utf8');
// 严格模式校验
if (opts.strict && !/^\{|\[/.test(bodyStr.trim())) {
throw new Error('Invalid JSON (strict mode)');
}
// 解析 JSON
const parsedData = JSON.parse(bodyStr, opts.reviver);
// 挂载到 req.body
req.body = parsedData;
next();
} catch (err) {
next(new Error(`Invalid JSON: ${err.message}`));
}
});
};
}
六、常见问题与解决方案
1. 问题:req.body 为 undefined
原因分析:
- 未注册
express.json()中间件; - 中间件注册顺序错误(在路由之后注册,导致路由执行时还未解析);
- 客户端请求头
Content-Type不是application/json(或未匹配自定义type); - 请求体不是合法的 JSON 格式(解析失败,但此时
req.body可能为{}或抛出错误)。
解决方案:
- 确保在路由之前注册
express.json()(app.use(express.json())放在app.post()/app.get()之前); - 客户端发送请求时,显式设置
Content-Type: application/json; - 检查请求体是否为合法 JSON(如避免语法错误:多余的逗号、未用双引号包裹键名等)。
2. 问题:报错「Payload Too Large」
原因分析:
请求体大小超过了 express.json() 的 limit 配置(默认 100kb)。
解决方案:
增大 limit 参数的值:
javascript
// 增大到 2MB
app.use(express.json({ limit: '2mb' }));
// 或增大到 10MB
app.use(express.json({ limit: '10mb' }));
3. 问题:报错「Invalid JSON」
原因分析:
- 请求体不是合法的 JSON 格式(如单引号包裹键名、语法错误、多余逗号等);
- 开启了严格模式(
strict: true),但请求体是单个字符串/数字/布尔值(如"hello"、123)。
解决方案:
- 修复请求体的 JSON 格式(使用双引号包裹键名和字符串,避免语法错误);
- 若需要支持单个 JSON 值,关闭严格模式:
express.json({ strict: false })。
4. 问题:解析后的数据类型不符合预期
原因分析:
JSON 格式中没有「日期类型」,日期会被序列化为字符串,默认解析后还是字符串,不是 Date 对象。
解决方案:
使用 reviver 参数自定义解析,将日期字符串转换为 Date 对象(参考前文「高级配置」中的示例)。
5. 问题:自定义 Content-Type 无法解析
原因分析:
express.json() 默认只解析 application/json,自定义 Content-Type 未配置 type 参数。
解决方案:
通过 type 参数指定自定义 Content-Type(参考前文「高级配置」中的示例)。
七、与其他请求体解析中间件的区别
Express 中还有其他内置请求体解析中间件,需注意与 express.json() 的区别:
| 中间件 | 作用 | 解析的 Content-Type | 适用场景 |
|---|---|---|---|
express.json() |
解析 JSON 格式请求体 | application/json |
前后端分离项目(AJAX/axios 请求) |
express.urlencoded() |
解析 URL 编码格式请求体 | application/x-www-form-urlencoded |
传统表单提交(form 标签默认格式) |
express.raw() |
解析原始二进制请求体 | 自定义类型(如 application/octet-stream) |
接收文件流、二进制数据 |
express.text() |
解析纯文本请求体 | 自定义类型(如 text/plain) |
接收纯文本字符串 |
示例:同时注册多个解析中间件
javascript
// 解析 JSON 格式
app.use(express.json());
// 解析 URL 编码格式(extended: true 支持嵌套对象)
app.use(express.urlencoded({ extended: true }));
// 解析纯文本格式
app.use(express.text({ type: 'text/plain' }));
// 解析原始二进制格式
app.use(express.raw({ type: 'application/octet-stream', limit: '10mb' }));
总结
express.json()是 Express 内置中间件,核心功能是解析 JSON 格式请求体并挂载到req.body,替代了早期的body-parser.json()。- 核心使用方式分为全局注册(
app.use(express.json()))和局部注册(路由内单独传入),且需在路由之前注册才能生效。 - 关键配置包括
limit(请求体大小限制)、strict(JSON 严格模式)、reviver(自定义解析逻辑)、type(指定解析的 Content-Type)。 - 常见问题中,
req.body为undefined多由中间件注册顺序错误或 Content-Type 不匹配导致,Payload Too Large需增大limit配置。