前端进阶之旅前端进阶之旅
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片
  • 场景篇按分类整理的大前端场景考点
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
  • 动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
  • AI 助手随时提问,即时解析
  • AI 模拟面试模拟真实面试 + 报告
  • AI 知识地图串起全站知识点
  • AI 定制路线按你的简历现排
AI 热点
旧版
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片
  • 场景篇按分类整理的大前端场景考点
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
  • 动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
  • AI 助手随时提问,即时解析
  • AI 模拟面试模拟真实面试 + 报告
  • AI 知识地图串起全站知识点
  • AI 定制路线按你的简历现排
AI 热点
旧版
  • 基础篇

    • 环境搭建
    • node部署
    • 基础应用
  • 内置模块

    • 1.0 本地路径处理 path
    • 2.0 文件系统操作 fs
    • 3.1 基础调试 console
    • 3.2 本地调试远程服务器上的Node代码
    • 4.1 网络服务 http
    • 4.2 网络服务 http res
    • 4.3 网络服务 http req
    • 4.4 网络服务 http server
    • 4.5 网络服务 http client
    • 4.6 网络服务 https
    • 4.7 网络TCP net
    • 4.8 网络UDP dgram
    • 4.9 域名解析 dns
    • 5.0 网络地址解析 url
    • 5.1 URL查询字符串 querystring
    • 6.1 流操作 stream
    • 6.2 逐行读取 readline
    • 7.1 进程相关 process
    • 7.2 子进程 child
    • 8.1 二进制数据 buffer
    • 8.2 二进制解码 string_decoder
    • 9.1 事件机制 events
    • 9.2 实用工具模块 util
    • 9.3 数据加密 crypto
    • 9.4 MD5入门介绍及crypto模块的应用
    • 9.4 资源压缩 zlib
    • 9.5 集群 cluster
    • 9.6 v8
  • 进阶篇

    • 5分钟入门非对称加密用法
    • cluster
    • cookie_parser深入
    • crypto模块之理论篇
    • express+cookie_parser签名机制深入剖析
    • express+session实现简易身份认证
    • https
    • log4js入门实例
    • node8_napi
    • 使用 async 控制并发
    • 使用 eventproxy 控制并发
    • 使用 superagent 与 cheerio 完成简单爬虫
    • 基于express+muter的文件上传
    • 将图片转成datauri嵌入到html
    • 常用中间件 body_parser 实现解析
    • 日志模块morgan
    • 服务端字符编解码&乱码处理 charset_enc_dec
    • 测试用例mocha,should,istanbul
    • 调试日志打印debug模块
  • Express

    • 1.1 Express概览篇
    • 1.2 Express
  • Koa2

    • 1.0 koa2概览篇
    • 1.1 快速开始
    • 1.2 async await使用
    • 1.3 koa2简析结构
    • 1.4 koa中间件开发与使用
    • 10.1 单元测试
    • 11.1 开发debug
    • 12.1 快速启动
    • 12.2 框架设计
    • 12.3 分层操作
    • 12.4 数据库设计
    • 12.5 路由设计
    • 12.6 webpack4环境搭建
    • 12.7 使用react
    • 12.8 登录注册功能实现
    • 12.9 session登录态判断处理
    • 13.1 import export使用
    • 2.1 原生koa2实现路由
    • 2.2 koa router中间
    • 3.1 GET请求数据获取
    • 3.2 POST请求数据获取
    • 3.3 koa bodyparser中间件
    • 4.1 原生koa2实现静态资源服务器
    • 4.2 koa static中间件
    • 5.1 koa2使用cookie
    • 5.2 koa2实现session
    • 6.1 koa2加载模板引擎
    • 6.2 ejs模板引擎
    • 7.1 busboy模块
    • 7.2 上传文件简单实现
    • 7.3 异步上传图片实现
    • 8.1 mysql模块
    • 8.2 async await封装使用mysql
    • 8.3 项目建表初始化
    • 9.1 原生koa2实现JSONP
    • 9.2 koa jsonp中间件
  • 其他

    • 操作数据库
    • Session 与 Token
    • Cookie、Session、Token、JWT
    • Socket

# POST请求参数获取

