springfox: Swagger UI unwrapping Spring Data `Pageable` with wrong field names

My controller is the following:

  @GetMapping
  public ResponseEntity getAll(final @PageableDefault(sort = "id") Pageable pageable) {
    return ResponseEntity.ok(null);
  }

The resulting Swagger UI is: screen shot 2018-08-15 at 17 54 22

It looks like it uses the Pageable getters to infer those parameters, bypassing springfox-data-rest configuration. Am I doing something wrong?

I am using Spring Boot 1.5.10.RELEASE and Springfox 2.9.2 with these dependencies:


    <springfox-swagger2.version>2.9.2</springfox-swagger2.version>

[...]

    <dependency>
      <groupId>io.springfox</groupId>
      <artifactId>springfox-core</artifactId>
      <version>${springfox-swagger2.version}</version>
    </dependency>

    <dependency>
      <groupId>io.springfox</groupId>
      <artifactId>springfox-spi</artifactId>
      <version>${springfox-swagger2.version}</version>
    </dependency>

    <dependency>
      <groupId>io.springfox</groupId>
      <artifactId>springfox-spring-web</artifactId>
      <version>${springfox-swagger2.version}</version>
    </dependency>

    <dependency>
      <groupId>io.springfox</groupId>
      <artifactId>springfox-swagger2</artifactId>
      <version>${springfox-swagger2.version}</version>
    </dependency>

    <dependency>
      <groupId>io.springfox</groupId>
      <artifactId>springfox-data-rest</artifactId>
      <version>${springfox-swagger2.version}</version>
    </dependency>

    <dependency>
      <groupId>io.springfox</groupId>
      <artifactId>springfox-bean-validators</artifactId>
      <version>${springfox-swagger2.version}</version>
    </dependency>

    <dependency>
      <groupId>io.springfox</groupId>
      <artifactId>springfox-swagger-ui</artifactId>
      <version>${springfox-swagger2.version}</version>
    </dependency>

About this issue

  • Original URL
  • State: open
  • Created 6 years ago
  • Reactions: 17
  • Comments: 29 (4 by maintainers)

Most upvoted comments

Would be nice to have this feature.

What I came up a while ago was

import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@ApiImplicitParams({
        @ApiImplicitParam(name = "page", dataType = "int", paramType = "query", defaultValue = "0", value = "Results page you want to retrieve (0..N)"),
        @ApiImplicitParam(name = "size", dataType = "int", paramType = "query", defaultValue = "20", 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.")})
public @interface ApiPageable {
}

and in the controller:

@ApiPageable
public ResponseEntity<Page<MyObject>> listBots(@NonNull Pageable pageable) {
    return ResponseEntity.ok(service.myObject(pageable));
}

Hi, I can confirm this issue (with version 2.9.2). In the meantime I have solved it by providing implicit params (as shown below)

 @GetMapping("items")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "page", dataType = "integer", paramType = "query",
                    value = "Results page you want to retrieve (0..N)", defaultValue = "0"),
            @ApiImplicitParam(name = "size", dataType = "integer", paramType = "query",
                    value = "Number of records per page.", defaultValue = "5"),
            @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.")
    })
    public List<Item> GetAll(
            @ApiIgnore(
                    "Ignored because swagger ui shows the wrong params, " +
                    "instead they are explained in the implicit params"
            ) Pageable pageable
    ) {
       // code
    }

You don’t need to place annotation in each function:

Kotlin:

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.",
        example = "&sort=created,asc"
    )
    var sort: String?
)

    @Bean
    fun api(): Docket = Docket(DocumentationType.SWAGGER_2)
        .groupName("Cards")
        .apiInfo(getApiInfo())
        .select()
        .apis(RequestHandlerSelectors.basePackage(controllerFolder))
        .paths(PathSelectors.any())
        .build()
        .directModelSubstitute(Pageable::class.java, SwaggerPageable::class.java)

I used @hannes-angst @ApiPageable and it works for me. I just have to add @ApiIgnore on my Pageable parameter though.

