首页 > 解决方案 > 通过 Jackson2ObjectMapperBuilder 设置自定义日期格式导致请求处理在异常后继续

问题描述

我有一个 MockMvc 测试,用于测试控制器的 JSON 有效负载是否经过验证,并且为org.springframework.data.mapping.PropertyReferenceException和呈现 HTTP 400(错误请求) org.springframework.http.converter.HttpMessageConversionException

相应的异常处理程序实现如下。

@ControllerAdvice
public class LocalExceptionHandler {

    @ExceptionHandler(PropertyReferenceException.class)
    public ResponseEntity<Object> handlePropertyReferenceException(PropertyReferenceException ex) {
        return new ResponseEntity<>(ex.getMessage(), HttpStatus.BAD_REQUEST);
    }

    @ExceptionHandler(HttpMessageConversionException.class)
    public ResponseEntity<Object> handleHttpMessageConversionException(HttpMessageConversionException ex) {
        return new ResponseEntity<>(ex.getMessage(), HttpStatus.BAD_REQUEST);
    }
}

我正在使用 Spock 规范来实现测试。设置如下。

MockMvc mvc

public JsonSerializer[] buildJsonSerializers() {
    return new JsonSerializer[]{new LocalDateSerializer(DateTimeFormatter.ofPattern(DATE_FORMAT)),
        new LocalDateTimeSerializer(DateTimeFormatter.ofPattern(DATE_TIME_FORMAT))};
}
Jackson2ObjectMapperBuilderCustomizer jsonCustomizer() {
    return builder -> {
        builder.simpleDateFormat(DATE_TIME_FORMAT);
        builder.serializers(buildJsonSerializers());
    };
}    
protected ObjectMapper buildObjectMapper() {
    def objectMapperBuilder = new Jackson2ObjectMapperBuilder()
    jsonCustomizer().customize(objectMapperBuilder)
    objectMapperBuilder.modules(new MoneyModule()
        .withMonetaryAmount(Money::of)
        .withAmountFieldName("number")
        .withFormattedFieldName("pretty"))
    objectMapperBuilder.build()
}

def setup() {
    ObjectMapper mapper = buildObjectMapper()

    def mockMvcBuilder = MockMvcBuilders
        .standaloneSetup(controller)
        .setControllerAdvice(LocalExceptionHandler.class)
        .setMessageConverters([new MappingJackson2HttpMessageConverter(mapper)]
            .toArray(new HttpMessageConverter[1]))
        .setCustomArgumentResolvers(new PageableHandlerMethodArgumentResolver())

        mvc = mockMvcBuilder.build()
}

所以上面的设置只是通过定制器设置日期格式,然后使用Jackson2ObjectMapperBuilder.

该设置的问题在于构建器导致对象映射器配置导致奇怪的 MockMvc 行为。

当向控制器发布错误请求时,会引发适当的异常并由上述异常处理程序之一处理,但请求处理不会停止并调用控制器方法。当运行生产代码(作为 Spring Boot 应用程序)时,错误处理很好,导致 HTTP 400。

只需删除构建器并仅模仿测试所需的配置(这是正确的日期时间格式),测试就会按预期工作,并且在异常处理后停止请求处理。

所以基本上我没有使用构建器

def mapper = new ObjectMapper()
mapper.registerModule(new MoneyModule()
        .withMonetaryAmount(Money::of)
        .withAmountFieldName("number")
        .withFormattedFieldName("pretty"))

SimpleModule serializerModule = new SimpleModule()
        Arrays.asList(buildJsonSerializers())
            .forEach({ s -> serializerModule.addSerializer(s.handledType(), s) })
mapper.registerModule(serializerModule)

所以看起来构建器正在添加一些 MockMvc 并没有真正正确处理的配置。将不胜感激有关解决此问题的提示。

标签: spring-mvcmockmvc

解决方案


推荐阅读