DEBUG.WIKI
V.PS:让项目靠近用户,亚太、欧洲、北美 KVM VPS
IPPeak:从目标地区访问网页,住宅代理、SEO 监测、广告验证
ZgoCloud:多地区部署,按需选节点;日本、美国、香港、德国

curl 表单里的 &、+ 和 % 导致字段值改变

curl 提交表单时特殊字符被当作分隔符或转义符;每个字段分别编码,避免整串参数变成一个 username 值。

用 curl 提交登录表单时,普通密码可以使用,含 % 的密码却报 URLDecoder: Incomplete trailing escape (%) pattern;或者换成 --data-urlencode 后,服务端又说缺少 password 字段。两个现象都需要检查表单编码方式。

本文适用于接收 application/x-www-form-urlencoded 的 HTTP 接口,以及支持 --data-urlencode 的 curl 命令行。JSON 和文件上传接口使用不同格式,不直接套用这个办法。示例采用 Bash 风格引号,所有用户名、密码和域名都是占位值。

给整串参数加引号,仍不会编码字段值

下面的示例命令把 shell 参数正确地保留为一个字符串,但 curl 的 --data 不会自动把其中的字段值转成表单编码:

curl --data 'username=demo&password=a&b+%' \
  'https://example.com/login'

表单解析器用 & 分隔字段,用 = 分隔字段名与值。字段值中的 + 通常被解码为空格,% 则被解释为百分号编码的起始字符。因而这条请求不代表密码原样为 a&b+%。

shell 引号解决的是命令行如何分词;表单编码解决的是服务器如何读取请求体。继续添加反斜杠,或仅改成 --data-raw,不会自动完成字段值的 URL 编码。

每个字段分别使用 --data-urlencode

把每个 name=value 分别交给 curl:

curl --data-urlencode 'username=demo' \
  --data-urlencode 'password=a&b+%' \
  'https://example.com/login'

curl 默认会采用 POST 和表单 Content-Type,不需要额外写 -X POST。在这个例子中,密码值经过编码,字段间的分隔符仍由 curl 正确连接。下面只是请求体示例,不是本站抓包:

username=demo&password=a%26b%2B%25

如果字段值来自已有变量,可以使用 --data-urlencode "password=$PASSWORD"。变量必须按 shell 规则引用,避免空格或其他字符被再次分词。示例值不是实际凭据;排查真实认证请求时,不要把密码、令牌或完整请求体贴到公开日志里。

为什么一次编码整串后,password 消失了

下面的写法仍然有问题:

curl --data-urlencode 'username=demo&password=a&b+%' \
  'https://example.com/login'

name=value 语法会把第一个 = 后面的内容当作一个字段的值。于是 &password=... 也进入 username 的编码值,而不是成为第二个字段。服务端拿不到 password 字段,与密码是否正确无关。

应拆成多个选项;如果请求体已经由可靠的表单编码器生成,则按其接口发送,避免再编码一次。--data-urlencode 期望字段名已经正确编码;常规的 username、password 不需要额外处理,但特殊字段名不能假定会被一起编码。

怎样确认请求格式已经正确

用不含真实秘密的示例值检查服务端收到的字段,确认字段数量、字段名和解码后的值都符合预期。密码示例应恢复为原来的 a&b+%,不能多出空格、截断,或变成带 password= 的 username。

格式正确后仍出现认证失败,再检查账号、权限和接口要求。不要通过关闭 HTTPS 证书验证解决表单编码问题;它不会改变请求体的解析方式。