30 秒速记

  • POST 数据在请求体里,不在 URL 上,ctx.query 拿不到
  • Koa 不解析请求体:要么自己读 ctx.req 流,要么用 @koa/bodyparser,结果放在 ctx.request.body
  • 按 Content-Type 选解析:application/json、application/x-www-form-urlencoded、multipart/form-data(文件上传)各不相同
  • 一定要有大小上限(bodyparser 默认 JSON 1mb、表单 56kb),解析完还要校验字段
  • 请求体是流,只能读一次:前面的中间件读过了,后面就拿不到

POST 的参数在请求体里,而请求体在 Node 里是一个流,Koa 本身不帮你解析,ctx.query 只管 URL 上的参数。 拿数据有两条路:自己监听 ctx.req 的 data / end 把流读完再解析,或者用 @koa/bodyparser 这类中间件,结果放在 ctx.request.body。实际项目我都用中间件,它会按 Content-Type 选解析方式,还自带大小限制,JSON 默认 1mb、表单 56kb。文件上传是 multipart/form-data,bodyparser 不管,要用 koa-body 或 @koa/multer。不管哪种,解析出来的数据照样不可信,字段校验不能省。

理解「POST请求参数获取」时,要先明确输入、运行阶段和可观察输出。这里的交互实验会分别执行正常路径与失败路径,并把 Node.js 的关键状态变化和同屏代码对应起来;不能只记命令名称。

工程中还要验证退出码、资源清理和异常输入。属于 module 场景的配置应保持最小可复现,并在开发环境与 CI 中使用相同命令复跑,避免把平台差异或隐式默认值带到线上。

用 @koa/bodyparser 接收 POST 数据的最小例子,JSON 和表单编码都能处理:

const Koa = require('koa')
const { bodyParser } = require('@koa/bodyparser')   // 老项目常见的是 require('koa-bodyparser')

const app = new Koa()
app.use(bodyParser({ jsonLimit: '1mb', formLimit: '100kb' }))

app.use(async ctx => {
  if (ctx.method !== 'POST') return
  const { email } = ctx.request.body ?? {}
  if (typeof email !== 'string' || !email.includes('@')) ctx.throw(400, 'email 不合法')
  ctx.body = { ok: true, email }
})

app.listen(3000)
// curl -X POST localhost:3000 -H 'Content-Type: application/json' -d '{"email":"a@b.c"}'
// -> {"ok":true,"email":"a@b.c"}
// curl -X POST localhost:3000 -d 'email=a%40b.c'      表单编码,结果一样
@前端进阶之旅: 代码已经复制到剪贴板

选型上可以这么记:只收 JSON 和普通表单,用 @koa/bodyparser;要接文件上传,用 koa-body 或 @koa/multer;需要拿原始字节做签名校验(比如支付回调),确认解析中间件保留了原始正文,或者在这条路由上不用通用解析器,自己读流。

面试官追问

追问 1注册接口用 POST 提交,后端读 ctx.query.email 一直是空的,为什么?
参考回答

表单字段在请求体里,ctx.query 只解析 URL 上的查询串。挂上 bodyparser 之后从 ctx.request.body.email 拿。

追问 2移动端从表单编码改成发 JSON,后端要改什么?
参考回答

用 bodyparser 的话基本不用改,它看 Content-Type 自动选解析方式。如果是手写的按 &、= 拆分,JSON 文本会被拆成一堆乱字段,得按 Content-Type 分开处理,不认识的类型回 415。

追问 3挂了 bodyparser,上传文件时 ctx.request.body 还是空的,为什么?
参考回答

bodyparser 只处理 json、form、text,不碰 multipart/form-data。文件上传用 koa-body(开 multipart: true)或 @koa/multer,同时限制单文件大小和文件类型。

追问 4有人发了个 50MB 的 JSON,服务内存一下涨上去了,怎么防?
参考回答

bodyparser 超过 jsonLimit 会直接回 413,别把上限随手调得很大。手写解析就要在 data 里累计字节数,超了立刻停止接收并报错;Nginx 那层也可以用 client_max_body_size 先挡一道。

追问 5日志中间件读了一遍请求体,后面的 bodyparser 就拿不到数据了,为什么?
参考回答

