Conditional GET:定义、条件请求机制

作者: IT Sectr 发布日期: 2026-06-14 阅读时间: 7 分钟

Conditional GET(条件 GET 请求)— HTTP 机制,允许客户端在完全加载之前检查缓存资源的有效性。客户端发送带有 If-None-Match(包含 ETag)或 If-Modified-Since(包含日期)头的 GET 请求,如果资源未更改,服务器返回 304 Not Modified,不带响应体。根据 MDN Web Docs,2025,条件请求可减少服务器和客户端的网络流量。304 Not Modified — 移动应用高效同步的关键 HTTP 状态码。

要点

  • Conditional GET — 带有 If-None-Match 或 If-Modified-Since 头的 HTTP 请求,用于检查缓存的有效性。
  • 304 Not Modified — 服务器响应,指示资源未更改。不传输响应体,节省流量。
  • If-None-Match — 带有 ETag(版本哈希)的头,提供资源内容级别的精确检查。
  • If-Modified-Since — 带有最后修改日期的头,实现更简单但精度较低(1 秒分辨率)。
  • 效率 — Conditional GET 对于未更改资源,可将同步数据量减少 80–95%。

HTTP 中的 Conditional GET 是什么?

Conditional GET — 是一个 GET 请求,包含一个或多个条件头,服务器根据这些条件头决定返回完整响应还是仅返回 304 Not Modified 状态码。主要目的是如果资源自上次请求以来未更改,则避免传输响应体。这是 HTTP 缓存的基本机制,在 RFC 7232 规范中定义。

对于移动应用,Conditional GET 是优化网络流量最有效的方法之一。典型场景:打开应用时,客户端发送一系列条件 GET 请求以加载信息流、个人资料和设置。如果数据未更改,应用收到 304 并使用本地副本。这需要毫秒而非秒,且不消耗移动流量。

根据 Google Web Fundamentals(2025),在移动应用中实施条件 GET 请求可将重复访问的平均加载时间减少 40–60%,并将更新不频繁页面的流量消耗降低 70–90%。在慢速连接(3G、Edge)上效果尤其明显,每个字节都至关重要。

条件 GET 请求的工作原理

该过程包含三个步骤。第一步 — 客户端发送普通 GET 请求,服务器返回资源及缓存头(ETag、Last-Modified)。第二步 — 客户端将资源及其验证器保存在本地。第三步 — 重复请求时,客户端发送带有 If-None-Match(用于 ETag)和/或 If-Modified-Since(用于 Last-Modified)的 GET 请求。服务器检查验证器,如果资源未更改则响应 304,否则返回 200 及新数据。

当两个头都存在时,服务器使用 ETag 优先于 Last-Modified。这是因为 ETag 提供更精确的验证 — 内容哈希随任何更改而变化,而 Last-Modified 的分辨率为一秒。如果 ETag 匹配,服务器立即返回 304,而不检查 Last-Modified。

请求序列中 Conditional GET 完整周期的示例:

kotlin
// 第 1 步:首次请求 — 获取数据和 ETag
GET /api/profile
Response: 200 OK
ETag: "33a64df551425fcc55e"
Body: { "name": "Alice" }

// 第 2 步:重复请求 — 使用 If-None-Match
GET /api/profile
If-None-Match: "33a64df551425fcc55e"
Response: 304 Not Modified
// 响应体不存在 — 使用本地副本

在第二个请求中,服务器将 If-None-Match 中的 ETag 与资源的当前哈希进行比较。如果匹配,则返回不带体的 304 — 客户端继续使用缓存数据。这就是 Conditional GET 的本质:最小流量,最大数据新鲜度。

Conditional GET 与普通 GET

普通 GET 请求始终返回完整的 200 OK 响应及响应体。即使资源未更改,服务器也会重新传输所有数据。这对于小资源或罕见请求可以接受,但对于每次启动发出数百个请求的移动应用,这种方法会导致流量和电池过度消耗。

Conditional GET 以头的形式增加额外开销(通常每个请求 50–200 字节),但在 304 响应中节省千字节和兆字节。资源越大,条件请求越有利。对于 10 KB 及以上的图像、数据列表和 JSON 文档,Conditional GET 从第一次重复请求即可收回成本。

两种方法的对比:

参数普通 GETConditional GET
流量(无更改)完整响应仅头(~200 字节)
延迟完全加载毫秒 (304)
服务器负载生成 + 传输仅 ETag 检查
实现复杂度最低需要存储 ETag
大数据效率

Kotlin 实现示例

