O artefato SpringDoc nasceu! A melhor ferramenta de documentação de API para SpringBoot está aqui!

A biblioteca Swagger fornecida pelo SpringFox já foi usada no projeto SpringBoot. Fui ao site oficial e descobri que não há uma nova versão há quase dois anos! Eu atualizei a versão SpringBoot 2.6.x alguns dias atrás , e descobri que a compatibilidade desta biblioteca está cada vez pior.Alguns atributos de anotação comuns foram abandonados e nenhuma substituição é fornecida! Encontrei outra biblioteca Swagger por acaso SpringDoc, e foi muito bom experimentá-la, recomendo a todos!

Endereço real do projeto de e-commerce SpringBoot (50k + estrelas): github.com/macrozheng/…

Introdução ao SpringDoc

SpringDoc é uma ferramenta de geração de documentos da API que pode ser usada em conjunto com SpringBoot. Com base OpenAPI 3nele, está atualmente disponível no Github. 1.7K+StarÉ bastante diligente para atualizar e liberar a versão, e é uma biblioteca Swagger melhor! Vale ressaltar que SpringDoc não só suporta projetos Spring WebMvc, mas também projetos Spring WebFlux, e até mesmo projetos Spring Rest e Spring Native, enfim, é muito poderoso, segue um diagrama de arquitetura do SpringDoc.

usar

A seguir, vamos apresentar o uso do SpringDoc, usando o mall-tiny-swaggerprojeto que antes integrava o SpringFox, vou transformá-lo para usar o SpringDoc.

integrado

Em primeiro lugar, temos que integrar o SpringDoc e pom.xmladicionar suas dependências a ele. Ele funciona fora da caixa sem nenhuma configuração.

<!--springdoc 官方Starter-->
<dependency>
    <groupId>org.springdoc</groupId>
    <artifactId>springdoc-openapi-ui</artifactId>
    <version>1.6.6</version>
</dependency>
复制代码

Migrando do SpringFox

  • Vamos primeiro dar uma olhada nas anotações Swagger usadas com frequência para ver a diferença entre SpringFox e SpringDoc. Afinal, devemos dominar novas tecnologias rapidamente comparando as tecnologias que aprendemos;
SpringFox SpringDocName
@Api @Marcação
@ApiIgnore @Parameter(hidden = true)or@Operation(hidden = true)or@Hidden
@ApiImplicitParam @Parameter
@ApiImplicitParams @Parameters
@ApiModel @Schema
@ApiModelProperty @Schema
@ApiOperation(value = "foo", notes = "bar") @Operation(summary = "foo", description = "bar")
@ApiParam @Parameter
@ApiResponse(code = 404, message = "foo") ApiResponse(responseCode = "404", description = "foo")
  • 接下来我们对之前Controller中使用的注解进行改造,对照上表即可,之前在@Api注解中被废弃了好久又没有替代的description属性终于被支持了!
/**
 * 品牌管理Controller
 * Created by macro on 2019/4/19.
 */
@Tag(name = "PmsBrandController", description = "商品品牌管理")
@Controller
@RequestMapping("/brand")
public class PmsBrandController {
    @Autowired
    private PmsBrandService brandService;

    private static final Logger LOGGER = LoggerFactory.getLogger(PmsBrandController.class);

    @Operation(summary = "获取所有品牌列表",description = "需要登录后访问")
    @RequestMapping(value = "listAll", method = RequestMethod.GET)
    @ResponseBody
    public CommonResult<List<PmsBrand>> getBrandList() {
        return CommonResult.success(brandService.listAllBrand());
    }

    @Operation(summary = "添加品牌")
    @RequestMapping(value = "/create", method = RequestMethod.POST)
    @ResponseBody
    @PreAuthorize("hasRole('ADMIN')")
    public CommonResult createBrand(@RequestBody PmsBrand pmsBrand) {
        CommonResult commonResult;
        int count = brandService.createBrand(pmsBrand);
        if (count == 1) {
            commonResult = CommonResult.success(pmsBrand);
            LOGGER.debug("createBrand success:{}", pmsBrand);
        } else {
            commonResult = CommonResult.failed("操作失败");
            LOGGER.debug("createBrand failed:{}", pmsBrand);
        }
        return commonResult;
    }

