后端面试:如何用 Expect: 100-continue 安全上传大请求?
题干与适用场景
一个客户端向对象存储上传数百 MB 文件。服务端仅根据请求头就可能发现令牌无效、配额不足或媒体类型不允许。请设计 HTTP/1.1 上传握手,并说明 Expect: 100-continue、417 回退、代理链、超时、重试和监控策略。
这是后端协议与可靠性题。文件大小和状态码是题设,不代表线上频率。
面试官考察点
- 能否说明先验权检查如何避免无效请求体传输。
- 能否准确描述 100、最终响应和 417 的职责。
- 能否处理代理忽略 Expect、客户端等待超时和连接复用。
- 能否把幂等、可重放、校验和可观测性放到正确边界。
回答前需要澄清的问题
- 上传是否幂等,是否已有上传会话或幂等键?
- 请求体是否可重新读取,客户端能否从磁盘重新打开文件?
- 哪些网关和对象存储支持 informational response?
- 失败时客户端是否允许无 Expect 重试,是否会产生重复对象?
- 服务端检查哪些头即可拒绝,哪些规则必须读取完整正文?
30 秒回答框架
“客户端先发送带 Expect: 100-continue 的请求头。服务端先校验认证、长度、类型、配额和路由;能立即拒绝就返回最终 4xx,允许接收才返回 100 Continue。客户端收到 100 后发送正文,等待超时要有上限。若得到 417,且请求语义允许,去掉 Expect 后重试;上传命令必须配合幂等键和可重读正文。我要用代理矩阵、字节节省、417 比例和正文中止率验证整条链路。”
分步骤深入解答
1. 先发送请求头
请求头包含认证、Content-Length、Content-Type、校验摘要、租户和幂等键,以及 Expect: 100-continue。服务端可在未读取正文前检查令牌、路由、配额和静态大小上限。规范要求服务器在判断后发送中间响应或最终响应,不能让客户端无限等待。
PUT /objects/o-123 HTTP/1.1
Host: upload.example
Authorization: Bearer ...
Content-Length: 524288000
Content-Type: application/octet-stream
Idempotency-Key: up-7f2
Expect: 100-continue2. 处理允许和拒绝
允许接收时返回 100 Continue,客户端再发送正文。认证失败、长度超限或配额不足时直接返回最终状态,例如 401、403、413 或 415,客户端不应继续发送尚未发送的正文。最终状态仍由完整上传流程决定,预检只能覆盖头部可判断的规则。
3. 正确使用 417
417 Expectation Failed 表示服务器或中间件不能满足期望。客户端可在请求语义允许时删除 Expect 并重新发送,但必须确认正文可重读、幂等键保持不变、旧连接没有残余正文。不要把任何 4xx 都当成 417,也不要自动重放不可逆的非幂等操作。
4. 处理等待、代理与连接
客户端应为等待 100 设置有限超时;超时后是否直接发送正文要遵循实现约定,并记录该路径。HTTP/1.0 中间件可能忽略 Expect,代理也可能代发 100,因此上线前要验证每一跳是否转发头部、是否吞掉 informational response、是否在最终拒绝后关闭或排空连接,避免下一次请求读取到残留字节。
5. 设计重试和幂等
只有正文源可回退且业务允许重复时才重试。对于创建对象或扣减配额等副作用操作,使用稳定幂等键,让服务端把重复尝试映射到同一结果。连接断开后的状态可能未知,客户端先查询上传会话或对象状态,不能因为没有收到响应就新建第二个对象。
6. 把安全校验放到两层
预检校验认证、租户、长度、类型和配额;正文接收后仍需校验真实字节数、摘要、恶意内容和存储策略。不要仅信任 Content-Length 或客户端声明的 MIME。日志记录请求 ID、预检结果和已接收字节数,禁止记录令牌和文件内容。
高质量示范回答
“我会把上传拆成头部决策和正文接收两个阶段。客户端发送认证、长度、类型、摘要、幂等键和 Expect: 100-continue。服务端先做便宜且确定的拒绝;通过后返回 100,客户端才发送大正文。417 只触发去掉 Expect 的可安全重试,且保持幂等键;等待超时、连接断开和代理忽略都通过状态查询恢复。正文仍要做字节数、摘要和内容安全校验。我会在真实代理矩阵中测试 100 延迟、417、提前 4xx、连接复用和节省字节。”
常见错误
- 把 100 当成功响应 → 100 只是允许发送正文 → 等待最终 2xx 或明确失败。
- 收到任意 4xx 都无 Expect 重试 → 可能重复副作用 → 只对 417 且业务可重放时回退。
- 无限等待 100 → 连接和用户请求悬挂 → 设置有限等待并记录超时路径。
- 只做头部校验 → 恶意或损坏正文仍可入库 → 正文阶段再次校验。
- 忽略代理差异 → 生产中可能提前发正文或留下残余字节 → 逐跳验收并限制连接复用风险。
追问及应对
服务端返回 401 后客户端已经发送了一部分正文怎么办?
服务端要按连接策略关闭或继续读取并丢弃正文,客户端停止继续发送并标记本次尝试失败。连接重新复用前必须确认协议状态已对齐。
为什么不总是直接发送正文?
小请求可以直接发送;大请求的收益来自在认证、长度或配额失败时节省上传字节和存储入口压力。是否启用应按请求大小、代理兼容性和实现复杂度灰度。
HTTP/2 或 HTTP/3 还需要这个思路吗?
不能简单假设 HTTP/1.1 的逐字节行为完全相同。应验证所用客户端、网关和服务器如何处理 informational response、流量控制和取消流;核心仍是尽早拒绝、可恢复上传和幂等状态。