• Spring Boot集成SpringFox 3.0与Pageable参数处理


    Springfox 3.0有多个模块,提供了spring boot starter,与Spring Boot集成时仅需引入springfox-boot-starter,如下:

    <dependency>
        <groupId>io.springfox</groupId>
        <artifactId>springfox-boot-starter</artifactId>
        <version>3.0.0</version>
    </dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5

    如果您以前使用了Springfox 2.x,需要修改一下Swagger配置

    删除@EnableSwagger2注解,改为@EnableOpenApi
    将Docket中的参数DocumentationType.SWAGGER_2改为DocumentationType.OAS_30
    Springfox 3.0删除了一些第三方模块,如有引用需要替换
    修改后的配置如下:

    package com.ys.config;
    import com.fasterxml.classmate.TypeResolver;
    import org.itrunner.heroes.exception.ErrorMessage;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.http.ResponseEntity;
    import springfox.documentation.builders.ApiInfoBuilder;
    import springfox.documentation.builders.PathSelectors;
    import springfox.documentation.builders.RequestHandlerSelectors;
    import springfox.documentation.oas.annotations.EnableOpenApi;
    import springfox.documentation.service.*;
    import springfox.documentation.spi.DocumentationType;
    import springfox.documentation.spi.service.contexts.SecurityContext;
    import springfox.documentation.spring.web.plugins.Docket;
    
    import java.time.LocalDate;
    import java.util.List;
    
    import static java.util.List.of;
    
    @EnableOpenApi
    @Configuration
    public class SwaggerConfig {
    
        @Bean
        public Docket createRestApi() {
            return new Docket(DocumentationType.OAS_30)
                    .select()
                    .apis(RequestHandlerSelectors.basePackage("com.ys.controller"))
                    .paths(PathSelectors.any())
                    .build()
                    .apiInfo(apiInfo())
                    .pathMapping("/")
                    .directModelSubstitute(LocalDate.class, String.class)
                    .genericModelSubstitutes(ResponseEntity.class)
                    .additionalModels(new TypeResolver().resolve(ErrorMessage.class))
                    .useDefaultResponseMessages(false)
                    .securitySchemes(of(authenticationScheme()))
                    .securityContexts(of(securityContext()))
                    .enableUrlTemplating(false);
        }
    
        private ApiInfo apiInfo() {
            return new ApiInfoBuilder()
                    .title("Api Documentation")
                    .description("Api Documentation")
                    .contact(new Contact("xx", "http://xxxxxx", "xxxxx@163.com"))
                    .version("1.0.0")
                    .build();
        }
    
        private HttpAuthenticationScheme authenticationScheme() {
            return HttpAuthenticationScheme.JWT_BEARER_BUILDER.name("BearerToken").build();
        }
    
        private SecurityContext securityContext() {
            return SecurityContext.builder()
                    .securityReferences(defaultAuth())
                    .operationSelector(operationContext ->
                            operationContext.requestMappingPattern().startsWith("/api/")
                    )
                    .build();
        }
    
        private List<SecurityReference> defaultAuth() {
            AuthorizationScope authorizationScope = new AuthorizationScope("global", "accessEverything");
            AuthorizationScope[] authorizationScopes = new AuthorizationScope[1];
            authorizationScopes[0] = authorizationScope;
            return of(new SecurityReference("BearerToken", authorizationScopes));
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71

    上面我们使用了HttpAuthenticationScheme构建Bearer Token Authentication,在Swagger UI界面点击Authorize时直接输出token值即可。

    Springfox 3.0的Swagger UI地址,从http://host/context-path/swagger-ui.html 改成了 http://host/context-path/swagger-ui/ ,OAS 3.0的api docs地址默认为/v3/api-docs,因此Security中的ignore path要进行相应修改。
    在这里插入图片描述
    Pageable参数
    当REST API的方法含有org.springframework.data.domain.Pageable参数时,Springfox根据接口生成pageNumber、pageSize、offset、paged、unpaged、sort.sorted、sort.unsorted等参数,这与Spring Boot实际使用的参数是不一致的,因此需要针对Pageable参数进行特殊处理。

    我们使用OperationBuilderPlugin处理Pageable参数,如下:

    package com.ys.config;
    import com.fasterxml.classmate.ResolvedType;
    import com.fasterxml.classmate.TypeResolver;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.core.annotation.Order;
    import org.springframework.data.domain.Pageable;
    import org.springframework.stereotype.Component;
    import springfox.documentation.builders.RequestParameterBuilder;
    import springfox.documentation.schema.ScalarType;
    import springfox.documentation.service.ParameterType;
    import springfox.documentation.service.RequestParameter;
    import springfox.documentation.service.ResolvedMethodParameter;
    import springfox.documentation.spi.DocumentationType;
    import springfox.documentation.spi.service.OperationBuilderPlugin;
    import springfox.documentation.spi.service.contexts.OperationContext;
    
    import java.util.ArrayList;
    import java.util.List;
    
    @Component
    @Order
    public class PageableParameterReader implements OperationBuilderPlugin {
        private final TypeResolver resolver;
    
        @Autowired
        public PageableParameterReader(TypeResolver resolver) {
            this.resolver = resolver;
        }
    
        @Override
        public void apply(OperationContext context) {
            List<ResolvedMethodParameter> methodParameters = context.getParameters();
            ResolvedType pageableType = resolver.resolve(Pageable.class);
            List<RequestParameter> parameters = new ArrayList<>();
    
            for (ResolvedMethodParameter methodParameter : methodParameters) {
                ResolvedType resolvedType = methodParameter.getParameterType();
    
                if (pageableType.equals(resolvedType)) {
    
                    parameters.add(new RequestParameterBuilder()
                            .in(ParameterType.QUERY)
                            .name("page")
                            .query(q -> q.model(m -> m.scalarModel(ScalarType.INTEGER)))
                            .description("Results page you want to retrieve (0..N)").build());
                    parameters.add(new RequestParameterBuilder()
                            .in(ParameterType.QUERY)
                            .name("size")
                            .query(q -> q.model(m -> m.scalarModel(ScalarType.INTEGER)))
                            .description("Number of records per page").build());
                    parameters.add(new RequestParameterBuilder()
                            .in(ParameterType.QUERY)
                            .name("sort")
                            .query(q -> q.model(m -> m.collectionModel(c -> c.model(cm -> cm.scalarModel(ScalarType.STRING)))))
                            .description("Sorting criteria in the format: property(,asc|desc). "
                                    + "Default sort order is ascending. "
                                    + "Multiple sort criteria are supported.")
                            .build());
                    context.operationBuilder().requestParameters(parameters);
                }
            }
        }
    
        @Override
        public boolean supports(DocumentationType delimiter) {
            return true;
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69

    方案一:在Controller分页方法的Pageable参数前添加@ApiIgnore,忽略默认的参数解析:

    public ResponseEntity> getAllDevices(@ApiIgnore @PageableDefault(sort = {"id"}, direction = Sort.Direction.DESC) Pageable pageable,
                                                         @RequestParam(required = false) @ApiParam(value = "关键字(序列号,地址,归属单位)") String keyword,
    
    • 1
    • 2

    方案二:全局配置,在SwaggerConfig类的createRestApi方法中添加:.ignoredParameterTypes(Pageable.class)

    @EnableOpenApi
    @Configuration
    public class SwaggerConfig {
    
        @Bean
        public Docket createRestApi() {
            return new Docket(DocumentationType.OAS_30)
                    .select()
                    .apis(RequestHandlerSelectors.basePackage("com.ys.controller"))
                    .paths(PathSelectors.any())
                    .build()
                    .apiInfo(apiInfo())
                    .ignoredParameterTypes(Pageable.class)
                    .pathMapping("/")
                    .directModelSubstitute(LocalDate.class, String.class)
                    .genericModelSubstitutes(ResponseEntity.class)
                    .additionalModels(new TypeResolver().resolve(ErrorMessage.class))
                    .useDefaultResponseMessages(false)
                    .securitySchemes(of(authenticationScheme()))
                    .securityContexts(of(securityContext()))
                    .enableUrlTemplating(false);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    修改后Swagger UI中显示的Pageable参数如下:
    在这里插入图片描述

  • 相关阅读:
    2023 PolarD&N靶场通关笔记 Crypto
    OSINT 和 15 大开源情报工具
    白话强化学习(理论+代码)
    TuGraph数据库
    008_第一代软件系统架构
    Codeforces Round #821 (Div. 2) A~C(模拟、构造)
    20.cuBLAS开发指南中文版--cuBLAS中的Level-2函数ger()
    SpringBoot之Swagger
    如何去正确理解股票量化接口的真实用途?
    java中使用springboot2.5.6整合activiti6和flowable6.3.0工作流引擎
  • 原文地址:https://blog.csdn.net/qq_34287953/article/details/133992843