问题
写了一个切面来处理被指定自定义注解标注的方法:
@Slf4j
@Aspect
@Component
@RequiredArgsConstructor
public class MyAnnoAspect {
private final HttpServletRequest request;
@Around("@annotation(myAnno)")
public Object handleMyAnno(ProceedingJoinPoint joinPoint, MyAnno myAnno) throws Throwable {
String body = ServletUtil.getBody(request);
// ...
// 继续执行原方法
return joinPoint.proceed(args);
}
}
其中使用ServletUtil.getBody(request)
来读取请求体。
测试发现,当 Controller 方法使用@RequestBody
标注方法参数时。切面中获取请求体会报错UT010004: Cannot call getReader(), getInputStream() already called
。因为request.getReader()
已经在@RequestBody
的处理逻辑中调用过了,不能重复调用。
解决
ContentCachingRequestWrapper
是 Spring 提供的一个请求包装器,可以缓存请求体。
可以在 Servlet 过滤器中使用ContentCachingRequestWrapper
包装原有请求,使请求体可以被多次读取。
先新建过滤器使用ContentCachingRequestWrapper
来包装请求:
public class CustomRequestFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(@NotNull HttpServletRequest request, @NotNull HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
ContentCachingRequestWrapper wrappedRequest = new ContentCachingRequestWrapper(request);
filterChain.doFilter(wrappedRequest, response);
}
}
然后注册过滤器:
@Configuration
public class FilterConfig {
@Bean
public FilterRegistrationBean<CustomRequestFilter> customFilter() {
FilterRegistrationBean<CustomRequestFilter> registrationBean = new FilterRegistrationBean<>();
registrationBean.setFilter(new CustomRequestFilter());
registrationBean.addUrlPatterns("/*");
return registrationBean;
}
}
其中OncePerRequestFilter
本身实现了javax.servlet.Filter
接口,所以实际上还是基于 Servlet 过滤器机制。
FilterRegistrationBean
是 Spring Boot 提供的一个组件,允许开发者以编程方式注册和配置过滤器。