未调用具有 Spring 引导 Webflux 的自定义转换器
Custom Converter with Spring Boot Webflux is not called
在版本 2.3.3 中使用 spring-boot-starter-webflux
和 Java 14,我正在尝试注册自定义转换器以将字符串输入转换为枚举。我遇到的问题是,在执行时,底层 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 decoding error: Cannot deserialize value of type
my.app.EventType
from String "a": not one of the values accepted for
Enum class: [A, B]
这让我觉得转换器从未被调用过。 运行 调试中的代码证实了这个假设。在 DEBUG 中设置根记录器似乎不会打印有关已注册转换器的信息。
我注册转换器的方式有问题吗?缺少什么?
不清楚为什么在反序列化带有 @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;
}
}
这不太灵活,需要对所有枚举重复,但至少它具有按预期工作的优点。
不使用 WebFluxConfigurer 只使用 Converter 作为 @Component:
@Component
public class CustomConverter implements Converter<String, EventType> {
@Override
public EventType convert(String source) {
return EventType.from(source);
}
}
在版本 2.3.3 中使用 spring-boot-starter-webflux
和 Java 14,我正在尝试注册自定义转换器以将字符串输入转换为枚举。我遇到的问题是,在执行时,底层 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 decoding error: Cannot deserialize value of type
my.app.EventType
from String "a": not one of the values accepted for Enum class: [A, B]
这让我觉得转换器从未被调用过。 运行 调试中的代码证实了这个假设。在 DEBUG 中设置根记录器似乎不会打印有关已注册转换器的信息。
我注册转换器的方式有问题吗?缺少什么?
不清楚为什么在反序列化带有 @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;
}
}
这不太灵活,需要对所有枚举重复,但至少它具有按预期工作的优点。
不使用 WebFluxConfigurer 只使用 Converter 作为 @Component:
@Component
public class CustomConverter implements Converter<String, EventType> {
@Override
public EventType convert(String source) {
return EventType.from(source);
}
}