【问题标题】:Swagger documentation for Spring Pageable interfaceSpring Pageable 接口的 Swagger 文档
【发布时间】:2016-05-26 01:34:46
【问题描述】:

我使用 Spring Boot 开发了一个微服务。 REST API 的文档是用 Swagger 制作的。一些 REST 资源利用 Spring 概念免费提供分页。下面是一个例子:

@RequestMapping(value = "/buckets", method = GET)
public PagedResources list(Pageable pageable, PagedResourcesAssembler assembler) {
    return bucketService.listBuckets(pageable, assembler);
}

如果我打开 Swagger 页面,资源可以使用以下表单:

我遇到的问题是使用 content-type application/json 检测到 pageable 参数,例如,我不知道如何传递值来更改页面大小。所有值似乎都被忽略了。

是否可以将查询参数作为 JSON 对象传递?或者是否可以配置 Swagger 为 Pageable 接口包含的 getter 生成独立的查询参数字段?

请注意,我将 Springfox 与 Gradle 一起使用:

compile 'io.springfox:springfox-spring-web:2.3.1'
compile 'io.springfox:springfox-swagger2:2.3.1'
compile 'io.springfox:springfox-swagger-ui:2.3.1'

【问题讨论】:

  • 您是否使用 spring-fox 从 REST 控制器生成 Swagger 规范?
  • 是的,我在 2.3.1 版本中使用 Springfox。

标签: json spring rest swagger


【解决方案1】:

Open API 3.0 无缝集成。

例如,

@GetMapping("/filter")
public Page<Employee> filterEmployees(Pageable pageable) {
     return repository.getEmployees(pageable);
}

添加 springdoc-openapi-data-rest 依赖

implementation 'org.springdoc:springdoc-openapi-data-rest:1.5.2'

注意:如果你有多个参数,你可以添加'@ParameterObject'

public Page<Employee> filterEmployees(@ParameterObject Pageable pageable)