我们来看一个完整的实现 — 使用 OkHttp 和 Room 存储 ETag 在 Kotlin 中实现 Conditional GET。任务列表应用从服务器加载任务,并使用条件请求最小化流量。ETag 存储在本地数据库中以在会话之间保持。

Kotlin 中带有 Conditional GET 的仓库:

kotlin
class TaskRepository(
    private val api: TaskApi,
    private val etagDao: EtagDao
) {
    suspend fun getTasks(): List<Task> {
        val savedEtag = etagDao.getEtag("tasks")

        val response = api.fetchTasks(
            ifNoneMatch = savedEtag
        )

        return when (response.code()) {
            304 -> taskDao.getAll() // 从本地缓存
            200 -> {
                response.header("ETag")?.let {
                    etagDao.saveEtag("tasks", it)
                }
                val tasks = response.body() ?: emptyList()
                taskDao.replaceAll(tasks)
                tasks
            }
            else -> throw Exception(
                "Sync failed: ${response.code()}")
        }
    }
}

TaskRepository 检查响应码:304 表示无更改,数据从 Room 的本地缓存返回。200 时保存新 ETag 并更新本地数据库中的任务。此模式是通过 REST API 同步的移动应用的标准。

Conditional GET 在移动开发中的应用

Conditional GET 广泛用于 移动应用中优化数据同步。主要场景:加载新闻信息流(Twitter、Instagram 定期使用 If-None-Match 查询 API)、更新用户资料、加载通知列表和同步任务。在每种情况下,应用都可以检查数据的有效性而无需重新加载。

对于 离线优先应用,Conditional GET 作为同步的第一阶段。应用首先为自上次同步以来所有本地修改过的资源发送条件 GET 请求。304 的资源不需要加载。之后,应用为本地更改发送 PUT/POST。这种两阶段方法确保了最小的流量消耗。

结合 冲突解决,Conditional GET 允许有效检测冲突。如果客户端收到带有新数据的 200(资源已更改),但客户端有未发送的本地更改 — 则记录冲突。客户端可以应用 LWW(本地更改丢失)或启动合并策略以合并本地和远程更改。根据 Meta Engineering Blog(2025),在 Messenger 中实施 Conditional GET 将同步的平均流量消耗降低了 73%。

常见问题解答

什么是 Conditional GET 请求?

Conditional GET — 带有条件头(If-None-Match、If-Modified-Since)的 HTTP GET 请求。如果资源未更改,服务器返回 304 Not Modified;否则返回 200 及新数据。这是一种高效的缓存机制。

Conditional GET 与普通请求有何不同?

普通 GET 始终返回带有体的完整响应。Conditional GET 添加版本检查头(ETag、日期)。如果数据未更改,服务器以 304 无体响应,节省流量和加载时间。

如何使用 Conditional GET 进行缓存?

为了高效缓存,将每个服务器响应中的 ETag 和 Last-Modified 保存在本地数据库中。下次请求时,在 If-None-Match 和 If-Modified-Since 头中发送它们。收到 304 时使用本地缓存中的数据。

Conditional GET 如何帮助节省流量?

在 304 响应中,服务器不传输响应体 — 仅头部(约 200 字节)。对于 50 KB 的资源,这意味着节省 99.6% 的流量。对于每天同步 50 次的应用,每月节省可达数十兆字节。

Conditional GET 可以用于同步吗?

是的,这是增量同步的标准方法。客户端通过 Conditional GET 检查每个资源的有效性,仅加载已更改的资源并发送本地更改。这种方法用于 Twitter、Instagram、Telegram 及大多数现代 API。

总结

  • Conditional GET — 通过条件头 If-None-Match 和 If-Modified-Since 检查缓存资源有效性的 HTTP 机制。
  • 304 Not Modified — 指示资源未更改的服务器响应。不传输响应体,节省流量和加载时间。
  • ETag vs Last-Modified — ETag 更精确(内容哈希),Last-Modified 更简单(日期)。建议两者结合以获得最大效率。
  • 节省流量 — 对于未更改资源,Conditional GET 根据资源大小减少 70–95% 的传输数据量。
  • 应用 — Twitter、Instagram、Telegram 及大多数现代 REST API 中的标准同步机制。
  • 集成 — 客户端需要在本地数据库中存储 ETag,服务器需要在每个请求中生成和比较 ETag。
  • 建议 — 为移动 API 中的所有 GET 端点实现 Conditional GET。这是最便宜、对用户影响最大的优化方法。

我们将开发一款交钥匙移动应用程序

IT Sectr自2017年以来为初创企业和企业打造iOS和Android应用程序。我们将为您提供咨询并提出最佳解决方案。

讨论项目

另请阅读