400 是什么
400 Bad Request 表示服务器因请求本身有问题而拒绝处理:报文畸形、参数非法、结构化数据解析失败、请求头不合规,都会触发它。RFC 9110 §15.5.1 把它定义为服务端无法或不愿处理该请求的通用响应,因此它本身不携带根因信息,真正的原因要看响应体与访问日志。它与业务校验失败的差别在语义归属:请求根本没法被理解才用 400,字段能理解但不满足业务规则,应当用语义更精确的码。
基本信息
| 名称 | 400 Bad Request(RFC 9110 §15.5.1) |
|---|---|
| 作用 | 请求本身无法被理解或处理 |
| 常见根因 | 结构化数据语法错、内容类型缺失、头部畸形、编码异常 |
| 是否可重试 | 不可,必须先修正请求 |
| 归因 | 责任在调用方或中间层改写,而不是服务端逻辑 |
典型使用场景
- 请求体语法错误:缺括号、多逗号、字段值类型不符
- 缺少内容类型声明,服务端无法判断该怎么解析请求体
- 请求头不合规:字段名拼错、值里带非法字符
- 参数校验失败:必填项缺失、数字越界、枚举值不在允许范围
- 中间层改写:网关或安全设备认为请求特征非法而直接拒绝
复现并看清原因
复现并看清原因
curl -si -X POST https://api.example.com/users -H 'Content-Type: application/json' -d '{"name":}' # 故意构造语法错误
# 响应体通常会指出解析失败的位置,这是最快的定位线索
curl -si -X POST https://api.example.com/users -d 'name=alice' # 不声明内容类型,观察服务端是否拒绝
# 抓包确认服务端收到的报文与客户端发出的完全一致
sudo tcpdump -nn -A -c 20 'tcp port 443' | head -60常见根因对照
| 现象 | 可能成因 | 排查入口 |
|---|---|---|
| 解析类报错 | 请求体语法不正确 | 按响应体给出的位置信息逐字段核对 |
| 缺少内容类型 | 服务端按默认类型解析失败 | 核对请求头是否完整 |
| 编码异常 | 字符集与报文实际编码不一致 | 确认双方约定的字符集 |
| 只有部分客户端失败 | 客户端拼接报文时有差异 | 用抓包对比两端实际发送内容 |
排查步骤
- 先看响应体里是否指出了具体字段或位置,这比读服务端代码快得多
- 用命令行手工构造一次请求,确认是客户端构造问题还是网关改写问题
- 抓包对照两端报文,特别关注中间设备是否加了额外头部或改了内容
网关自身也会返回这个码:地址里带特殊字符被安全设备拦下、请求体超过网关限制、头部数量过多,都会在应用之前就被拒绝。先确认请求是否真的到达了应用,再看应用日志。
把业务校验失败一律返回请求错误码,会让客户端无法区分「报文写坏了」与「业务不允许」。前者修代码即可,后者往往要提示用户或走审批流程,两者的处理路径完全不同。