HTTP条件请求与范围请求
概念引入
想象你去图书馆借书,管理员不会每次都把书从书架上取下来重新检查——她会先看登记卡:"这本书上次借出后有没有更新版本?"如果没有,直接告诉你"还是老样子,不用看了"。
条件请求(Conditional Request)就是这个思路:客户端在请求时带上"验证器",服务器比较后决定是否执行操作。如果资源没变化,直接返回 304 Not Modified,省去重复传输。
而范围请求(Range Request)则像你去蛋糕店说:"我只要中间那块草莓的,不要整个蛋糕"。客户端只请求资源的一部分,服务器返回 206 Partial Content。
在飞翔科技,凌叔(运维)经常需要更新服务器上的部署包,空少(前端)则负责官网视频播放。条件请求和范围请求正是他们日常工作的技术基石。
核心内容
验证器:资源的"身份证"
服务器给每个资源配两张"身份证":
| 验证器 | 比喻 | 特点 |
|---|---|---|
| Last-Modified | 书的"最后修订日期" | 时间戳,精度到秒,可能不够精准 |
| ETag | 书的"指纹/哈希值" | 内容稍有变化就完全不同,更可靠 |
ETag(Entity Tag)是服务器为资源生成的唯一标识符,通常基于文件内容的哈希计算。就像给文件按了个"指纹"——内容变一比特,指纹全变。
HTTP/1.1 200 OK
Last-Modified: Wed, 21 Oct 2026 07:28:00 GMT
ETag: "33a64df5-0012b"
Content-Type: application/octet-stream
条件请求头:五员大将
客户端携带验证器,通过以下请求头发起条件请求:
If-Modified-Since / If-Unmodified-Since
用 Last-Modified 做比较:
GET /deploy/package.zip HTTP/1.1
Host: www.feixiang.net
If-Modified-Since: Wed, 21 Oct 2026 07:28:00 GMT
- 资源在此时间后修改过 → 返回
200 OK+ 新内容 - 资源未修改 → 返回
304 Not Modified
If-Unmodified-Since 则相反:如果资源已被修改,返回 412 Precondition Failed(前置条件失败)。
场景:航仔(后端)上传新代码前,先检查"如果部署包没被人动过,我才覆盖"。
If-None-Match / If-Match
用 ETag 做比较,更精准:
GET /api/v2/config.json HTTP/1.1
Host: www.feixiang.net
If-None-Match: "33a64df5-0012b"
- ETag 不匹配 → 返回
200 OK+ 新内容 - ETag 匹配 → 返回
304 Not Modified
If-Match 常用于 PUT/DELETE:"只有资源还是这个版本,我才修改/删除"。
场景:图妹(产品经理)和星宇(产品助理)同时编辑需求文档。图妹先保存,ETag 变为
"v2"。星宇保存时带的还是"v1",服务器返回412 Precondition Failed,防止覆盖。
If-Range
If-Range 是范围请求的"保险栓":先检查资源是否变化,没变才发范围内容,否则发完整内容。
GET /video/intro.mp4 HTTP/1.1
Host: www.feixiang.net
Range: bytes=1024000-2047999
If-Range: "abc123"
- ETag 匹配 → 返回
206 Partial Content - ETag 不匹配 → 返回
200 OK(完整内容,避免客户端拿到过时的片段)
范围请求:只取所需
Range 头的格式
Range: bytes=0-1023 # 第0到1023字节(共1024字节)
Range: bytes=1024-2047 # 第1024到2047字节
Range: bytes=-500 # 最后500字节
Range: bytes=500- # 从第500字节到末尾
Range: bytes=0-1023,2048-3071 # 多段范围(多段响应用 multipart/byteranges)
服务器响应
HTTP/1.1 206 Partial Content
Content-Range: bytes 1024000-2047999/5242880
Content-Length: 1024000
[二进制数据...]
Content-Range 响应头格式:bytes 起始-结束/总大小
应用场景
飞翔公司场景:
断点续传:凌叔下载 5GB 的日志备份,下到 2GB 时断网。重连后发送
Range: bytes=2147483648-,从断点继续,不用从头再来。视频拖拽:空少做的官网宣传视频,用户直接拖到第 5 分钟。播放器发送
Range: bytes=5242880-,秒开播放。大文件分段下载:风速(算法)训练用的数据集 10GB,下载工具开 8 个线程,每个线程请求不同 Range,并行下载后合并。
本篇小结
- 条件请求通过验证器(Last-Modified/ETag)避免重复传输未变更的资源,节省带宽
- If-Modified-Since / If-None-Match 用于缓存刷新,匹配则返回
304 Not Modified - If-Match / If-Unmodified-Since 用于并发控制,不匹配则返回
412 Precondition Failed - If-Range 是范围请求的"保险栓",确保片段与整体版本一致
- 范围请求通过
Range: bytes=起始-结束只获取资源片段,服务器返回206 Partial Content - Content-Range 头告知客户端返回的是哪一段、总长度多少
- 典型应用:断点续传、视频拖拽、大文件多线程下载、缓存优化
动手实践
温馨提示: 以下实践示例中涉及的域名(如
www.feixiang.net)、公司场景和接口均为虚构数据,仅用于演示协议原理,实际执行时可能不会产生文档中描述的效果。建议将命令中的域名替换为你自己可访问的真实地址进行练习。
实践:用 curl 测试条件请求
# 先获取资源的 ETag
curl -I http://www.feixiang.net/static/logo.png
# 带上 If-None-Match 再次请求(如果未修改,返回 304)
curl -I -H 'If-None-Match: "abc123"' http://www.feixiang.net/static/logo.png
实践:用 curl 测试范围请求
# 只下载前 1024 字节
curl -r 0-1023 http://www.feixiang.net/download/setup.exe -o setup_head.exe
# 下载第 1024 到 2047 字节
curl -r 1024-2047 http://www.feixiang.net/download/setup.exe -o setup_part2.exe
实践:思考题
- 为什么 ETag 比 Last-Modified 更可靠?如果服务器在 1 秒内多次修改文件,Last-Modified 会出什么问题?
- 假设图妹和星宇同时编辑同一个文档,如何用 If-Match 实现"乐观锁"防止覆盖?画出请求时序图。
- 断点续传时,如果服务器不支持 Range 请求,会返回什么状态码?客户端应该如何降级处理?