簡體   English   中英

Springfox 3 能否根據 OAS3 API 文檔中的 JSR 303 @Min/@Max 生成 Integer 邊界?

[英]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 文檔中是不完整的 - 沒有為 Integer 字段生成最小/最大邊界。

我正在使用 Spring Boot 2.5.2(這很重要,因為最新版本 2.6.2 與 Springfox 存在問題)和 Springfox 3.0.0。

由於我在文檔 + Springfox 問題跟蹤中沒有發現任何具體提示,並且 JSR303 支持大部分都在起作用,我認為這是 Springfox OAS3 支持中的錯誤或疏忽。 與此同時,我找到了一個解決方法,我將把它作為答案發布——如果我遺漏了什么或者有更好的解決方案,我會很高興聽到這個消息。

細節:

Controller

@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- springfox.documentation.oas.mappers.SchemaMapper中的 class springfox.documentation.oas.mappers.SchemaMapper 將“通用模型”轉換為 OAS3 的格式。 在“通用模型”中,字段邊界由“NumericElementFacet”表示。 正在映射的特定屬性是“Schema”的子類。

問題似乎發生在這里: https://github.com/springfox/springfox/blob/bc9d0cad83e5dfdb30ddb487594fbc33fc1ba28c/springfox-oas/src/main/java/springfox/documentation/oas/mappers/SchemaMapper.java#L358

由“NumberSchema”表示的屬性被正確處理(例如 BigDecimal),它們來自“NumericElementFacet”的邊界被應用。 然而,Integer 字段(以及進一步的測試表明:也短和長)由“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;
    }
}

就我而言,這很好用,所以也許它也可以幫助其他人。

旁注:每次我檢索 http://localhost:8080/v3/api-docs 時都會調用 SchemaMapper,這在考慮對模式進行其他耗時的修改時可能需要牢記。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM