无法在 Spring Boot 中将 ProblemHandler 设置为 ObjectMapper

Posted

技术标签:

【中文标题】无法在 Spring Boot 中将 ProblemHandler 设置为 ObjectMapper【英文标题】:Can't set ProblemHandler to ObjectMapper in Spring Boot 【发布时间】:2018-03-20 12:35:13 【问题描述】:

我尝试使用 Jackson2ObjectMapperBuilderCustomizer 将自定义问题处理程序添加到对象映射器:

@Bean
public Jackson2ObjectMapperBuilderCustomizer customizer() 
    return new Jackson2ObjectMapperBuilderCustomizer() 
        @Override
        public void customize(Jackson2ObjectMapperBuilder builder) 
            ObjectMapper m = builder.build();
            m.addHandler(
                    new DeserializationProblemHandler() 
                        @Override
                        public boolean handleUnknownProperty(DeserializationContext ctxt, JsonParser p, JsonDeserializer<?> deserializer, Object beanOrClass, String propertyName) throws IOException 
                            System.out.println("ahahahaa");
                            return super.handleUnknownProperty(ctxt, p, deserializer, beanOrClass, propertyName);
                        
                    
            );
        
    ;

但是当我自动装配 ObjectMapper bean 时,_problemHandlers 属性为空。

我还尝试使用以下方法自定义现有的 ObjectMapper:

@Autowired
public customize(ObjectMapper mapper) 
...

但结果是一样的。我不知道谁能删除这个属性。我根本不会在另一个地方初始化对象映射器的另一个构建器/工厂/等。我做错了什么?

【问题讨论】:

【参考方案1】:

无法通过Jackson2ObjectMapperBuilderJackson2ObjectMapperBuilderCustomizer 直接将DeserializationProblemHandler 添加到ObjectMapper。在构建器上调用build() 是不行的,因为生成的ObjectMapper 是方法的本地:Spring 本身稍后会调用build(),创建另一个ObjectMapper 实例。

但是,可以通过注册 Jackson 模块来做到这一点:

构建器有一个modules() 方法 模块可以通过setupModule() 访问SetupContext 实例,该实例具有addDeserializationProblemHandler() 方法

这应该可以工作

@Bean
public Jackson2ObjectMapperBuilderCustomizer customizer() 
    return new Jackson2ObjectMapperBuilderCustomizer() 
        @Override
        public void customize(Jackson2ObjectMapperBuilder builder) 
            builder.modules(new MyModule());
        
    ;


private static class MyModule extends SimpleModule 
    @Override
    public void setupModule(SetupContext context) 
        // Required, as documented in the Javadoc of SimpleModule
        super.setupModule(context);
        context.addDeserializationProblemHandler(new MyDeserializationProblemHandler());
     


private static class MyDeserializationProblemHandler extends DeserializationProblemHandler 
    @Override
    public boolean handleUnknownProperty(DeserializationContext ctxt,
                                         JsonParser p,
                                         JsonDeserializer<?> deserializer,
                                         Object beanOrClass,
                                         String propertyName)
            throws IOException 
        System.out.println("ahahahaa");
        return super.handleUnknownProperty(ctxt, p, deserializer, beanOrClass, propertyName);
    

【讨论】:

嗨,我使用了你的代码。但是当我的请求包含无效的 JSON 字段时,控制不会进入handleUnknownProperty。我在这里错过了什么吗? context.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false) 也许?这取决于它在做什么。【参考方案2】:

我是 Spring Boot 的新手,因此很难理解如何使用它。但经过一番研究,我设法弄清楚了。

我必须创建一个名为 src.main.java.com.applicationname.config.JacksonUnknownPropertyConfig.java 的类,其内容:

package com.applicationname.config;

import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.deser.DeserializationProblemHandler;
import com.fasterxml.jackson.databind.module.SimpleModule;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.http.HttpStatus;
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
import org.springframework.web.server.ResponseStatusException;

import java.io.IOException;