请求体是流,读完就没了,不能重复读。让 bodyparser 放在最前面,日志中间件读 ctx.request.body;要原始文本的话,bodyparser 会把它放在 ctx.request.rawBody。

# POST请求参数获取

# 原理

30 秒速记

  • Koa 2 没封装 POST 解析,要从原生请求 ctx.req 上读流
  • 表单提交的正文长这样:a=1&b=2&c=3,读完再解析成 { a: '1', b: '2', c: '3' }
  • ctx.request / ctx.response 是 Koa 封装的;ctx.req / ctx.res 是 Node 原生的 IncomingMessage / ServerResponse
  • 数据分块(chunk)到达,要等 end 再解析;先收 Buffer 最后 Buffer.concat
  • 响应走 ctx.body,别直接 ctx.res.end(),会绕开 Koa 的响应流程

Koa 2 没有内置 POST 解析,原理就是从 Node 原生请求对象 ctx.req 上把请求体这个流读完,再按格式解析。 以表单为例,浏览器发过来的正文是 a=1&b=2&c=3 这样的字符串,读完后按 & 和 = 拆成 { a: '1', b: '2', c: '3' }。这里要分清两套对象:ctx.request、ctx.response 是 Koa 包装过的,平时都用它们;ctx.req、ctx.res 是原生的,读原始流时才碰 ctx.req。响应一定走 ctx.body,直接 ctx.res.end() 等于绕开了 Koa,外层中间件和错误处理都管不到了。

对于POST请求的处理,koa2没有封装获取参数的方法,需要通过解析上下文context中的原生node.js请求对象req,将POST表单数据解析成query string(例如:a=1&b=2&c=3),再将query string 解析成JSON格式(例如:{"a":"1", "b":"2", "c":"3"})

注意:ctx.request是context经过封装的请求对象,ctx.req是context提供的node.js原生HTTP请求对象,同理ctx.response是context经过封装的响应对象,ctx.res是context提供的node.js原生HTTP响应对象。

具体koa2 API文档可见 https://github.com/koajs/koa/blob/master/docs/api/context.md#ctxreq

面试官追问

追问 1ctx.request 和 ctx.req 是同一个对象吗?
参考回答

不是。ctx.request 是 Koa 的封装,上面有 query、path,挂了 bodyparser 还有 body;ctx.req 是 Node 原生的 http.IncomingMessage,是个可读流。它俩的关系是 ctx.request.req === ctx.req。

追问 2为什么不能在第一个 data 事件里就开始解析?
参考回答

请求体是分块到达的,稍大一点的表单可能分好几个 chunk,第一块可能只有一半。要累积到 end 触发再解析。

追问 3发 JSON 时在 data 里直接拼字符串,中文偶尔乱码,为什么?
参考回答

一个中文字符的 UTF-8 编码占 3 个字节,可能正好被切在两个 chunk 之间,各自转字符串就坏了。先把 Buffer 收进数组,end 时 Buffer.concat(chunks).toString('utf8'),或者一开始就 ctx.req.setEncoding('utf8')。

追问 4业务里直接 ctx.res.end('ok') 返回,会出什么问题?
参考回答

Koa 的响应流程被绕开了:外层中间件再设响应头会报 ERR_HTTP_HEADERS_SENT,日志里记的状态码也可能对不上。真要直接操作 ctx.res,官方的做法是先设 ctx.respond = false。

追问 5文件上传也能用「拼成字符串再拆」这套思路吗?
参考回答

不能。

基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片
  • 场景篇按分类整理的大前端场景考点
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
  • 动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
  • AI 助手随时提问,即时解析
  • AI 模拟面试模拟真实面试 + 报告
  • AI 知识地图串起全站知识点
  • AI 定制路线按你的简历现排