【讨论】:

    【解决方案2】:

    Java 示例:

    豆子:

    @Bean
    public Docket api() {
        return new Docket(DocumentationType.SWAGGER_2)
            .select()
            .paths(PathSelectors.any())
            .build()
            .directModelSubstitute(Pageable.class, SwaggerPageable.class);
    }
    

    SwaggerPageable:

    @Getter
    private static class SwaggerPageable {
    
        @ApiParam(value = "Number of records per page", example = "0")
        @Nullable
        private Integer size;
    
        @ApiParam(value = "Results page you want to retrieve (0..N)", example = "0")
        @Nullable
        private Integer page;
    
        @ApiParam(value = "Sorting criteria in the format: property(,asc|desc). Default sort order is ascending. Multiple sort criteria are supported.")
        @Nullable
        private String sort;
    
    }
    

    大摇大摆:

    【讨论】:

      【解决方案3】:

      这里是集成到 OpenAPI v3 的 springdoc-openapi-data-rest 中的注释版本:

      @Target({ ElementType.METHOD, ElementType.ANNOTATION_TYPE })
      @Retention(RetentionPolicy.RUNTIME)
      @Parameter(in = ParameterIn.QUERY
              , description = "Zero-based page index (0..N)"
              , name = "page"
              , content = @Content(schema = @Schema(type = "integer", defaultValue = "0")))
      @Parameter(in = ParameterIn.QUERY
              , description = "The size of the page to be returned"
              , name = "size"
              , content = @Content(schema = @Schema(type = "integer", defaultValue = "20")))
      @Parameter(in = ParameterIn.QUERY
              , description = "Sorting criteria in the format: property(,asc|desc). "
              + "Default sort order is ascending. " + "Multiple sort criteria are supported."
              , name = "sort"
              , content = @Content(array = @ArraySchema(schema = @Schema(type = "string"))))
      public @interface PageableAsQueryParam {
      
      }
      

      https://springdoc.github.io/springdoc-openapi-demos/faq.html#how-can-i-map-pageable-spring-date-commons-object-to-correct-url-parameter-in-swagger-ui

      【讨论】:

      【解决方案4】:

      此解决方案无需注释每个控制器中的每个 API 方法即可工作。首先,我们使用正确的属性名称和描述创建 Pageable 类的替换(Kotlin 代码,您可以使用 Java 接口):

      data class SwaggerPageable(
              @ApiModelProperty("Number of records per page", example = "20")
              val size: Int?,
      
              @ApiModelProperty("Results page you want to retrieve (0..N)", example = "0")
              val page: Int?,
      
              @ApiModelProperty("Sorting criteria in the format: property(,asc|desc)." +
                      "Default sort order is ascending. Multiple sort criteria are supported.")
              var sort: String?
      )
      

      然后在 Swagger 配置中,将 Pageable 的直接替换添加到此类(同样是 Kotlin 代码,但 Java 应该非常相似):

      @Bean
      fun api(): Docket {
          return Docket(DocumentationType.SWAGGER_2)
                  .select()
                  .paths(PathSelectors.any())
                  .build()
                  .directModelSubstitute(Pageable::class.java, SwaggerPageable::class.java)
      }
      

      结果如下:

      缺点是无法在ApiModelProperty 中定义默认值,但这对我的项目来说已经足够了。

      【讨论】:

      • 我们如何在 Open API 3 版本中使用 Pageable?
      【解决方案5】:

      回答 Evgeny 指出的验证问题。

      使用

      @ApiImplicitParams({
          @ApiImplicitParam(name = "page", dataType = "int", paramType = "query", value = "Results page you want to retrieve (0..N)"),
          @ApiImplicitParam(name = "size", dataType = "int", paramType = "query", value = "Number of records per page."),
          @ApiImplicitParam(name = "sort", allowMultiple = true, dataType = "string", paramType = "query", value = "Sorting criteria in the format: property(,asc|desc). "
                  + "Default sort order is ascending. " + "Multiple sort criteria are supported.") })
      

      抛出异常:

      Illegal DefaultValue  for parameter type integer
      java.lang.NumberFormatException: For input string: ""
          at java.base/java.lang.NumberFormatException.forInputString(NumberFormatException.java:68)
          at java.base/java.lang.Long.parseLong(Long.java:709)
          at java.base/java.lang.Long.valueOf(Long.java:1151)
          at io.swagger.models.parameters.AbstractSerializableParameter.getExample(AbstractSerializableParameter.java:412)
          at jdk.internal.reflect.GeneratedMethodAccessor366.invoke(Unknown Source)
          at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
          at java.base/java.lang.reflect.Method.invoke(Method.java:567)
          at com.fasterxml.jackson.databind.ser.BeanPropertyWriter.serializeAsField(BeanPropertyWriter.java:688)
          at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:719)
          at com.fasterxml.jackson.databind.ser.BeanSerializer.serialize(BeanSerializer.java:155)
          at com.fasterxml.jackson.databind.ser.impl.IndexedListSerializer.serializeContents(IndexedListSerializer.java:119)
          at com.fasterxml.jackson.databind.ser.impl.IndexedListSerializer.serialize(IndexedListSerializer.java:79)
      

      (至少,它适用于 springfox-swagger2 和 springfox-swagger2-ui 版本 2.9.2)

      您可以通过遵循 Evgeny 的回答或为整数参数添加默认值和示例值来避免异常:

      @ApiImplicitParams({
          @ApiImplicitParam(name = "page", dataType = "int", paramType = "query", value = "Results page you want to retrieve (0..N)", defaultValue = "0", example = "2"),
          @ApiImplicitParam(name = "size", dataType = "int", paramType = "query", value = "Number of records per page.", defaultValue = "20", example = "10"),
          @ApiImplicitParam(name = "sort", allowMultiple = true, dataType = "string", paramType = "query", value = "Sorting criteria in the format: property(,asc|desc). "
                  + "Default sort order is ascending. " + "Multiple sort criteria are supported.") })
      

      【讨论】:

        【解决方案6】:

        【讨论】:

        • 使用这个类我得到以下错误: 原因:org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'org.springframework.data.rest.core.config.RepositoryRestConfiguration'可用:预计至少有 1 个符合自动装配候选资格的 bean。依赖注释:{}
        【解决方案7】:

        虽然使用隐式参数的解决方案有效,但它引入了许多额外的、脆弱的代码。最后我们采用了以下解决方案:

        @GetMapping(value = "/")
        public HttpEntity<PagedResources<Item>> getItems(
            @RequestParam(value = "page", required = false) Integer page,
            @RequestParam(value = "size", required = false) Integer size,
            PagedResourcesAssembler assembler) {
            Page<Item> itemPage = itemService.listItems(PageRequest.of(page, size, Sort.unsorted()));
            return new ResponseEntity<>(assembler.toResource(itemPage), HttpStatus.OK);
        }
        

        我们将PageRequest(实现Pageable)传递给我们的服务,该服务返回Page。 (全部来自org.springframework.data.domain)。

        org.springframework.data.web.PagedResourcesAssembler 通过控制器方法自动注入,并允许将 Items 映射到 org.springframework.hateoas.PagedResources

        我们不需要动态排序,所以我们省略了;由于 springfox 不能很好地与 org.springframework.data.domain.Sort 配合使用,因此添加排序会带来一些挑战。

        【讨论】:

          【解决方案8】:

          基于 Vineet Bhatia 的回答,您可以将解决方案包装在自定义注释中以实现可重用性:

          @Target({ ElementType.METHOD, ElementType.ANNOTATION_TYPE, ElementType.TYPE })
          @Retention(RetentionPolicy.RUNTIME)
          @ApiImplicitParams({
              @ApiImplicitParam(name = "page", dataType = "int", paramType = "query", value = "Results page you want to retrieve (0..N)"),
              @ApiImplicitParam(name = "size", dataType = "int", paramType = "query", value = "Number of records per page."),
              @ApiImplicitParam(name = "sort", allowMultiple = true, dataType = "string", paramType = "query", value = "Sorting criteria in the format: property(,asc|desc). "
                      + "Default sort order is ascending. " + "Multiple sort criteria are supported.") })
          @interface ApiPageable {
          }
          

          然后可以这样使用:

          @ApiPageable
          public Page<Data> getData(Pageable pageRequest) {
          

          【讨论】:

          • 这样不行。 'ApiImplicitParams' not applicable to annotation type
          • 在 2.8.0 版中工作得很好
          • 确保对Pageable参数使用@ApiIgnore注解。
          • @Sean - 我们如何为 OpenAPI 3 或 Swagger 3 配置分页?
          • @SeanConnolly 他在这里提出了一个问题:stackoverflow.com/questions/60058976/…
          【解决方案9】:

          当您不在本地主机上运行时,Vineet Bhatia 的答案将出现验证问题。它将争论整数参数,它们不对应于 json 模式。

          所以我把整数改成字符串:

              @ApiImplicitParams({
                  @ApiImplicitParam(name = "page", dataType = "string", paramType = "query",
                          value = "Results page you want to retrieve (0..N)"),
                  @ApiImplicitParam(name = "size", dataType = "string", paramType = "query",
                          value = "Number of records per page."),
                  @ApiImplicitParam(name = "sort", allowMultiple = true, dataType = "string", paramType = "query",
                          value = "Sorting criteria in the format: property(,asc|desc). " +
                                  "Default sort order is ascending. " +
                                  "Multiple sort criteria are supported.")
          })
          

          【讨论】:

            【解决方案10】:

            Vineet Bhatia 对@ApiImplicitParams 的回答看起来不错。但是我遇到了这样的情况,当@ApiIgnor@ApiParam(hidden = true) 不起作用时,您仍然可以观察到汇编器和可分页参数。我通过添加下一行解决了这个问题

            docket.ignoredParameterTypes(Pageable.class, PagedResourcesAssembler.class);
            

            到我的 SwaggerConfig 中的 Docket bean。

            【讨论】:

            • 看起来这不适用于 SpringDocs-openapi-ui。任何指导如何使它工作?
            【解决方案11】:

            这是 Spring-Fox 的一个已知问题。请参阅问题 #755。根据 zdila 的评论 2,此时的替代方法是添加 @ApiImplicitParams,这并不理想,但确实有效。

            @ApiImplicitParams({
                @ApiImplicitParam(name = "page", dataType = "integer", paramType = "query",
                        value = "Results page you want to retrieve (0..N)"),
                @ApiImplicitParam(name = "size", dataType = "integer", paramType = "query",
                        value = "Number of records per page."),
                @ApiImplicitParam(name = "sort", allowMultiple = true, dataType = "string", paramType = "query",
                        value = "Sorting criteria in the format: property(,asc|desc). " +
                                "Default sort order is ascending. " +
                                "Multiple sort criteria are supported.")
            })
            

            [

            1https://github.com/springfox/springfox/issues/755

            2https://github.com/springfox/springfox/issues/755#issuecomment-135059871

            【讨论】:

            • 我发现我必须使用dataType = "int" 否则数据类型显示为未定义,如图所示。
            • 看来问题已经解决,答案可能会随之更新..
            • 能否请您在这里指导我:stackoverflow.com/questions/59967447/…
            • springdoc的任何解决方案?
            猜你喜欢
            • 2019-05-03
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2018-11-16
            • 2016-03-05
            • 1970-01-01
            • 2021-02-06
            相关资源
            最近更新 更多