    @Operation(summary = "更新指定id品牌信息")
    @RequestMapping(value = "/update/{id}", method = RequestMethod.POST)
    @ResponseBody
    @PreAuthorize("hasRole('ADMIN')")
    public CommonResult updateBrand(@PathVariable("id") Long id, @RequestBody PmsBrand pmsBrandDto, BindingResult result) {
        CommonResult commonResult;
        int count = brandService.updateBrand(id, pmsBrandDto);
        if (count == 1) {
            commonResult = CommonResult.success(pmsBrandDto);
            LOGGER.debug("updateBrand success:{}", pmsBrandDto);
        } else {
            commonResult = CommonResult.failed("操作失败");
            LOGGER.debug("updateBrand failed:{}", pmsBrandDto);
        }
        return commonResult;
    }

    @Operation(summary = "删除指定id的品牌")
    @RequestMapping(value = "/delete/{id}", method = RequestMethod.GET)
    @ResponseBody
    @PreAuthorize("hasRole('ADMIN')")
    public CommonResult deleteBrand(@PathVariable("id") Long id) {
        int count = brandService.deleteBrand(id);
        if (count == 1) {
            LOGGER.debug("deleteBrand success :id={}", id);
            return CommonResult.success(null);
        } else {
            LOGGER.debug("deleteBrand failed :id={}", id);
            return CommonResult.failed("操作失败");
        }
    }

    @Operation(summary = "分页查询品牌列表")
    @RequestMapping(value = "/list", method = RequestMethod.GET)
    @ResponseBody
    @PreAuthorize("hasRole('ADMIN')")
    public CommonResult<CommonPage<PmsBrand>> listBrand(@RequestParam(value = "pageNum", defaultValue = "1")
                                                        @Parameter(description = "页码") Integer pageNum,
                                                        @RequestParam(value = "pageSize", defaultValue = "3")
                                                        @Parameter(description = "每页数量") Integer pageSize) {
        List<PmsBrand> brandList = brandService.listBrand(pageNum, pageSize);
        return CommonResult.success(CommonPage.restPage(brandList));
    }

    @Operation(summary = "获取指定id的品牌详情")
    @RequestMapping(value = "/{id}", method = RequestMethod.GET)
    @ResponseBody
    @PreAuthorize("hasRole('ADMIN')")
    public CommonResult<PmsBrand> brand(@PathVariable("id") Long id) {
        return CommonResult.success(brandService.getBrand(id));
    }
}
复制代码
  • 接下来进行SpringDoc的配置,使用OpenAPI来配置基础的文档信息,通过GroupedOpenApi配置分组的API文档,SpringDoc支持直接使用接口路径进行配置。
/**
 * SpringDoc API文档相关配置
 * Created by macro on 2022/3/4.
 */
@Configuration
public class SpringDocConfig {
    @Bean
    public OpenAPI mallTinyOpenAPI() {
        return new OpenAPI()
                .info(new Info().title("Mall-Tiny API")
                        .description("SpringDoc API 演示")
                        .version("v1.0.0")
                        .license(new License().name("Apache 2.0").url("https://github.com/macrozheng/mall-learning")))
                .externalDocs(new ExternalDocumentation()
                        .description("SpringBoot实战电商项目mall(50K+Star)全套文档")
                        .url("http://www.macrozheng.com"));
    }

    @Bean
    public GroupedOpenApi publicApi() {
        return GroupedOpenApi.builder()
                .group("brand")
                .pathsToMatch("/brand/**")
                .build();
    }

    @Bean
    public GroupedOpenApi adminApi() {
        return GroupedOpenApi.builder()
                .group("admin")
                .pathsToMatch("/admin/**")
                .build();
    }
}
复制代码

结合SpringSecurity使用

  • 由于我们的项目集成了SpringSecurity,需要通过JWT认证头进行访问,我们还需配置好SpringDoc的白名单路径,主要是Swagger的资源路径;