@SpringBootConfiguration
public class JacksonUnknownPropertyConfig 
  private static final Logger logger = LoggerFactory.getLogger(JacksonUnknownPropertyConfig.class);

  @Bean
  public Jackson2ObjectMapperBuilderCustomizer customizer() 
    return new Jackson2ObjectMapperBuilderCustomizer() 
      @Override
      public void customize(Jackson2ObjectMapperBuilder builder) 
        builder.modules(new MyModule());
      
    ;
  

  private static class MyModule extends SimpleModule 
    @Override
    public void setupModule(SetupContext context) 
      // Required, as documented in the Javadoc of SimpleModule
      super.setupModule(context);
      context.addDeserializationProblemHandler(new MyDeserializationProblemHandler());
    
  

  private static class MyDeserializationProblemHandler extends DeserializationProblemHandler 
    @Override
    public boolean handleUnknownProperty(
        DeserializationContext ctxt,
        JsonParser p,
        JsonDeserializer<?> deserializer,
        Object beanOrClass,
        String propertyName)
        throws IOException 

      System.out.println("ahahahaa");

      final String missing = String.format("Unknown request property '%s'", propertyName);
      logger.warn(missing);

      if (ctxt.isEnabled(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)) 
        throw new ResponseStatusException(HttpStatus.BAD_REQUEST, missing);
      
      return super.handleUnknownProperty(ctxt, p, deserializer, beanOrClass, propertyName);
    
  

我还必须将 FAIL_ON_UNKNOWN_PROPERTIES 添加到文件 src.main.resources.application.properties

spring.jackson.deserialization.FAIL_ON_UNKNOWN_PROPERTIES=true

在这之后,看起来 Sprint Boot 会自动识别我创建的新类并正确加载它。

2020-07-20 23:41:22.934  INFO 16684 --- [         task-1] o.h.e.t.j.p.i.JtaPlatformInitiator       : HHH000490: Using JtaPlatform implementation: [org.hibernate.engine.transaction.jta.platform.internal.NoJtaPlatform]
2020-07-20 23:41:22.944 TRACE 16684 --- [         task-1] o.h.type.spi.TypeConfiguration$Scope     : Handling #sessionFactoryCreated from [org.hibernate.internal.SessionFactoryImpl@3edd0926] for TypeConfiguration
2020-07-20 23:41:22.946  INFO 16684 --- [         task-1] j.LocalContainerEntityManagerFactoryBean : Initialized JPA EntityManagerFactory for persistence unit 'default'
2020-07-20 23:41:23.209  INFO 16684 --- [           main] DeferredRepositoryInitializationListener : Spring Data repositories initialized!
2020-07-20 23:41:23.222  INFO 16684 --- [           main] c.a.p.AppointmentPublishingApplication   : Started AppointmentPublishingApplication in 6.445 seconds (JVM running for 7.615)
2020-07-20 23:41:26.229  INFO 16684 --- [nio-8081-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring DispatcherServlet 'dispatcherServlet'
2020-07-20 23:41:26.229  INFO 16684 --- [nio-8081-exec-1] o.s.web.servlet.DispatcherServlet        : Initializing Servlet 'dispatcherServlet'
2020-07-20 23:41:26.236  INFO 16684 --- [nio-8081-exec-1] o.s.web.servlet.DispatcherServlet        : Completed initialization in 7 ms
ahahahaa

相关话题:

    Spring Boot, Spring MVC JSON RequestBody: Unknown property ignored How to customise Jackson in Spring Boot 1.4 jackson Unrecognized field Ignoring new fields on JSON objects using Jackson Configure FAIL_ON_UNKNOWN_PROPERTIES for each RequestMapping differently in the Controller Jackson deserializer priority? Spring Data Elastic - Java.Time.Instant class Jackson deserliization not working Enable Jackson Deserialization of Empty Objects to Null Spring RestController : reject request with unknown fields How do you globally set Jackson to ignore unknown properties within Spring? https://www.baeldung.com/spring-bean https://docs.spring.io/spring-boot/docs/current/reference/html/howto.html#howto-spring-mvc

【讨论】:

以上是关于无法在 Spring Boot 中将 ProblemHandler 设置为 ObjectMapper的主要内容,如果未能解决你的问题,请参考以下文章

无法使用 Hibernate 在 Spring Boot 中将 MongoDB 设置为自动递增

Spring Boot 应用程序无法在 Docker 中将事件发布到 Kafka

如何在 Spring Boot 中将属性注入测试类?

如何在 Wildfly 中将外部属性文件加载到 Spring Boot

如何在弹性beantalk中将PostgreSQL RDS连接到spring boot?

如何在 Spring Boot 中将 mongo db update 运算符应用为 $inc