使用@ControllerAdvice全局异常处理器仍输出时间戳问题
问题描述
- 已配置禁用
WRITE_DATES_AS_TIMESTAMPS的ObjectMapper,但全局异常处理器(@ControllerAdvice)返回的响应中,timestamp字段仍输出时间戳格式 - 但如果在Controller内部使用
@ExceptionHandler处理异常,返回的时间格式是正常的字符串格式
相关代码
全局异常处理器
@ControllerAdvice public class GlobalExceptionHandlerAdvice extends ResponseEntityExceptionHandler { @Override public ResponseEntity<Object> handleServletRequestBindingException( ServletRequestBindingException servletRequestBindingException, HttpHeaders headers, HttpStatus status, WebRequest request) { return ErrorResponse.builder(e, status, errorMessage) .property("code", String.valueOf(applicationError.getCode())) .property("timestamp", OffsetDateTime.now()) .build(); } }
MockMvc配置代码
MockMvc mock = MockMvcBuilders.standaloneSetup(myController) .setMessageConverters(new MappingJackson2HttpMessageConverter(createObjectMapper())) .setHandlerExceptionResolvers(createExceptionResolver()) .build(); private ExceptionHandlerExceptionResolver createExceptionResolver() { ExceptionHandlerExceptionResolver exceptionResolver = new ExceptionHandlerExceptionResolver() { @Override protected ServletInvocableHandlerMethod getExceptionHandlerMethod(HandlerMethod handlerMethod, Exception exception) { Method method = new ExceptionHandlerMethodResolver(GlobalExceptionHandlerAdvice.class).resolveMethod(exception); return new ServletInvocableHandlerMethod(new GlobalExceptionHandlerAdvice(), method); } }; exceptionResolver.getMessageConverters().add(new MappingJackson2HttpMessageConverter()); exceptionResolver.afterPropertiesSet(); return exceptionResolver; } public ObjectMapper createObjectMapper() { ObjectMapper objectMapper = new ObjectMapper(); objectMapper.registerModule(new JavaTimeModule()); objectMapper.setPropertyNamingStrategy(PropertyNamingStrategy.LOWER_CAMEL_CASE); objectMapper.enable(SerializationFeature.INDENT_OUTPUT); objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); return objectMapper; }
问题原因
你在createExceptionResolver方法中,给异常解析器添加的是默认的MappingJackson2HttpMessageConverter(未传入自定义ObjectMapper),而非你配置好的、禁用了时间戳序列化的转换器。全局异常处理器的响应通过异常解析器的转换器序列化,而Controller内的异常处理器用的是setMessageConverters设置的自定义转换器,因此两者表现不一致。
解决方案
修改createExceptionResolver方法,使用自定义ObjectMapper创建转换器,替换默认添加逻辑:
private ExceptionHandlerExceptionResolver createExceptionResolver() { ExceptionHandlerExceptionResolver exceptionResolver = new ExceptionHandlerExceptionResolver() { @Override protected ServletInvocableHandlerMethod getExceptionHandlerMethod(HandlerMethod handlerMethod, Exception exception) { Method method = new ExceptionHandlerMethodResolver(GlobalExceptionHandlerAdvice.class).resolveMethod(exception); return new ServletInvocableHandlerMethod(new GlobalExceptionHandlerAdvice(), method); } }; // 使用自定义ObjectMapper创建转换器 exceptionResolver.getMessageConverters().add(new MappingJackson2HttpMessageConverter(createObjectMapper())); exceptionResolver.afterPropertiesSet(); return exceptionResolver; }
如果担心多个转换器冲突,可先清空默认转换器再添加自定义的:
private ExceptionHandlerExceptionResolver createExceptionResolver() { ExceptionHandlerExceptionResolver exceptionResolver = new ExceptionHandlerExceptionResolver() { @Override protected ServletInvocableHandlerMethod getExceptionHandlerMethod(HandlerMethod handlerMethod, Exception exception) { Method method = new ExceptionHandlerMethodResolver(GlobalExceptionHandlerAdvice.class).resolveMethod(exception); return new ServletInvocableHandlerMethod(new GlobalExceptionHandlerAdvice(), method); } }; // 清空默认转换器,避免冲突 exceptionResolver.getMessageConverters().clear(); exceptionResolver.getMessageConverters().add(new MappingJackson2HttpMessageConverter(createObjectMapper())); exceptionResolver.afterPropertiesSet(); return exceptionResolver; }
修改后,全局异常处理器的响应会使用配置好的ObjectMapper,将OffsetDateTime序列化为字符串格式而非时间戳。
内容的提问来源于stack exchange,提问作者Jigar Shah
相关产品推荐
相关产品推荐

