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

如何通过注解为指定Controller方法缓存请求InputStream且不影响同路由端点?

解决方案:基于注解的请求负载缓存实现

1. 定义自定义缓存注解

先创建一个标记型注解,用来标识需要缓存请求体的控制器方法:

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface CacheRequestBody {
}

2. 实现请求流缓存的请求包装类

自定义HttpServletRequestWrapper子类,将输入流缓存到字节数组中,实现重复读取:

public class CachedHttpServletRequest extends HttpServletRequestWrapper {
    private final byte[] cachedBody;

    public CachedHttpServletRequest(HttpServletRequest request) throws IOException {
        super(request);
        this.cachedBody = StreamUtils.copyToByteArray(request.getInputStream());
    }

    @Override
    public ServletInputStream getInputStream() throws IOException {
        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(this.cachedBody);
        return new ServletInputStream() {
            @Override
            public int read() throws IOException {
                return byteArrayInputStream.read();
            }

            @Override
            public boolean isFinished() {
                return byteArrayInputStream.available() == 0;
            }

            @Override
            public boolean isReady() {
                return true;
            }

            @Override
            public void setReadListener(ReadListener readListener) {}
        };
    }

    @Override
    public BufferedReader getReader() throws IOException {
        return new BufferedReader(new InputStreamReader(this.getInputStream()));
    }

    public byte[] getCachedBody() {
        return this.cachedBody;
    }
}

3. 实现拦截器识别注解并缓存请求

在拦截器中判断当前请求对应的控制器方法是否带有目标注解,若有则替换请求为缓存包装类:

public class RequestBodyCacheInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        if (handler instanceof HandlerMethod) {
            HandlerMethod handlerMethod = (HandlerMethod) handler;
            CacheRequestBody annotation = handlerMethod.getMethodAnnotation(CacheRequestBody.class);
            if (annotation != null) {
                CachedHttpServletRequest cachedRequest = new CachedHttpServletRequest(request);
                RequestContextHolder.setRequestAttributes(new ServletRequestAttributes(cachedRequest));
            }
        }
        return true;
    }
}

4. 注册拦截器

通过配置类将拦截器加入Spring MVC拦截链:

@Configuration
public class WebConfig implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new RequestBodyCacheInterceptor())
                .addPathPatterns("/**");
    }
}

5. 控制器方法中使用注解

在需要缓存请求体的方法上添加注解,同路由的其他方法不加则不会触发缓存:

@RestController
@RequestMapping("/api")
public class DemoController {

    @PostMapping("/data")
    @CacheRequestBody
    public ResponseEntity<String> cachedEndpoint(@RequestBody String requestBody) {
        // 可重复读取缓存的请求体
        return ResponseEntity.ok("Cached: " + requestBody);
    }

    @PostMapping("/data")
    public ResponseEntity<String> normalEndpoint(@RequestBody String requestBody) {
        // 不触发缓存,保持原有逻辑
        return ResponseEntity.ok("Normal: " + requestBody);
    }
}

核心说明

  • 拦截器可以通过HandlerMethod直接获取控制器方法的注解,不存在无法识别的问题,之前的误区是未正确将包装后的请求替换到上下文。
  • 通过HttpServletRequestWrapper包装请求,既实现了输入流缓存,又不影响原有请求的读取逻辑。
  • 同路由下的不同方法可通过注解精准区分,完全不影响未标注注解的端点。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.19 01:15:03