后端面试:如何用 HTTP Prefer 协商响应,而不破坏幂等与缓存?
题干与适用场景
批量写入 API 的客户端有不同带宽和延迟目标:有的只需要状态,有的需要完整资源表示,还有的愿意等待几秒以避免轮询。请基于 RFC 7240 设计 Prefer 请求头、Preference-Applied 响应头、错误处理、缓存和降级。
Prefer 是请求偏好,不是服务器必须满足的指令。题目考察协议语义与 API 契约设计,不代表所有代理都会完整保留偏好头。
面试官考察点
- 能否区分客户端偏好与服务器承诺,并在响应中明确实际采用的偏好。
- 能否正确使用
return=minimal、return=representation、respond-async和wait。 - 能否处理 Prefer 导致的响应差异、
Vary、缓存键和代理兼容。 - 能否让重试、幂等键和异步任务状态在偏好未满足时仍然安全。
回答前需要澄清的问题
- API 是安全读取还是有副作用的写入,是否已有幂等键?
- 完整表示的大小、生成成本和最大等待时间是多少?
- 客户端能否接受 202 与状态资源,是否支持轮询或回调?
- 中间代理是否会透传 Prefer,缓存是否可能共享?
- 偏好未满足时,客户端需要看到哪些稳定字段?
30 秒回答框架
“Prefer 表达客户端偏好,服务器可以忽略或只部分采用;采用后用 Preference-Applied 明确回显。写入请求可用 return=minimal 减少响应,调试或同步场景可用 return=representation,长任务可用 respond-async 和有上限的 wait。我会把幂等键、202 状态资源、缓存 Vary 和客户端降级一起设计:没有 Preference-Applied 就按默认响应解析,不能假设偏好一定生效。”
分步骤深入解答
1. 把偏好当作可忽略协商
RFC 7240 定义 Prefer 请求头及 Preference-Applied 响应头。服务端可选择不应用偏好;因此响应体和状态码必须有稳定的默认契约,客户端不能仅凭发送了 Prefer 就跳过解析。
2. 选择合适的偏好令牌
return=minimal 适合只需要成功确认的写入;return=representation 适合希望拿到更新后资源的同步调用。respond-async 表示客户端接受异步处理,wait=n 表示愿意等待的时间上限。把这些令牌当作预算提示,不要把 wait 当作 SLA 保证。
3. 设计响应确认
采用偏好时返回 Preference-Applied;未采用时可以省略该头,并按默认契约返回。异步场景返回 202、任务状态 URI 和可追踪 ID;同步超出等待预算后也应保持可查询的任务状态,而不是让客户端重复提交副作用。
POST /v1/imports HTTP/1.1
Prefer: return=minimal, respond-async, wait=3
Idempotency-Key: imp-8f2
HTTP/1.1 202 Accepted
Preference-Applied: respond-async
Location: https://api.example/imports/jobs/42
Cache-Control: no-store4. 处理缓存变体
如果同一安全 GET 的表示会随 Prefer 改变,响应需要正确声明 Vary,或避免把偏好相关响应放入共享缓存。对写入响应通常使用 no-store;异步状态资源应定义短缓存、ETag 或明确的轮询条件,避免中间层返回过期进度。
5. 保证幂等与降级
Prefer 不改变操作语义,重试仍需遵守幂等性。写入接口使用幂等键或业务去重键;客户端若看到 202、超时或没有 Preference-Applied,应查询任务或按默认响应处理,不能因为偏好未确认就再次创建资源。
6. 设置观测和限界
记录偏好令牌、是否采用、等待时长、响应大小、202 比例和代理链路。限制允许的 wait 范围,拒绝过大的值或将其截断;对未知偏好采用忽略并记录,避免把任意客户端字符串变成昂贵的执行路径。
高质量示范回答
“我会把 Prefer 当作可忽略的客户端偏好,而不是承诺。写入接口默认返回稳定状态;客户端需要轻量响应时发送 return=minimal,需要资源表示时发送 return=representation,长任务使用 respond-async 和受限的 wait。服务器实际采用才返回 Preference-Applied;异步处理返回 202、Location 和任务 ID。写入使用幂等键,响应按安全性设置 no-store,表示差异通过 Vary 或缓存隔离处理。客户端没有 Preference-Applied 时走默认解析并查询任务,绝不因偏好不确定而重复副作用。”
常见错误
- 把 Prefer 当强制指令 → 服务器或代理可忽略它 → 用默认契约和 Preference-Applied确认。
- 把 wait 当完成保证 → 长任务仍可能超时 → 限制预算并提供 202 状态资源。
- 异步超时后再次提交 → 产生重复副作用 → 使用幂等键并先查询任务。
- 忽略响应变体缓存 → 客户端拿到不匹配的表示 → 设置 Vary 或隔离缓存。
- 接受任意未知偏好并执行昂贵逻辑 → 攻击者可放大资源消耗 → 未知偏好忽略、记录并限制令牌。
追问及应对
没有 Preference-Applied 是否代表请求失败?
不代表。服务器可能选择默认行为或忽略偏好;客户端应依据稳定默认契约解析响应,只有协议或业务状态明确失败时才判定失败。
return=minimal 能否用于所有写操作?
不能。它只表达客户端不需要完整表示;若客户端必须获得服务器生成的版本号、校验摘要或后续链接,就应请求表示或查询资源,不能从空响应中推断这些字段。
wait=5 是否会让服务器阻塞五秒?
不会构成硬性保证。它表示客户端愿意等待的偏好上限,服务器可更早返回、忽略或转为异步;服务端仍需执行自身超时、并发和资源预算。