AI 热点
旧版
  • 基础篇

    • 环境搭建
    • node部署
    • 基础应用
  • 内置模块

    • 1.0 本地路径处理 path
    • 2.0 文件系统操作 fs
    • 3.1 基础调试 console
    • 3.2 本地调试远程服务器上的Node代码
    • 4.1 网络服务 http
    • 4.2 网络服务 http res
    • 4.3 网络服务 http req
    • 4.4 网络服务 http server
    • 4.5 网络服务 http client
    • 4.6 网络服务 https
    • 4.7 网络TCP net
    • 4.8 网络UDP dgram
    • 4.9 域名解析 dns
    • 5.0 网络地址解析 url
    • 5.1 URL查询字符串 querystring
    • 6.1 流操作 stream
    • 6.2 逐行读取 readline
    • 7.1 进程相关 process
    • 7.2 子进程 child
    • 8.1 二进制数据 buffer
    • 8.2 二进制解码 string_decoder
    • 9.1 事件机制 events
    • 9.2 实用工具模块 util
    • 9.3 数据加密 crypto
    • 9.4 MD5入门介绍及crypto模块的应用
    • 9.4 资源压缩 zlib
    • 9.5 集群 cluster
    • 9.6 v8
  • 进阶篇

    • 5分钟入门非对称加密用法
    • cluster
    • cookie_parser深入
    • crypto模块之理论篇
    • express+cookie_parser签名机制深入剖析
    • express+session实现简易身份认证
    • https
    • log4js入门实例
    • node8_napi
    • 使用 async 控制并发
    • 使用 eventproxy 控制并发
    • 使用 superagent 与 cheerio 完成简单爬虫
    • 基于express+muter的文件上传
    • 将图片转成datauri嵌入到html
    • 常用中间件 body_parser 实现解析
    • 日志模块morgan
    • 服务端字符编解码&乱码处理 charset_enc_dec
    • 测试用例mocha,should,istanbul
    • 调试日志打印debug模块
  • Express

    • 1.1 Express概览篇
    • 1.2 Express
  • Koa2

    • 1.0 koa2概览篇
    • 1.1 快速开始
    • 1.2 async await使用
    • 1.3 koa2简析结构
    • 1.4 koa中间件开发与使用
    • 10.1 单元测试
    • 11.1 开发debug
    • 12.1 快速启动
    • 12.2 框架设计
    • 12.3 分层操作
    • 12.4 数据库设计
    • 12.5 路由设计
    • 12.6 webpack4环境搭建
    • 12.7 使用react
    • 12.8 登录注册功能实现
    • 12.9 session登录态判断处理
    • 13.1 import export使用
    • 2.1 原生koa2实现路由
    • 2.2 koa router中间
    • 3.1 GET请求数据获取
    • 3.2 POST请求数据获取
    • 3.3 koa bodyparser中间件
    • 4.1 原生koa2实现静态资源服务器
    • 4.2 koa static中间件
    • 5.1 koa2使用cookie
    • 5.2 koa2实现session
    • 6.1 koa2加载模板引擎
    • 6.2 ejs模板引擎
    • 7.1 busboy模块
    • 7.2 上传文件简单实现
    • 7.3 异步上传图片实现
    • 8.1 mysql模块
    • 8.2 async await封装使用mysql
    • 8.3 项目建表初始化
    • 9.1 原生koa2实现JSONP
    • 9.2 koa jsonp中间件
  • 其他

    • 操作数据库
    • Session 与 Token
    • Cookie、Session、Token、JWT
    • Socket
完整面试题地址:
作者:程序员poetry
扫码关注作者公众号:「前端进阶之旅」 每天分享技术干货
前端进阶之旅公众号二维码

POST请求参数获取|Nodejs专题系列

# POST请求参数获取

30 秒速记

  • POST 数据在请求体里,不在 URL 上,ctx.query 拿不到
  • Koa 不解析请求体:要么自己读 ctx.req 流,要么用 @koa/bodyparser,结果放在 ctx.request.body
  • 按 Content-Type 选解析:application/json、application/x-www-form-urlencoded、multipart/form-data(文件上传)各不相同
  • 一定要有大小上限(bodyparser 默认 JSON 1mb、表单 56kb),解析完还要校验字段
  • 请求体是流,只能读一次:前面的中间件读过了,后面就拿不到