/**
 * SpringSecurity的配置
 * Created by macro on 2018/4/26.
 */
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {                                              

    @Override
    protected void configure(HttpSecurity httpSecurity) throws Exception {
        httpSecurity.csrf()// 由于使用的是JWT,我们这里不需要csrf
                .disable()
                .sessionManagement()// 基于token,所以不需要session
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
                .authorizeRequests()
                .antMatchers(HttpMethod.GET, // Swagger的资源路径需要允许访问
                        "/",   
                        "/swagger-ui.html",
                        "/swagger-ui/",
                        "/*.html",
                        "/favicon.ico",
                        "/**/*.html",
                        "/**/*.css",
                        "/**/*.js",
                        "/swagger-resources/**",
                        "/v3/api-docs/**"
                )
                .permitAll()
                .antMatchers("/admin/login")// 对登录注册要允许匿名访问
                .permitAll()
                .antMatchers(HttpMethod.OPTIONS)//跨域请求会先进行一次options请求
                .permitAll()
                .anyRequest()// 除上面外的所有请求全部需要鉴权认证
                .authenticated();
        
    }

}
复制代码
  • 然后在OpenAPI对象中通过addSecurityItem方法和SecurityScheme对象,启用基于JWT的认证功能。
/**
 * SpringDoc API文档相关配置
 * Created by macro on 2022/3/4.
 */
@Configuration
public class SpringDocConfig {
    private static final String SECURITY_SCHEME_NAME = "BearerAuth";
    @Bean
    public OpenAPI mallTinyOpenAPI() {
        return new OpenAPI()
                .info(new Info().title("Mall-Tiny API")
                        .description("SpringDoc API 演示")
                        .version("v1.0.0")
                        .license(new License().name("Apache 2.0").url("https://github.com/macrozheng/mall-learning")))
                .externalDocs(new ExternalDocumentation()
                        .description("SpringBoot实战电商项目mall(50K+Star)全套文档")
                        .url("http://www.macrozheng.com"))
                .addSecurityItem(new SecurityRequirement().addList(SECURITY_SCHEME_NAME))
                .components(new Components()
                                .addSecuritySchemes(SECURITY_SCHEME_NAME,
                                        new SecurityScheme()
                                                .name(SECURITY_SCHEME_NAME)
                                                .type(SecurityScheme.Type.HTTP)
                                                .scheme("bearer")
                                                .bearerFormat("JWT")));
    }
}
复制代码

测试

  • Nós primeiro efetuamos login através da interface de login e podemos descobrir que esta versão do Swagger retorna resultados que suportam realce, e a versão é obviamente mais recente que SpringFox;

  • Em seguida, insira as informações de cabeçalho de autenticação obtidas através do botão de autenticação, observe que nenhum bearerprefixo é necessário aqui;

  • Depois disso, podemos acessar a interface que requer autenticação de login;

  • Dê uma olhada na documentação dos parâmetros de solicitação, o familiar estilo Swagger!

Colocação regular

SpringDoc também tem algumas configurações comuns que você pode aprender.Para mais configurações, você pode consultar a documentação oficial.

springdoc:
  swagger-ui:
    # 修改Swagger UI路径
    path: /swagger-ui.html
    # 开启Swagger UI界面
    enabled: true
  api-docs:
    # 修改api-docs路径
    path: /v3/api-docs
    # 开启api-docs
    enabled: true
  # 配置需要生成接口文档的扫描包
  packages-to-scan: com.macro.mall.tiny.controller
  # 配置需要生成接口文档的接口路径
  paths-to-match: /brand/**,/admin/**
复制代码

Resumir

Caso a biblioteca Swagger do SpringFox não seja lançada há muito tempo, migrar para o SpringDoc é realmente uma escolha melhor. Experimentei um SpringDoc hoje. É realmente fácil de usar. É semelhante ao uso familiar antes, e o custo de aprendizado é extremamente baixo. Além disso, SpringDoc pode suportar projetos como WebFlux, e suas funções são mais poderosas. Amigos que estão um pouco presos ao SpringFox podem migrar para ele e experimentar!

Se você quiser saber mais habilidades de combate do SpringBoot, experimente este projeto prático com um conjunto completo de tutoriais (50K+Star): github.com/macrozheng/…

Referências

Endereço do código-fonte do projeto

github.com/macrozheng/…

Acho que você gosta

Origin juejin.im/post/7080328458206707720
Recomendado
Clasificación