從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過濾失敗的問題


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM