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

Spring Java中如何为ResponseEntity添加Shallow ETag支持

Spring 更简便的Shallow ETag实现方案

你完全不需要自己手写过滤器实现Shallow ETag,Spring Web MVC本身就提供了开箱即用的组件,比你现在每个接口手动计算ETag、手动塞到ResponseEntity里的写法简单很多。

方案1:全局生效,零业务代码侵入

Spring 内置了ShallowEtagHeaderFilter,所有Shallow ETag的生成、校验逻辑都已经封装好了,你只需要把这个过滤器注册到Spring容器里就行,不用写任何核心逻辑。
Spring Boot项目直接加个配置类注册过滤器即可:

@Configuration
public class WebConfig {
    @Bean
    public FilterRegistrationBean<ShallowEtagHeaderFilter> shallowEtagFilter() {
        FilterRegistrationBean<ShallowEtagHeaderFilter> registration = new FilterRegistrationBean<>();
        registration.setFilter(new ShallowEtagHeaderFilter());
        registration.addUrlPatterns("/api/*"); // 按需配置需要生效的接口路径,不需要的静态资源路径可以直接排除
        return registration;
    }
}

这个过滤器的工作逻辑完全符合Shallow ETag规范:

  • 自动缓存响应的输出流内容,基于响应字节内容计算MD5哈希,自动生成带W/前缀的弱ETag写入响应头
  • 自动读取请求头里的If-None-Match值和生成的ETag比对,匹配的话直接返回304 Not Modified,不会把响应体传输给客户端,节省带宽
  • 所有逻辑对业务代码完全透明,你原来接口里手动写的.eTag(getETag(preloadMap))这行可以直接删掉,直接正常返回响应体即可:
return ResponseEntity.ok().body(preloadMap);

不需要在每个接口重复写ETag计算逻辑。

方案2:细粒度控制,只给指定接口加Shallow ETag

如果不想全局开启过滤器,只想给部分接口加Shallow ETag,可以用Spring自带的ResponseBodyAdvice做统一拦截处理,同样不需要每个接口手动写ETag逻辑。

  1. 先写一个自定义注解用来标记需要加Shallow ETag的接口:
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface EnableShallowEtag {
}
  1. 实现响应拦截逻辑,统一做ETag生成和校验:
@ControllerAdvice
public class ShallowEtagAdvice implements ResponseBodyAdvice<Object> {
    private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();

    @Override
    public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
        // 只给加了@EnableShallowEtag注解的接口生效
        return returnType.hasMethodAnnotation(EnableShallowEtag.class);
    }

    @Override
    public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, 
                                  Class<? extends HttpMessageConverter<?>> selectedConverterType,
                                  ServerHttpRequest request, ServerHttpResponse response) {
        try {
            // 序列化响应体计算MD5,生成标准Shallow ETag
            byte[] bodyBytes = OBJECT_MAPPER.writeValueAsBytes(body);
            String etag = "W/\"" + DigestUtils.md5DigestAsHex(bodyBytes) + "\"";
            response.getHeaders().setETag(etag);
            
            // 比对请求头,匹配直接返回304
            String requestEtag = request.getHeaders().getFirst(HttpHeaders.IF_NONE_MATCH);
            if (etag.equals(requestEtag)) {
                response.setStatusCode(HttpStatus.NOT_MODIFIED);
                return null;
            }
            return body;
        } catch (JsonProcessingException e) {
            throw new RuntimeException("Shallow ETag计算失败", e);
        }
    }
}

之后哪个接口需要Shallow ETag支持,直接在接口方法上加@EnableShallowEtag注解就行,业务代码里不需要写任何ETag相关的逻辑,正常返回响应体即可。

注意点

  • 内置ShallowEtagHeaderFilter的性能开销极低,MD5计算的耗时对大部分接口来说可以忽略,生产环境直接用完全没问题。
  • 如果你之前的getETag逻辑不是基于全响应内容计算,而是基于特定业务字段做版本标记,可以在方案2的基础上调整ETag的计算规则,比每个接口手动塞ETag的维护成本低很多。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.27 03:45:33