我创建了一个Spring应用程序,它使用RestController验证传递给POST方法的DTO,该方法基于JSR 303验证注释。API文档是使用Springfox生成的。
验证是正确应用的,并显示在OAS2 API文档中。然而,它们在OAS3 API文档中是不完整的--不为Integer字段生成最小/最大边界。
我正在使用SpringBoot2.5.2(这很重要,因为最新版本2.6.2与Springfox有问题)和Springfox 3.0.0。
由于我在文档+ Springfox问题跟踪和JSR303支持中没有发现具体的提示,所以我认为这是Springfox OAS3支持中的一个bug或疏忽。与此同时,我找到了一个解决办法,如果我错过了什么,或者有更好的解决方案,我会很高兴听到这个答案的。
详细信息:
控制器
@Slf4j
@RestController
public class MyController {
@PostMapping
public void send(@Valid @RequestBody MyDTO dto) {
log.info("{}", dto);
}
}DTO
@Value
public class MyDTO {
@Size(max = 200)
String text;
@Max(2)
@Min(1)
Integer number;
@Max(4)
@Min(3)
int number2;
@Max(6)
@Min(5)
BigDecimal decimal;
}OAS2 DTO模式(从http://localhost:8080/v2/api-docs中提取)
{
"MyDTO": {
"type": "object",
"properties": {
"decimal": {
"type": "number",
"minimum": 5,
"maximum": 6,
"exclusiveMinimum": false,
"exclusiveMaximum": false
},
"number": {
"type": "integer",
"format": "int32",
"minimum": 1,
"maximum": 2,
"exclusiveMinimum": false,
"exclusiveMaximum": false
},
"number2": {
"type": "integer",
"format": "int32",
"minimum": 3,
"maximum": 4,
"exclusiveMinimum": false,
"exclusiveMaximum": false
},
"text": {
"type": "string",
"minLength": 0,
"maxLength": 200
}
},
"title": "MyDTO"
}
}OAS3 DTO模式(从http://localhost:8080/v3/api-docs中提取)
{
"schemas": {
"MyDTO": {
"title": "MyDTO",
"type": "object",
"properties": {
"decimal": {
"maximum": 6,
"exclusiveMaximum": false,
"minimum": 5,
"exclusiveMinimum": false,
"type": "number",
"format": "bigdecimal"
},
"number": {
"type": "integer",
"format": "int32"
},
"number2": {
"type": "integer",
"format": "int32"
},
"text": {
"maxLength": 200,
"minLength": 0,
"type": "string"
}
}
}
}
}发布于 2022-01-14 09:49:15
调试Springfox之后,我了解到springfox中的类springfox.documentation.oas.mappers.SchemaMapper将“通用模型”转换为OAS3格式。在“一般模型”中,字段边界由"NumericElementFacet“表示。正在映射的特定属性是"Schema“的子类。
由"NumberSchema“表示的属性被正确地处理(例如BigDecimal),它们与"NumericElementFacet”的边界被应用。但是,整数字段(以及进一步的测试显示:也是短的和长的)是由"IntegerSchema“表示的,因为在那里不处理它,所以边界不会应用到结果的API中。
因此,作为解决办法,我所做的是子类SchemaMapper,对mapProperties的结果进行后处理,并将子类注册为@Primary来覆盖springfox组件:
import io.swagger.v3.oas.models.media.Schema;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Component;
import springfox.documentation.oas.mappers.*;
import springfox.documentation.schema.*;
import springfox.documentation.service.ModelNamesRegistry;
import java.util.*;
@Primary
@Component
@Slf4j
public class IntegerBoundarySupportingOasSchemaMapper extends SchemaMapper {
@Override
@SuppressWarnings("rawtypes")
protected Map<String, Schema> mapProperties(
Map<String, PropertySpecification> properties,
ModelNamesRegistry modelNamesRegistry) {
var result = super.mapProperties(properties, modelNamesRegistry);
result.values()
.stream()
// "integer" seems to cover at least Java Short, Integer and Long.
.filter(property -> "integer".equals(property.getType()))
.forEach(property -> properties.get(property.getName())
.getFacets()
.stream()
.filter(NumericElementFacet.class::isInstance)
.map(NumericElementFacet.class::cast)
.findFirst()
.ifPresent(f -> {
log.trace("Adding boundaries to API field {} (min={}, max={})",
property.getName(),
f.getMinimum(),
f.getMaximum());
property.setMaximum(f.getMaximum());
property.exclusiveMaximum(f.getExclusiveMaximum());
property.setMinimum(f.getMinimum());
property.exclusiveMinimum(f.getExclusiveMinimum());
}));
return result;
}
}在我的例子中,这是很好的工作,所以也许它也能帮助别人。
附带注意:每次我检索SchemaMapper时都会调用http://localhost:8080/v3/api-docs,在考虑对模式进行其他耗时的修改时,可能要记住这一点。
https://stackoverflow.com/questions/70708724
复制相似问题