zoukankan      html  css  js  c++  java
  • 从springfox迁移到springdoc

    前言

    之所以考虑从springfox迁移到springdoc是因为我的开源项目hoteler在升级到spring boot 2.6之后,UT的CI/CD挂了:

    HotelerApplicationTests > contextLoads() FAILED
        java.lang.IllegalStateException at DefaultCacheAwareContextLoaderDelegate.java:132
            Caused by: org.springframework.context.ApplicationContextException at DefaultLifecycleProcessor.java:181
                Caused by: java.lang.NullPointerException at WebMvcPatternsRequestConditionWrapper.java:56
    
    ErrorPropTest > setErrors() FAILED
        java.lang.IllegalStateException at DefaultCacheAwareContextLoaderDelegate.java:132
            Caused by: org.springframework.context.ApplicationContextException at DefaultLifecycleProcessor.java:181
                Caused by: java.lang.NullPointerException at WebMvcPatternsRequestConditionWrapper.java:56
    
    ... more
    

    在一番定位和谷歌之后,在Spring 5.3/Spring Boot 2.4 supportspring boot升级2.6.0。启动报错 原因详见:springfox/springfox#3462中的提示下,猜测是springfox未升级导致的不兼容导致的问题。本来计划等待springfox官方自行升级,结果一看github的提价记录,springfox从2020年10月14号之后就在也没有更新代码了,因此决定,从springfox迁移到springdoc

    备注

    如果仍然想在spring 2.6之后使用springfox,建议在配置文件中添加:

    spring.mvc.pathmatch.matching-strategy=ant_path_matcher
    

    更多信息可以参考https://github.com/lWoHvYe/eladmin

    删除springfox依赖和相关代码

    首先删除springfox的依赖:

    # gradle
    io.springfox:springfox-boot-starter:3.0.0`
    
    # maven
    <dependency>
        <groupId>io.springfox</groupId>
        <artifactId>springfox-boot-starter</artifactId>
        <version>3.0.0</version>
    </dependency>
    

    删除Controller下的swagger的注解,

    删除自定的swagger的配置类,

    删除@EnableOpenApi

    导入springdoc依赖

    对于gradle用户:

    implementation 'org.springdoc:springdoc-openapi-ui:1.5.12'
    

    对于maven用户:

    <dependency>
        <groupId>org.springdoc</groupId>
        <artifactId>springdoc-openapi-ui</artifactId>
        <version>1.5.12</version>
    </dependency>
    

    OpenApi配置

    创建OpenApi配置类

    @Configuration
    public class OpenApiConfig {
    
    }
    

    定义License:

    private License license() {
          return new License()
                  .name("MIT")
                  .url("https://opensource.org/licenses/MIT");
    }
    

    注: 这里我采用MIT的开源协议,更多协议,请访问https://opensource.org/licenses

    定义Open Api的基本信息:

    private Info info() {
          return new Info()
                  .title("Hoteler Open API")
                  .description("大明二代")
                  .version("v0.0.1")
                  .license(license());
    }
    

    定义外部文档信息:

    private ExternalDocumentation externalDocumentation() {
        return new ExternalDocumentation()
                .description("大明二代的Hoteler")
                .url("https://github.com/damingerdai/hoteler");
    }
    

    定义OpenAPI的spring bean:

    @Bean
    public OpenAPI springShopOpenAPI() {
        return new OpenAPI()
                .info(info())
                .externalDocs(externalDocumentation());
    }
    

    在配置文件中定义openapi的扫描策略:

    1. 基于指定接口包扫描
    springdoc:
      packagesToScan: org.daming.hoteler.api.web (这是我自己的web包)
    
    1. 基于接口路由扫描
    springdoc:
      pathsToMatch: /api/**, /dev/**
    

    在controler添加@Tag注解
    在controler方法上添加@Operation注解
    在 @Operation 注解上添加@Parameters 和 @Parameter 说明,用于springdoc解析controler方法的参数
    在的请求体类添加@Schema注解

    示例:

    package org.daming.hoteler.api.web;
    
    import io.swagger.v3.oas.annotations.Operation;
    import io.swagger.v3.oas.annotations.Parameter;
    import io.swagger.v3.oas.annotations.enums.ParameterIn;
    import io.swagger.v3.oas.annotations.security.SecurityRequirement;
    import io.swagger.v3.oas.annotations.tags.Tag;
    ...more
    
    /**
     * customer constoller
     *
     * @author gming001
     * @create 2020-12-25 22:13
     **/
    @Tag(name = "客户", description = "客户 API")
    @RestController
    @RequestMapping("api/v1")
    public class CustomerController {
    
        private ICustomerService customerService;
    
        private IErrorService errorService;
    
        @Operation(
                summary = "创建客户信息"},
                parameters = {
                        @Parameter(name = "body", description = "创建用户信息的请求体")
                }
        )
        @PostMapping("customer")
        public CommonResponse create(@RequestBody CreateCustomerRequest request) {
            var customer = new Customer().setName(request.getName()).setGender(request.getGender()).setCardId(request.getCardId()).setPhone(request.getPhone());
            var id =  this.customerService.create(customer);
            return new DataResponse<>(id);
        }
    
        @Operation(summary = "更新客户信息")
        @PutMapping("customer")
        public CommonResponse update(@RequestBody UpdateCustomerRequest request) {
            var customer = new Customer().setId(request.getId()).setName(request.getName()).setGender(request.getGender()).setCardId(request.getCardId()).setPhone(request.getPhone());
            this.customerService.update(customer);
            return new CommonResponse();
        }
    
        @Operation(summary = "获取所有的客户信息")
        @GetMapping("customers")
        public CommonResponse list() {
            var list = this.customerService.list();
            return new ListResponse<>(list);
        }
    
    
        @Operation(summary = "删除客户信息")
        @DeleteMapping("customer/{id}")
        public CommonResponse delete(@PathVariable("id") String customerId) throws HotelerException {
            try {
                var id = Long.valueOf(customerId);
                this.customerService.delete(id);
            } catch (NumberFormatException nfe) {
                var params = new Object[] { nfe.getMessage() };
                throw errorService.createHotelerException(ErrorCodeConstants.BAD_REQUEST_ERROR_CODEE, params, nfe);
            } catch (Exception ex) {
                throw errorService.createHotelerException(ErrorCodeConstants.SYSTEM_ERROR_CODEE, ex);
            }
    
            return new CommonResponse();
        }
    
        public CustomerController(
                ICustomerService customerService,
                IErrorService errorService) {
            super();
            this.customerService = customerService;
            this.errorService = errorService;
        }
    }
    
    /**
     * create customer request
     *
     * @author gming001
     * @create 2020-12-25 22:15
     **/
    @Schema(name = "创建客户请求")
    public class CreateCustomerRequest implements Serializable {
    
        private static final long serialVersionUID = -7819607546063963266L;
    
        @Schema(name = "名字")
        private String name;
    
        private Gender gender;
    
        private String cardId;
    
        private long phone:
    
        ...more
    }
    
    

    添加对JWT对token的支持(本步骤可选)

    在添加OpenApiConfig类上添加Components信息:

    private Components components() {
        return new Components()
                .addSecuritySchemes("bearer-key", new SecurityScheme().type(SecurityScheme.Type.HTTP).scheme("bearer").bearerFormat("JWT"));
    }
    

    然后在OpenApi中注册Components:

    @Bean
    public OpenAPI springShopOpenAPI() {
        return new OpenAPI()
                .info(info())
                .components(components())
                .externalDocs(externalDocumentation());
    }
    

    在需要使用Authorization的接口上添加:

    @Operation(security = { @SecurityRequirement(name = "bearer-key") })
    

    swagger-ui

    在浏览器中输入http://127.0.0.1:8443/swagger-ui.html或者http://localhost:8443/swagger-ui/index.html?configUrl=/v3/api-docs/swagger-config

    注: 8443是我个人比较喜欢的web开发的端口占用,一般人使用8080比较多。

    效果

    swagger-ui

    Github

    feat: 从springfox迁移到springdoc以及修复AuthenticationFilter过滤失败的问题

  • 相关阅读:
    Docker+Jenkins持续集成环境(1)使用Docker搭建Jenkins+Docker持续集成环境
    DockOne技术分享(二十):Docker三剑客之Swarm介绍
    最佳实战Docker持续集成图文详解
    Spring Cloud Netflix Eureka源码导读与原理分析
    JVM内存区域的划分(内存结构或者内存模型)
    深入理解JVM(一)——JVM内存模型
    java多线程有哪些实际的应用场景?
    深入理解Java类加载器(1):Java类加载原理解析
    【深入Java虚拟机】之四:类加载机制
    OAuth 2和JWT
  • 原文地址:https://www.cnblogs.com/xiao2/p/15621730.html
Copyright © 2011-2022 走看看