【问题标题】:Custom Converter with Spring Boot Webflux is not called不调用带有 Spring Boot Webflux 的自定义转换器
【发布时间】:2020-08-20 20:14:06
【问题描述】:

在 Java 14 的 2.3.3 版本中使用 spring-boot-starter-webflux,我正在尝试注册一个自定义转换器以将字符串输入转换为枚举。我遇到的问题是,在执行时,底层的 Jackson 库会尝试在不考虑我注册的自定义转换器的情况下转换字符串。

这是我使用的控制器:

@RestController
public class MyController {

    private final MyService myService;


    @Autowired
    public MyController(MyService myService) {
        this.myService = myService;
    }

    @PostMapping(value = "/subscriptions")
    public CreateSubscriptionOutput createSubscription(@RequestBody @Valid CreateSubscriptionInput input) throws Exception {
        return myService.createSubscription(input);
    }

}

输入定义如下:

public class CreateSubscriptionInput {

    private EventType eventType;

    public CreateSubscriptionInput() {
        this.eventType = EventType.A;
    }

    public CreateSubscriptionInput(EventType type) {
        this.eventType = type;
    }

    public EventType getEventType() {
        return this.eventType;
    }

}

public enum EventType implements SafeEnum {

    A(0L, "a"),
    B(1L, "b"),
   
    private final long id;
    private final String name;

    public static EventType from(long id) {
        return (EventType) Enums.from(EventType.class, id);
    }

    public static EventType from(String name) {
        return (EventType) Enums.from(EventType.class, name);
    }

    private EventType(long id, String name) {
        this.id = id;
        this.name = name;
    }

    public long getId() {
        return this.id;
    }

    public String getName() {
        return this.name;
    }

}

public interface SafeEnum {

    long getId();

    String getName();

}

public final class Enums {

    public static <E extends Enum<E> & SafeEnum> E from(Class<E> clazz, long id) {
        for (E e : EnumSet.allOf(clazz)) {
            if (e.getId() == id) {
                return e;
            }
        }

        throw new IllegalArgumentException("Unknown " + clazz.getSimpleName() + " id: " + id);
    }

    public static <E extends Enum<E> & SafeEnum> E from(Class<E> clazz, String name) {
        if (name == null) {
            return null;
        }

        for (E e : EnumSet.allOf(clazz)) {
            if (e.getName().equals(name)) {
                return e;
            }
        }

        throw new IllegalArgumentException("Unknown " + clazz.getSimpleName() + " name: " + name);
    }

}

自定义转换器的定义和注册如下:

@Configuration
@EnableWebFlux
public class ApplicationConfiguration implements WebFluxConfigurer {

    @Override
    public void addFormatters(FormatterRegistry registry) {
        registry.addConverter(new Converter<String, EventType>() {
            @Override
            public EventType convert(String source) {
                return EventType.from(source);
            }
        });
    }

目的是将“a”转换为EventType.A

当我如下调用 REST 资源时:

curl --location --request POST 'http://localhost:8081/subscriptions' \
     --header 'Content-Type: application/json' \
     --data-raw '{
         "eventType": "a",
     }'

我收到 400 错误,并出现以下内部错误:

JSON 解码错误:无法反序列化类型的值 my.app.EventType 来自字符串“a”:不是接受的值之一 枚举类:[A, B]

这让我认为转换器永远不会被调用。在调试中运行代码证实了这个假设。在 DEBUG 中设置根记录器似乎不会打印有关已注册转换器的信息。

我注册转换器的方式有问题吗?缺少什么?

【问题讨论】:

    标签: java spring spring-boot converters


    【解决方案1】:

    尚不清楚为什么在反序列化带有 @RequestBody 注释的 DTO 中的值时不使用已注册的转换器。官方文档似乎没有描述这种情况。希望解决方法是使用@JsonCreator 注释:

    public enum EventType implements SafeEnum {
    
        A(0L, "a"),
        B(1L, "b"),
       
        private final long id;
        private final String name;
    
        public static EventType from(long id) {
            return (EventType) Enums.from(EventType.class, id);
        }
    
        @JsonCreator
        public static EventType from(String name) {
            return (EventType) Enums.from(EventType.class, name);
        }
    
        private EventType(long id, String name) {
            this.id = id;
            this.name = name;
        }
    
        public long getId() {
            return this.id;
        }
    
        public String getName() {
            return this.name;
        }
    
    }
    

    这不太灵活,需要对所有枚举重复,但至少它具有按预期工作的优点。

    【讨论】:

      【解决方案2】:

      只需使用 Converter 作为 @Component 而不使用 WebFluxConfigurer:

      @Component
      public class CustomConverter implements Converter<String, EventType> {
          @Override
          public EventType convert(String source) {
              return EventType.from(source);
          }
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-05-25
        • 2020-01-24
        • 2020-10-24
        • 2015-08-25
        • 1970-01-01
        • 2016-07-30
        相关资源
        最近更新 更多