express.json 深入全面讲解教程

一、先搞懂: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() 做解析:

  1. 你需要手动监听 req 对象的 data 事件和 end 事件,拼接原始数据流;
  2. 手动将拼接后的字符串转换为 JS 对象(JSON.parse());
  3. 还要处理编码、异常(如无效 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. 测试验证

可以用 PostmancurlAxios 发送 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 步:

  1. 判断是否需要解析

    首先检查请求的 Content-Type 是否匹配配置的 type 参数(默认 application/json),不匹配则直接跳过,不解析。

  2. 处理压缩请求体

    如果请求体是 gzip/deflate 压缩格式(且 inflate: true),先自动解压为原始字符串。

  3. 拼接请求体数据流

    HTTP 请求体可能分块传输(TCP 粘包/拆包),express.json() 会监听 req 对象的 data 事件,逐步拼接所有数据块,直到 end 事件触发(表示数据传输完成)。

  4. 校验与解析

    • 校验请求体大小是否超过 limit 限制,超过则抛出 413 错误;
    • 若开启 strict: true,校验 JSON 格式是否为对象/数组,否则抛出 400 错误;
    • 调用 JSON.parse()(传入 reviver 参数)将 JSON 字符串转换为 JS 对象。
  5. 挂载到 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' }));

总结

  1. express.json() 是 Express 内置中间件,核心功能是解析 JSON 格式请求体并挂载到 req.body,替代了早期的 body-parser.json()
  2. 核心使用方式分为全局注册(app.use(express.json()))和局部注册(路由内单独传入),且需在路由之前注册才能生效。
  3. 关键配置包括 limit(请求体大小限制)、strict(JSON 严格模式)、reviver(自定义解析逻辑)、type(指定解析的 Content-Type)。
  4. 常见问题中,req.bodyundefined 多由中间件注册顺序错误或 Content-Type 不匹配导致,Payload Too Large 需增大 limit 配置。
相关推荐
GDAL3 小时前
Express 中 CORS 跨域问题解决教程
express·cors
GDAL3 小时前
express.text和fetch配合使用深入全面教程
express·text
武藤一雄3 小时前
彻底吃透.NET中序列化反序列化
xml·微软·c#·json·.net·.netcore
liangshanbo12154 小时前
JSON-RPC 2.0 协议详解
网络协议·rpc·json
elangyipi1231 天前
深入理解前端项目中的 package.json 和 package-lock.json
前端·json
雪域迷影1 天前
Windows11中VS2026使用C++ 现代化json库nlohmann的3种方式
开发语言·c++·json
雪域迷影1 天前
nlohmann::json库对象和json结构体转换的新方式
c++·json·nlohmann_json库
GDAL1 天前
Express POST 请求深入全面讲解教程
express
生产队队长2 天前
Web:免费的JSON接口
json