无法在 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的主要内容,如果未能解决你的问题,请参考以下文章