POST 的参数在请求体里,而请求体在 Node 里是一个流,Koa 本身不帮你解析,ctx.query 只管 URL 上的参数。 拿数据有两条路:自己监听 ctx.req 的 data / end 把流读完再解析,或者用 @koa/bodyparser 这类中间件,结果放在 ctx.request.body。实际项目我都用中间件,它会按 Content-Type 选解析方式,还自带大小限制,JSON 默认 1mb、表单 56kb。文件上传是 multipart/form-data,bodyparser 不管,要用 koa-body 或 @koa/multer。不管哪种,解析出来的数据照样不可信,字段校验不能省。

理解「POST请求参数获取」时,要先明确输入、运行阶段和可观察输出。这里的交互实验会分别执行正常路径与失败路径,并把 Node.js 的关键状态变化和同屏代码对应起来;不能只记命令名称。

工程中还要验证退出码、资源清理和异常输入。属于 module 场景的配置应保持最小可复现,并在开发环境与 CI 中使用相同命令复跑,避免把平台差异或隐式默认值带到线上。

用 @koa/bodyparser 接收 POST 数据的最小例子,JSON 和表单编码都能处理:

const Koa = require('koa')
const { bodyParser } = require('@koa/bodyparser')   // 老项目常见的是 require('koa-bodyparser')

const app = new Koa()
app.use(bodyParser({ jsonLimit: '1mb', formLimit: '100kb' }))

app.use(async ctx => {
  if (ctx.method !== 'POST') return
  const { email } = ctx.request.body ?? {}
  if (typeof email !== 'string' || !email.includes('@')) ctx.throw(400, 'email 不合法')
  ctx.body = { ok: true, email }
})

app.listen(3000)
// curl -X POST localhost:3000 -H 'Content-Type: application/json' -d '{"email":"a@b.c"}'
// -> {"ok":true,"email":"a@b.c"}
// curl -X POST localhost:3000 -d 'email=a%40b.c'      表单编码,结果一样
@前端进阶之旅: 代码已经复制到剪贴板

选型上可以这么记:只收 JSON 和普通表单,用 @koa/bodyparser;要接文件上传,用 koa-body 或 @koa/multer;需要拿原始字节做签名校验(比如支付回调),确认解析中间件保留了原始正文,或者在这条路由上不用通用解析器,自己读流。

面试官追问

追问 1注册接口用 POST 提交,后端读 ctx.query.email 一直是空的,为什么?
参考回答

表单字段在请求体里,ctx.query 只解析 URL 上的查询串。挂上 bodyparser 之后从 ctx.request.body.email 拿。

追问 2移动端从表单编码改成发 JSON,后端要改什么?
参考回答

用 bodyparser 的话基本不用改,它看 Content-Type 自动选解析方式。如果是手写的按 &、= 拆分,JSON 文本会被拆成一堆乱字段,得按 Content-Type 分开处理,不认识的类型回 415。

追问 3挂了 bodyparser,上传文件时 ctx.request.body 还是空的,为什么?
参考回答

bodyparser 只处理 json、form、text,不碰 multipart/form-data。文件上传用 koa-body(开 multipart: true)或 @koa/multer,同时限制单文件大小和文件类型。

追问 4有人发了个 50MB 的 JSON,服务内存一下涨上去了,怎么防?
参考回答

bodyparser 超过 jsonLimit 会直接回 413,别把上限随手调得很大。手写解析就要在 data 里累计字节数,超了立刻停止接收并报错;Nginx 那层也可以用 client_max_body_size 先挡一道。

追问 5日志中间件读了一遍请求体,后面的 bodyparser 就拿不到数据了,为什么?
参考回答

请求体是流,读完就没了,不能重复读。让 bodyparser 放在最前面,日志中间件读 ctx.request.body;要原始文本的话,bodyparser 会把它放在 ctx.request.rawBody。

# POST请求参数获取

# 原理

30 秒速记

  • Koa 2 没封装 POST 解析,要从原生请求 ctx.req 上读流
  • 表单提交的正文长这样:a=1&b=2&c=3,读完再解析成 { a: '1', b: '2', c: '3' }
  • ctx.request / ctx.response 是 Koa 封装的;ctx.req / ctx.res 是 Node 原生的 IncomingMessage / ServerResponse
  • 数据分块(chunk)到达,要等 end 再解析;先收 Buffer 最后 Buffer.concat
  • 响应走 ctx.body,别直接 ctx.res.end(),会绕开 Koa 的响应流程

