无HTTP请求体时@ControllerAdvice不触发handleEmptyBody的问题咨询
解决思路与问题解析
首先明确几个关键问题,再给出针对性的解决方案:
关于handleEmptyBody()的调用时机
Spring 只会在满足以下两个条件时调用handleEmptyBody():
- 你的
supports()方法返回true; - 控制器方法带有
@RequestBody注解,但请求体为空(包括Content-Length为0、请求体是空白字符串,或者分块传输但无实际内容的情况)。
如果请求本身没有请求体(比如GET请求)且控制器方法没有@RequestBody参数,那么整个RequestBodyAdvice的方法都不会被触发,自然也不会走到handleEmptyBody()。
实现「仅在有请求体时触发Advice」的方案
你的需求是让这个@ControllerAdvice在任何无请求体的场景下完全被忽略(包括有@RequestBody但空体的情况),核心是在supports()方法中添加判断逻辑,仅当请求存在非空请求体时返回true,否则返回false。
具体步骤与代码修改
- 首先过滤非
@RequestBody参数:因为RequestBodyAdvice本来就是为@RequestBody参数设计的,先明确只对这类参数生效; - 判断请求是否带有非空请求体:通过
HttpServletRequest获取请求信息,结合Content-Length、请求方法或缓存请求流来判断。
修改后的supports()方法示例:
import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; import org.springframework.util.StringUtils; // 其余导入保持不变 @Override public boolean supports( MethodParameter methodParameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType ) { // 第一步:仅对带有@RequestBody注解的参数生效 if (!methodParameter.hasParameterAnnotation(RequestBody.class)) { LOGGER.info("当前方法无@RequestBody参数,忽略Advice"); return false; } // 第二步:获取当前请求对象 ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); if (attributes == null) { LOGGER.info("无法获取请求上下文,忽略Advice"); return false; } HttpServletRequest request = attributes.getRequest(); // 判断是否存在非空请求体 boolean hasNonEmptyBody = false; // 先检查Content-Length(适用于非分块传输的场景) if (request.getContentLengthLong() > 0) { hasNonEmptyBody = true; } else { // 处理分块传输(Transfer-Encoding: chunked)的情况,缓存请求流并检查内容 ContentCachingRequestWrapper wrappedRequest = new ContentCachingRequestWrapper(request); byte[] bodyBytes = wrappedRequest.getContentAsByteArray(); hasNonEmptyBody = bodyBytes.length > 0 && !StringUtils.isBlank(new String(bodyBytes, StandardCharsets.UTF_8).trim()); } if (hasNonEmptyBody) { LOGGER.info("请求带有非空请求体,触发Advice"); return true; } else { LOGGER.info("请求无有效请求体,忽略Advice"); return false; } }
关键细节说明
- 对于
GET这类通常不带请求体的方法,即使控制器方法误加了@RequestBody,上面的逻辑也会因为检测不到非空请求体而返回false,避免Advice被触发; - 使用
ContentCachingRequestWrapper是为了安全地读取请求流内容(不会消耗流,后续HttpMessageConverter仍能正常读取); - 额外判断字符串是否为空白,是为了排除请求体全是空格、换行这类无意义内容的情况。
最终效果
修改后,只有当请求带有非空的请求体,且控制器方法参数带有@RequestBody时,你的RequestBodyAdvice的beforeBodyRead()、afterBodyRead()才会被触发;无请求体或请求体为空时,整个Advice完全不会被调用,包括handleEmptyBody()。
内容的提问来源于stack exchange,提问作者BetaRide
相关产品推荐
相关产品推荐

