能,但需满足两个前提:返回可缓存响应(GET、无用户态依赖、无随机/时间敏感字段)且控制器不提前输出内容;HttpCache基于Last-Modified或ETag实现协商缓存,天然适合纯读取型RESTful接口。

Yii RESTful 接口能用 HttpCache 吗?
能,但必须满足两个硬性前提:接口返回的是「可缓存的响应」(GET 请求、无用户身份态依赖、无随机/时间敏感字段),且控制器动作不提前输出内容。Yii 的 yii\filters\HttpCache 本质是基于 HTTP 协议的协商缓存(Last-Modified 或 ETag),它不缓存数据本身,只控制客户端是否复用本地副本——所以它天然适合纯读取型 RESTful 接口。
HttpCache 的 lastModified 怎么写才稳定?
常见错误是直接用 time() 或数据库记录的 updated_at 字段,导致每次请求时间戳都变,304 永远不触发。关键在于找一个「变化频率 ≈ 接口内容真实更新频率」的稳定时间源:
- 静态资源类接口:用视图文件或模板文件的修改时间,
return filemtime('@app/views/api/v1/users.php'); - 聚合数据类接口:查主表最新更新时间,但要加条件限定范围,比如
(new \yii\db\Query())->from('user')->max('updated_at'),避免全表扫描 - 配置驱动型接口:硬编码时间戳,如
return strtotime('2026-04-01 00:00:00');,配合发布流程手动更新
注意:lastModified 回调函数返回值必须是 Unix 时间戳(整数),不是字符串或 DateTime 对象。
etagSeed 为什么比 lastModified 更常用?
因为大多数 RESTful 接口的数据来源不止一个表,或含计算逻辑(比如拼接字段、权限过滤),Last-Modified 很难准确反映「内容是否真变了」。etagSeed 允许你自定义哈希依据,更灵活:
- 简单场景:固定版本号,
return 'api-v2.1';,发版时改字符串 - 数据一致性场景:对关键字段做序列化+hash,
return md5(json_encode(['users_count' => User::find()->count(), 'config_hash' => Yii::$app->cache->get('config_etag')])); - 避免 Nginx 干扰:如果前端有 Nginx 且开了
proxy_buffering on,它可能把200响应整个缓存住,导致后续304请求根本到不了 PHP 层——这时要么关掉proxy_buffering,要么确保 Nginx 的proxy_cache_valid显式包含304
调试时 304 总不生效,先检查这三处
实际开发中最常卡在这几个地方,顺序排查效率最高:
-
YII_DEBUG = true:只要开启调试模式,HttpCache会自动禁用全部缓存逻辑,头都不发。上线前务必确认环境配置 - 响应体里有
echo、var_dump、未捕获的 Notice 级错误:哪怕只是多一个空格或换行,PHP 就会提前 flush 输出缓冲,导致Headers already sent,缓存头根本设不上 - Response 已被其他中间件/行为提前处理:比如你同时用了
ContentNegotiator或自定义 JSON 包装器,确保HttpCache在行为数组中声明位置靠前,且没被后续逻辑覆盖Cache-Control
真正起效时,浏览器开发者工具 Network 标签页能看到响应状态码是 304 Not Modified,且 Size 显示 from disk cache 或 from memory cache——这时才算缓存链路跑通了。


