Koa 2 没有内置 POST 解析,原理就是从 Node 原生请求对象 ctx.req 上把请求体这个流读完,再按格式解析。 以表单为例,浏览器发过来的正文是 a=1&b=2&c=3 这样的字符串,读完后按 & 和 = 拆成 { a: '1', b: '2', c: '3' }。这里要分清两套对象:ctx.request、ctx.response 是 Koa 包装过的,平时都用它们;ctx.req、ctx.res 是原生的,读原始流时才碰 ctx.req。响应一定走 ctx.body,直接 ctx.res.end() 等于绕开了 Koa,外层中间件和错误处理都管不到了。

对于POST请求的处理,koa2没有封装获取参数的方法,需要通过解析上下文context中的原生node.js请求对象req,将POST表单数据解析成query string(例如:a=1&b=2&c=3),再将query string 解析成JSON格式(例如:{"a":"1", "b":"2", "c":"3"})

注意:ctx.request是context经过封装的请求对象,ctx.req是context提供的node.js原生HTTP请求对象,同理ctx.response是context经过封装的响应对象,ctx.res是context提供的node.js原生HTTP响应对象。

具体koa2 API文档可见 https://github.com/koajs/koa/blob/master/docs/api/context.md#ctxreq

面试官追问

追问 1ctx.request 和 ctx.req 是同一个对象吗?
参考回答

不是。ctx.request 是 Koa 的封装,上面有 query、path,挂了 bodyparser 还有 body;ctx.req 是 Node 原生的 http.IncomingMessage,是个可读流。它俩的关系是 ctx.request.req === ctx.req。

追问 2为什么不能在第一个 data 事件里就开始解析?
参考回答

请求体是分块到达的,稍大一点的表单可能分好几个 chunk,第一块可能只有一半。要累积到 end 触发再解析。

追问 3发 JSON 时在 data 里直接拼字符串,中文偶尔乱码,为什么?
参考回答

一个中文字符的 UTF-8 编码占 3 个字节,可能正好被切在两个 chunk 之间,各自转字符串就坏了。先把 Buffer 收进数组,end 时 Buffer.concat(chunks).toString('utf8'),或者一开始就 ctx.req.setEncoding('utf8')。

追问 4业务里直接 ctx.res.end('ok') 返回,会出什么问题?
参考回答

Koa 的响应流程被绕开了:外层中间件再设响应头会报 ERR_HTTP_HEADERS_SENT,日志里记的状态码也可能对不上。真要直接操作 ctx.res,官方的做法是先设 ctx.respond = false。

追问 5文件上传也能用「拼成字符串再拆」这套思路吗?
参考回答

不能。multipart/form-data 按 boundary 分段,里面夹着二进制文件,转字符串会把文件弄坏,体积大了也不该全读进内存。用 busboy 这类流式解析库,或者直接上 koa-body。

# 解析出POST请求上下文中的表单数据

30 秒速记

  • 监听 ctx.req 的 data 累积、end 时解析,包成 Promise 给中间件 await
  • parseQueryStr 按 &、= 拆再 decodeURIComponent:+ 不会变空格,没编码的 = 会截断值
  • 外层 try/catch 只包住了注册监听这步;end 回调里 decodeURIComponent 遇到畸形 % 抛错,会变成未捕获异常打挂进程
  • 要补:大小上限、error 监听、按 Buffer 收集
  • 生产直接用 URLSearchParams / querystring.parse,或者上 @koa/bodyparser

这段代码分两步:parsePostData 监听原生请求流,把数据块拼起来,等 end 再交给 parseQueryStr;parseQueryStr 按 & 和 = 拆成对象。 包一层 Promise 是为了在 async 中间件里直接 await 结果。拿来理解原理很合适,但手写解析的边界问题不少:decodeURIComponent 不会把 + 还原成空格,表单里的 Tom+Lee 解析出来还是 Tom+Lee;外层那个 try/catch 只包住了注册监听这几行,end 回调里遇到畸形的 % 抛错是接不住的,会直接把进程打挂。生产里我会用 URLSearchParams,或者干脆上 bodyparser。

← 3.1 GET请求数据获取3.3 koa bodyparser中间件 →

fe