You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

REST接口缓存异常:服务器返回200无Cache-Control头未返回304

解决REST接口缓存不生效(始终返回200而非304)的问题

我来帮你捋捋这个问题哈——你的代码里虽然注入了Request对象,但完全没用到它来处理HTTP缓存的校验逻辑,也没给响应加上Cache-Control头,难怪服务器每次都返回200了。

下面我拆解一下JAX-RS里实现HTTP缓存的核心步骤,再把你的代码改到位:

核心逻辑梳理

要让服务器返回304(缓存命中),你得完成这几件关键事:

  1. 从数据库拿到实体后,生成这个资源的唯一标识(ETag),或者获取它的最后修改时间(Last-Modified)
  2. 用注入的Request对象,根据ETag/Last-Modified检查客户端的缓存是否有效
  3. 如果缓存有效,直接返回304;如果无效,返回200并带上ETag和Cache-Control头告诉客户端怎么缓存

修改后的完整代码示例

先修正你代码里的小笔误(查询的是Product但返回User,我统一改成User了,你可以根据实际业务调整):

@GET 
@Path("/{id:[0-9][0-9]*}") 
@Produces("application/json") 
public Response findById(@PathParam("id") Long id, @Context Request request) {
    // 1. 查询数据库获取实体
    TypedQuery<User> findByIdQuery = em.createQuery(
        "SELECT DISTINCT u FROM User u WHERE u.id = :entityId ORDER BY u.id", 
        User.class
    );
    findByIdQuery.setParameter("entityId", id);
    User user = findByIdQuery.getSingleResult(); // 实际业务中建议加空值判断,避免查不到数据报错

    // 2. 生成ETag:用实体ID+最后修改时间生成唯一标识(确保资源变化时ETag同步更新)
    // 前提是你的User实体有lastModified字段,没有的话可以用实体哈希值替代
    String eTag = "\"" + user.getId() + "-" + user.getLastModified().getTime() + "\"";

    // 3. 检查客户端缓存是否有效
    Response.ResponseBuilder builder = request.evaluatePreconditions(eTag);
    
    if (builder != null) {
        // 缓存有效,直接返回304响应
        return builder.build();
    }

    // 4. 缓存无效,构建带缓存规则的200响应
    CacheControl cacheControl = new CacheControl();
    cacheControl.setMaxAge(3600); // 设置缓存有效期1小时
    cacheControl.setPrivate(true); // 如果是用户专属资源,设为私有缓存,避免代理服务器缓存

    // 构建响应,带上ETag和Cache-Control头
    return Response.ok(user)
                   .tag(eTag)
                   .cacheControl(cacheControl)
                   .build();
}

关键注意点

  • ETag的生成:一定要保证资源内容变化时ETag也跟着变,不然会导致客户端一直用旧缓存。如果你的实体没有lastModified字段,也可以用实体的哈希值(比如Objects.hash(user)转成字符串)。
  • 客户端请求头:客户端需要在第二次请求时自动发送If-None-Match头(值就是服务器第一次返回的ETag),服务器才能判断是否返回304,大部分浏览器和HTTP客户端都会自动处理这个逻辑。
  • Cache-Control头:这个头是告诉客户端怎么缓存资源,比如max-age是缓存有效期,private表示只能客户端自己缓存,不能被代理服务器缓存;如果是公共资源可以设public。

这样修改后,当客户端第二次请求同一个资源且缓存没过期时,服务器就会返回304状态码,同时响应里也会带上Cache-Control头啦。

内容的提问来源于stack exchange,提问作者Indigo_heart

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.21 03:35:41