@ApiPageable
public ResponseEntity<Page<MyObject>> listBots(@ApiIgnore @NonNull Pageable pageable) {
    return ResponseEntity.ok(service.myObject(pageable));
}

As workaround, if you’re not using spring-data-rest plus @Import(SpringDataRestConfiguration.class), you can register an AlternateTypeRule that’s doing the same as pageableConvension in the swagger configuration:

@Configuration
@EnableSwagger2
public class SwaggerConfig {

    private final TypeResolver typeResolver;
    private final RepositoryRestConfiguration restConfiguration;

    public SwaggerConfig(TypeResolver typeResolver, RepositoryRestConfiguration restConfiguration) {
        this.typeResolver = typeResolver;
        this.restConfiguration = restConfiguration;
    }

    @Bean
    @ConditionalOnMissingBean
    public Docket apiDocket() {
        return new Docket(DocumentationType.SWAGGER_2)
                .alternateTypeRules(newRule(typeResolver.resolve(Pageable.class),
                                            pageableMixin(restConfiguration),
                                            Ordered.HIGHEST_PRECEDENCE))
                ...
                .build();
    }

    private Type pageableMixin(RepositoryRestConfiguration restConfiguration) {
        return new AlternateTypeBuilder()
                .fullyQualifiedClassName(
                        String.format("%s.generated.%s",
                                      Pageable.class.getPackage().getName(),
                                      Pageable.class.getSimpleName()))
                .withProperties(Arrays.asList(
                        property(Integer.class,
                                 restConfiguration.getPageParamName()),
                        property(Integer.class,
                                 restConfiguration.getLimitParamName()),
                        property(String.class,
                                 restConfiguration.getSortParamName())
                ))
                .build();
    }

    private AlternateTypePropertyBuilder property(Class<?> type, String name) {
        return new AlternateTypePropertyBuilder()
                .withName(name)
                .withType(type)
                .withCanRead(true)
                .withCanWrite(true);
    }

@ArchangelX360 you’re right that it should work, but the only for spring data rest. For spring mvc, it isnt natively supported. Will add support in the upcoming release.

Interesting note: the name and defaultValue are not the same for every Spring project, they are dynamic because they can be configured.

e.g.

spring:
  data:
    web:
      pageable:
        page-parameter: page
        size-parameter: page-size
        one-indexed-parameters: true
        default-page-size: 10
        max-page-size: 50

Would be nice to have this feature.

What I came up a while ago was

import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@ApiImplicitParams({
        @ApiImplicitParam(name = "page", dataType = "int", paramType = "query", defaultValue = "0", value = "Results page you want to retrieve (0..N)"),
        @ApiImplicitParam(name = "size", dataType = "int", paramType = "query", defaultValue = "20", 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.")})
public @interface ApiPageable {
}

and in the controller:

@ApiPageable
public ResponseEntity<Page<MyObject>> listBots(@NonNull Pageable pageable) {
    return ResponseEntity.ok(service.myObject(pageable));
}

For some strange reason i was getting a io.swagger.models.parameters.AbstractSerializableParameter - Illegal DefaultValue for parameter type integer because of the dataType="int" and defaultValue="0" so i changed the dataType to dataType="integer" like below and the error doesn’t show up:

import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@ApiImplicitParams({
        @ApiImplicitParam(name = "page", dataType = "integer", paramType = "query", defaultValue = "0", value = "Results page you want to retrieve (0..N)"),
        @ApiImplicitParam(name = "size", dataType = "integer", paramType = "query", defaultValue = "20", 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.")})
public @interface ApiPageable {
}

Not sure if this is a bug or something

Any update? 😃

No updates on this bug?

Hi,

Is there any update on this issue? Still seeing the original issue

Thanks

I just ran into this as well. Subscribing to the issue so I know when it’s been fixed.

@ArchangelX360 Yep, definitely a bug missing feature. Btw. be careful this could break the schema generation (I ran into this issue with the @ApiImplicitParam annotation) - see #2204