Spring Boot集成Interceptor时@ControllerAdvice异常处理Postman返回空
问题场景
项目集成Spring MVC的Interceptor拦截器时,全局异常处理的日志仅能在终端控制台正常输出,预期需要将异常信息同步返回给REST API调用端,但实际通过Postman调用对应接口时响应体为空,无任何异常返回内容。
相关代码
拦截器实现代码
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.ModelAndView; public class WebInterceptor implements HandlerInterceptor { private Logger logger = LoggerFactory.getLogger(WebInterceptor.class); @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { logger.error("WebInterceptor preHandle is now logged"); return true; } @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { logger.error("WebInterceptor posthandle is now logged"); HandlerInterceptor.super.postHandle(request, response, handler, modelAndView); } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { logger.error("WebInterceptor afterCompletion is now logged"); HandlerInterceptor.super.afterCompletion(request, response, handler, ex); } }
拦截器注册配置
import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; @Configuration public class AppConfig implements WebMvcConfigurer{ @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new WebInterceptor()).addPathPatterns("/cart/**").order(1); registry.addInterceptor(new WebInterceptor()).addPathPatterns("/product/**").order(2); } }
全局异常处理配置
import com.kongapigateway.KongAPIgateway.ModelException.DATE_FORMAT_ERROR; import com.kongapigateway.KongAPIgateway.ModelException.ProductExecption; import com.kongapigateway.KongAPIgateway.ModelException.ProductIDnotFound; import com.kongapigateway.KongAPIgateway.ModelException.ProductValueNotNull; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.bind.annotation.RestControllerAdvice; @RestControllerAdvice public class WEbAspectExceptionConfig { private Logger logger = LoggerFactory.getLogger(WEbAspectExceptionConfig.class); @ExceptionHandler(ProductValueNotNull.class) @ResponseStatus public void handle(ProductValueNotNull e) { logger.error(e.getMessage()); } @ExceptionHandler(DATE_FORMAT_ERROR.class) @ResponseStatus public void handle2(DATE_FORMAT_ERROR e) { logger.error(e.getMessage()); } @ExceptionHandler(ProductExecption.class) @ResponseStatus public void handle2(ProductExecption e) { logger.error(e.getMessage()); } @ExceptionHandler(ProductIDnotFound.class) @ResponseStatus public void handle2(ProductIDnotFound e) { logger.error(e.getMessage()); } }
现场现象
终端日志显示异常已经被全局异常处理器捕获,捕获到的异常提示为Please input all field,日志内容如下:
kongapigateway | 2022-06-07 11:21:33.149 INFO 1 --- [nio-8095-exec-1] o.s.web.servlet.DispatcherServlet : Completed initialization in 1 ms kongapigateway | 2022-06-07 11:21:33.161 ERROR 1 --- [nio-8095-exec-1] c.k.K.Interceptor.WebInterceptor : WebInterceptor preHandle is now logged kongapigateway | 2022-06-07 11:21:33.277 ERROR 1 --- [nio-8095-exec-1] c.k.K.AOP.WEbAspectExceptionConfig : Please input all field kongapigateway | 2022-06-07 11:21:33.277 ERROR 1 --- [nio-8095-exec-1] c.k.K.Interceptor.WebInterceptor : WebInterceptor afterCompletion is now logged
通过Postman调用对应接口时,响应体完全为空,没有任何异常返回内容。
问题根因
全局异常处理类里所有@ExceptionHandler修饰的方法返回值都是void,方法内部仅做了日志打印,没有向HTTP响应体写入任何内容;同时@ResponseStatus注解没有指定对应的HTTP错误状态码,默认返回200状态码,Spring不会自动将异常信息序列化到响应中,最终调用方拿到空响应。
修复方法
- 定义统一的错误响应结构,将所有异常处理方法的返回值改为该结构,方法内构造包含错误信息、错误码的响应对象返回。由于类上标注了
@RestControllerAdvice,自带@ResponseBody序列化逻辑,返回的对象会自动转成JSON写入响应体。 - 给
@ResponseStatus注解指定匹配业务场景的HTTP状态码,比如参数校验错误对应HttpStatus.BAD_REQUEST(400),资源不存在对应HttpStatus.NOT_FOUND(404)。
修复后的异常处理代码示例:
import com.kongapigateway.KongAPIgateway.ModelException.DATE_FORMAT_ERROR; import com.kongapigateway.KongAPIgateway.ModelException.ProductExecption; import com.kongapigateway.KongAPIgateway.ModelException.ProductIDnotFound; import com.kongapigateway.KongAPIgateway.ModelException.ProductValueNotNull; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.http.HttpStatus; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.bind.annotation.RestControllerAdvice; import java.util.HashMap; import java.util.Map; @RestControllerAdvice public class WEbAspectExceptionConfig { private Logger logger = LoggerFactory.getLogger(WEbAspectExceptionConfig.class); @ExceptionHandler(ProductValueNotNull.class) @ResponseStatus(HttpStatus.BAD_REQUEST) public Map<String, Object> handle(ProductValueNotNull e) { logger.error(e.getMessage()); Map<String, Object> res = new HashMap<>(); res.put("code", 400); res.put("msg", e.getMessage()); return res; } @ExceptionHandler(DATE_FORMAT_ERROR.class) @ResponseStatus(HttpStatus.BAD_REQUEST) public Map<String, Object> handleDateFormatError(DATE_FORMAT_ERROR e) { logger.error(e.getMessage()); Map<String, Object> res = new HashMap<>(); res.put("code", 400); res.put("msg", e.getMessage()); return res; } @ExceptionHandler(ProductExecption.class) @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) public Map<String, Object> handleProductError(ProductExecption e) { logger.error(e.getMessage()); Map<String, Object> res = new HashMap<>(); res.put("code", 500); res.put("msg", e.getMessage()); return res; } @ExceptionHandler(ProductIDnotFound.class) @ResponseStatus(HttpStatus.NOT_FOUND) public Map<String, Object> handleProductNotFound(ProductIDnotFound e) { logger.error(e.getMessage()); Map<String, Object> res = new HashMap<>(); res.put("code", 404); res.put("msg", e.getMessage()); return res; } }
注:可根据项目实际规范替换为项目统一的响应实体类,不用手写Map构造返回值。
内容的提问来源于stack exchange,提问作者SpicySandwich
相关产品推荐
相关产品推荐

