Spring如何在Controller方法外部访问@RequestBody绑定的对象
存在完全符合要求的实现方案,基于Spring MVC原生的RequestBodyAdvice即可实现,不需要修改任何原有Controller代码,也不会产生重复反序列化的开销。
实现原理
Spring MVC在处理@RequestBody参数时,会先通过消息转换器将请求流反序列化为目标对象,在将对象注入到Controller方法参数之前,会触发所有匹配的RequestBodyAdvice实现的回调。你可以在这个回调节点直接拿到已经完成反序列化的对象,执行校验或存储逻辑,完全符合你的需求。
具体实现步骤
- 自定义类实现
RequestBodyAdvice接口,添加@ControllerAdvice注解使其全局生效,也可通过注解的属性限定生效范围,避免拦截非目标接口 - 重写
supports方法,设置拦截规则,只有匹配你需要校验的目标类型的请求才会进入后续处理,降低性能损耗 - 重写
afterBodyRead方法,直接获取反序列化完成的@RequestBody对象,执行你的校验逻辑,如果后续业务组件需要使用该对象,可将其存入当前请求的HttpServletRequest属性中,同请求链路下的组件可直接读取使用
参考代码
import org.springframework.core.MethodParameter; import org.springframework.http.HttpInputMessage; import org.springframework.http.converter.HttpMessageConverter; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; import org.springframework.web.servlet.mvc.method.annotation.RequestBodyAdvice; import javax.servlet.http.HttpServletRequest; import java.io.IOException; import java.lang.reflect.Type; // 可通过basePackages、annotations等属性限定生效范围 @ControllerAdvice(basePackages = "com.your.business.controller") public class TargetTypeValidationAdvice implements RequestBodyAdvice { // 自定义Request属性存储的key,避免和其他属性冲突 private static final String VALIDATED_BODY_KEY = "VALIDATED_REQUEST_BODY"; @Override public boolean supports(MethodParameter methodParameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) { // 仅拦截你需要校验的目标类型,比如你要校验的是OrderDTO类,这里直接匹配类型即可 return targetType.getTypeName().equals(OrderDTO.class.getName()); } @Override public Object afterBodyRead(Object body, HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) { // 直接拿到已经反序列化完成的对象,无需重复反序列化 OrderDTO targetBody = (OrderDTO) body; // 执行你的校验逻辑,校验不通过可直接抛出业务异常终止请求 validateTargetBody(targetBody); // 若后续业务组件需要使用该对象,存入当前请求的属性中 HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); request.setAttribute(VALIDATED_BODY_KEY, targetBody); return body; } @Override public HttpInputMessage beforeBodyRead(HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) throws IOException { return inputMessage; } @Override public Object handleEmptyBody(Object body, HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) { return body; } private void validateTargetBody(OrderDTO body) { // 你的自定义校验逻辑实现 } }
方案说明
- 完全不侵入原有Controller代码,所有逻辑独立在Advice中实现
- 复用Spring原生的反序列化结果,没有额外的反序列化开销
- 支持灵活调整生效范围,可针对特定包、特定注解、特定类型的请求生效,不会影响其他业务接口
- 同请求链路下的其他业务组件可直接通过
HttpServletRequest.getAttribute(VALIDATED_BODY_KEY)获取已经校验完成的对象,无需重复获取或校验 - 若使用的是Spring WebFlux响应式框架,替换为响应式版本的
RequestBodyAdvice实现即可,核心逻辑一致
内容的提问来源于stack exchange,提问作者regisxp
相关产品推荐
相关产品推荐

