Menu

Express 的 req.params、req.query 和 req.body 详解

req.params 保存路径中的命名部分(/users/:id),req.query 保存查询字符串(?sort=new),req.body 保存解析后的请求体;在你添加 express.json() 这类请求体解析器之前,它是 undefined。

本页包含可运行的编辑器。编辑、运行,立即查看输出。

在 Express 中,req.params 保存路由路径中命名段的值(/users/:id 对应 req.params.id),req.query 保存 ? 后面的查询字符串(/search?q=node 对应 req.query.q),req.body 保存请求体中发送的数据。每个请求都会填充 req.params 和 req.query,但在 app.use(express.json()) 这样的请求体解析器读取它之前,req.body 一直是 undefined。params 和 query 中的值永远是字符串。

第一个请求返回 {"params":{"userId":"7"},"query":{"sort":"new","limit":"2"}}。注意引号:7 和 2 都是字符串,所以在做运算或与数字比较之前,要先用 Number() 转换。现在把方法改成 POST,路径保持 /users/7/posts,发送请求体 {"title": "Hello", "draft": true}:响应是 201 和 "body":{"title":"Hello","draft":true},其中 true 是真正的布尔值,因为它来自 JSON。

req.params、req.query 与 req.body 的区别

req.paramsreq.queryreq.body
来源路径,/users/:id查询字符串,?sort=new请求体
示例 URL 或请求体/users/7/users?sort=new{"name": "Ada"}
值{ id: '7' }{ sort: 'new' }{ name: 'Ada' }
类型字符串字符串和字符串数组JSON 里是什么就是什么
需要的设置路由中的 :name无app.use(express.json())
典型用途指定哪个资源筛选、排序、分页要创建或更新的数据

一个路由可以有多个参数,每个名称都会成为 req.params 的一个键:/teams/:teamId/members/:memberId 会同时给出两个 id。连字符或点号也能分隔参数,所以 /flights/:from-:to 会把 /flights/LAX-SFO 变成 { from: 'LAX', to: 'SFO' }。

重复和缺失的 req.query 键

出现两次的键会变成数组,缺失的键是 undefined,所以要给选项设一个默认值:

响应是 {"page":1,"tags":["red","sale"],"raw":{"tag":["red","sale"]}}。发送 GET /products?tag=red&page=3,tags 仍然是数组 ["red"],因为 [].concat() 会把单个字符串或数组都变成数组。Express 5 用 Node 的简单解析器解析查询字符串,所以 ?filter[size]=m 得到的键是 "filter[size]",而不是一个嵌套对象;Express 4 则会把它解析成嵌套对象。

req.body 需要 express.json()

Node 以字节流的形式交付请求体。express.json() 会读取并解析它,然后设置 req.body,但只在请求带有 Content-Type: application/json 时才这样做。HTML 表单发送的是 application/x-www-form-urlencoded,需要改用 express.urlencoded()。

发送 POST /signup,请求体为 {"email": "ada@example.com"},你会得到 201 和 {"email":"ada@example.com","name":"anonymous"}。发送空请求体则得到 400。?? {} 很重要:没有它,任何没有 JSON 请求体的请求都会在解构 undefined 时抛出 TypeError。要真正校验结构和类型,请使用 zod 这类 schema 库。

req.headers 与 req.get()

req.headers 是一个包含所有请求头的对象,名称都是小写。req.get(name) 读取单个请求头并且不区分大小写,所以 req.get('Content-Type') 和 req.get('content-type') 返回同一个值。还有几个你会用到的属性:

path 是 /whoami,originalUrl 是 /whoami?debug=1。在请求栏中添加请求头 Authorization: Bearer abc 后再发送一次:token 变成 "Bearer abc"。在用 app.use('/api', router) 挂载的路由器里,req.path 会去掉 /api 前缀,而 req.originalUrl 会保留它(挂载的内容见 Express 路由)。

常见错误

在 GET 请求中读取 req.body。 浏览器和大多数客户端在 GET 请求中都不发送请求体。把 GET 的选项放进查询字符串。

用真值判断查询参数。 ?admin=false 得到的是字符串 'false',它是真值,所以 if (req.query.admin) 会通过。应该比较字符串:req.query.admin === 'true'。

发送 JSON 却没有设置请求头。 以 text/plain 提交 JSON 的客户端会得到 req.body === undefined。在客户端设置 Content-Type: application/json。

常见问题

在 Express 中如何获取客户端的 IP 地址?

读取 req.ip。在代理或负载均衡器后面,它是代理的地址,除非你设置了 app.set('trust proxy', 1),这样 Express 就会改为读取 X-Forwarded-For 请求头。

在 Express 中如何获取请求的完整 URL?

req.originalUrl 是客户端发来的路径加查询字符串。用 `${req.protocol}://${req.get('host')}${req.originalUrl}` 拼出绝对 URL。

在 Express 中如何处理文件上传?

文件上传以 multipart/form-data 格式到达,express.json() 和 express.urlencoded() 都不会解析它。使用 multer 这类包,它会把文件放进 req.file 或 req.files,把其他字段放进 req.body。

如何从请求中读取 cookie?

原始请求头在 req.get('cookie') 中。要在 req.cookies 中得到一个对象,请安装 cookie-parser 包并添加 app.use(cookieParser())。

Coddy 编程语言插图

和 Coddy 一起学编程

开始