英文:
Making spring-boot obey enum values with generated openapi code
问题
I'm trying the "api first" approach with a spring-boot app and a very basic openapi.yaml
I defined.
I created a basic API which gets a single mandatory parameter named type
which is an array of an enum I created (sophisticatedly named SomeEnum
).
When I try to generate a request via the Swagger editor - I get this request generated:
curl -X 'GET' \
'https://localhost:8080/api/1/demo?type=best_value' \
-H 'accept: application/json';
The problem is when I try it - I get a 400 error from Spring and this error from the log (formatted for readability):
2023-04-06 15:52:05.223 WARN 16396 --- [nio-8080-exec-1] .w.s.m.s.DefaultHandlerExceptionResolver : Resolved [org.springframework.web.method.annotation.MethodArgumentTypeMismatchException: Failed to convert value of type 'java.lang.String' to required type 'java.util.List';
nested exception is org.springframework.core.convert.ConversionFailedException: Failed to convert from type [java.lang.String] to type [@javax.validation.constraints.NotNull @io.swagger.v3.oas.annotations.Parameter @javax.validation.Valid @org.springframework.web.bind.annotation.RequestParam com.ronkitay.openapidemo.model.SomeEnum] for value 'another-value';
nested exception is java.lang.IllegalArgumentException: No enum constant com.ronkitay.openapidemo.model.SomeEnum.another-value]
Sending an API request like the one below works fine:
curl http://localhost:8080/api/1/demo?type=ANOTHER_VALUE
The generated enum seems fine to me:
@Generated(value = "org.openapitools.codegen.languages.SpringCodegen", date = "2023-04-06T15:51:49.262+03:00[Asia/Jerusalem]")
public enum SomeEnum {
VALUE1("value1"),
ANOTHER_VALUE("another-value"),
BEST_VALUE("best_value"),
THEVALUE("thevalue");
private String value;
SomeEnum(String value) {
this.value = value;
}
@JsonValue
public String getValue() {
return value;
}
@Override
public String toString() {
return String.valueOf(value);
}
@JsonCreator
public static SomeEnum fromValue(String value) {
for (SomeEnum b : SomeEnum.values()) {
if (b.value.equals(value)) {
return b;
}
}
throw an IllegalArgumentException("Unexpected value '" + value + "'");
}
}
And I can make spring act as expected by adding this configuration:
@Configuration
public class Config implements WebMvcConfigurer {
@Override
public void addFormatters(FormatterRegistry registry) {
registry.addConverterFactory(new SomeEnumFormatter());
}
public static class SomeEnumFormatter implements ConverterFactory<String, SomeEnum> {
@Override
public <T extends SomeEnum> Converter<String, T> getConverter(Class<T> targetType) {
return new StringToSomeEnumConverter<>(targetType);
}
public static class StringToSomeEnumConverter<T extends SomeEnum> implements Converter<String, T> {
private Class<T> targetClass;
public StringToSomeEnumConverter(Class<T> targetClass) {
this.targetClass = targetClass;
}
@Override
public T convert(String source) {
return (T) SomeEnum.fromValue(source);
}
}
}
}
My question is - is there some hidden configuration in Spring or in the openApiGenerator which can do this for me automatically so I do not need to implement a custom converter for every enum I have?
英文:
I'm trying the "api first" approach with a spring-boot app and a very basic openapi.yaml
I defined.
I created a basic API which gets a single mandatory parameter named type
which is an array of an enum I created (sophisticatedly named SomeEnum
).
When I try to generate a request via the Swagger editor - I get this request generated:
curl -X 'GET' \
'https://localhost:8080/api/1/demo?type=best_value' \
-H 'accept: application/json'
The problem is when I try it - I get a 400 error from Spring and this error from the log (formatted for readability):
2023-04-06 15:52:05.223 WARN 16396 --- [nio-8080-exec-1] .w.s.m.s.DefaultHandlerExceptionResolver : Resolved [org.springframework.web.method.annotation.MethodArgumentTypeMismatchException: Failed to convert value of type 'java.lang.String' to required type 'java.util.List';
nested exception is org.springframework.core.convert.ConversionFailedException: Failed to convert from type [java.lang.String] to type [@javax.validation.constraints.NotNull @io.swagger.v3.oas.annotations.Parameter @javax.validation.Valid @org.springframework.web.bind.annotation.RequestParam com.ronkitay.openapidemo.model.SomeEnum] for value 'another-value';
nested exception is java.lang.IllegalArgumentException: No enum constant com.ronkitay.openapidemo.model.SomeEnum.another-value]
Sending an API request like the one below works fine:
curl http://localhost:8080/api/1/demo?type=ANOTHER_VALUE
The generated enum seems fine to me:
@Generated(value = "org.openapitools.codegen.languages.SpringCodegen", date = "2023-04-06T15:51:49.262+03:00[Asia/Jerusalem]")
public enum SomeEnum {
VALUE1("value1"),
ANOTHER_VALUE("another-value"),
BEST_VALUE("best_value"),
THEVALUE("thevalue");
private String value;
SomeEnum(String value) {
this.value = value;
}
@JsonValue
public String getValue() {
return value;
}
@Override
public String toString() {
return String.valueOf(value);
}
@JsonCreator
public static SomeEnum fromValue(String value) {
for (SomeEnum b : SomeEnum.values()) {
if (b.value.equals(value)) {
return b;
}
}
throw new IllegalArgumentException("Unexpected value '" + value + "'");
}
}
And I can make spring act as expected by adding this configuration:
@Configuration
public class Config implements WebMvcConfigurer {
@Override
public void addFormatters(FormatterRegistry registry) {
registry.addConverterFactory(new SomeEnumFormatter());
}
public static class SomeEnumFormatter implements ConverterFactory<String,SomeEnum> {
@Override
public <T extends SomeEnum> Converter<String, T> getConverter(Class<T> targetType) {
return new StringToSomeEnumConverter<>(targetType);
}
public static class StringToSomeEnumConverter<T extends SomeEnum> implements Converter<String, T> {
private Class<T> targetClass;
public StringToSomeEnumConverter(Class<T> targetClass) {
this.targetClass = targetClass;
}
@Override
public T convert(String source) {
return (T) SomeEnum.fromValue(source);
}
}
}
}
My question is - is there some hidden configuration in Spring or in the openApiGenerator which can do this for me automatically so I do not need to implement a custom converter for every enum I have?
答案1
得分: 1
看起来这个问题是通过这个合并的拉取请求解决的。
我们需要添加转换器,因为Spring不使用Jackson来处理路径和查询参数(参见spring-projects/spring-boot#24233)。这将修复#12874。
因此,将openapi-generator
升级到6.5.0
(在撰写时为最新版本)应该解决这个问题。
英文:
Seems that this issue is addressed through this merged pull-request
> We need to add converters because spring doesn't use jackson for path
> and query params (cf spring-projects/spring-boot#24233 ) This will fix
> #12874
Hence, bumping to openapi-generator:6.5.0
(which is latest when writing this) should solve the problem.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论