Springfox 3 可以根据 OAS3 API 文档中的 JSR 303 @Min/@Max 生成整数边界吗?
Can Springfox 3 generate Integer boundaries based on JSR 303 @Min/@Max in OAS3 API docs?
我使用 RestController 创建了一个 Spring 引导应用程序,该应用程序根据 JSR 303 验证注释验证传递给 POST 方法的 DTO。 API 文档是使用 Springfox 生成的。
验证已正确应用并显示在 OAS2 API 文档中。然而,它们在 OAS3 API 文档中不完整 - 没有为整数字段生成 minimum/maximum 边界。
我正在使用 Spring Boot 2.5.2(这很重要,因为最新版本 2.6.2 与 Springfox 有问题)和 Springfox 3.0。 0.
由于我在文档中没有发现具体提示 + Springfox 问题跟踪和 JSR303 支持大部分都在工作,我认为这是 Springfox OAS3 支持中的错误或疏忽。与此同时,我找到了一个解决方法,我将 post 作为答案 - 如果我遗漏了什么或有更好的解决方案,我很乐意听到。
详情:
控制器
@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"
}
}
}
}
}
调试Springfox后,我了解到springfox-oas中的class springfox.documentation.oas.mappers.SchemaMapper
将“通用模型”转换为OAS3格式。
在“通用模型”中,字段边界由“NumericElementFacet”表示。正在映射的特定 属性 是“Schema”的子class。
由“NumberSchema”表示的属性得到正确处理(例如 BigDecimal),它们与“NumericElementFacet”的边界被应用。然而,整数字段(以及进一步的测试表明:还有短和长)由“IntegerSchema”表示,它没有在那里处理,因此边界不会应用于结果 API.
所以我做的解决方法是 subclassing SchemaMapper,post-处理 mapProperties
的结果并将 subclass 注册为 @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;
}
}
就我而言,这工作正常,所以也许它也能帮助其他人。
旁注:每次我检索 http://localhost:8080/v3/api-docs 时都会调用 SchemaMapper,在考虑其他耗时的模式修改时可能需要记住这一点。
我使用 RestController 创建了一个 Spring 引导应用程序,该应用程序根据 JSR 303 验证注释验证传递给 POST 方法的 DTO。 API 文档是使用 Springfox 生成的。
验证已正确应用并显示在 OAS2 API 文档中。然而,它们在 OAS3 API 文档中不完整 - 没有为整数字段生成 minimum/maximum 边界。
我正在使用 Spring Boot 2.5.2(这很重要,因为最新版本 2.6.2 与 Springfox 有问题)和 Springfox 3.0。 0.
由于我在文档中没有发现具体提示 + Springfox 问题跟踪和 JSR303 支持大部分都在工作,我认为这是 Springfox OAS3 支持中的错误或疏忽。与此同时,我找到了一个解决方法,我将 post 作为答案 - 如果我遗漏了什么或有更好的解决方案,我很乐意听到。
详情:
控制器
@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"
}
}
}
}
}
调试Springfox后,我了解到springfox-oas中的class springfox.documentation.oas.mappers.SchemaMapper
将“通用模型”转换为OAS3格式。
在“通用模型”中,字段边界由“NumericElementFacet”表示。正在映射的特定 属性 是“Schema”的子class。
由“NumberSchema”表示的属性得到正确处理(例如 BigDecimal),它们与“NumericElementFacet”的边界被应用。然而,整数字段(以及进一步的测试表明:还有短和长)由“IntegerSchema”表示,它没有在那里处理,因此边界不会应用于结果 API.
所以我做的解决方法是 subclassing SchemaMapper,post-处理 mapProperties
的结果并将 subclass 注册为 @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;
}
}
就我而言,这工作正常,所以也许它也能帮助其他人。
旁注:每次我检索 http://localhost:8080/v3/api-docs 时都会调用 SchemaMapper,在考虑其他耗时的模式修改时可能需要记住这一点。