乐趣区

关于java:神器-SpringDoc-横空出世最适合-SpringBoot-的API文档工具来了

之前在 SpringBoot 我的项目中始终应用的是 SpringFox 提供的 Swagger 库,上了下官网发现曾经有靠近两年没出新版本了!前几天降级了 SpringBoot 2.6.x 版本,发现这个库的兼容性也越来越不好了,有的罕用注解属性被废除了竟然都没提供代替!无心中发现了另一款 Swagger 库SpringDoc,试用了一下十分不错,举荐给大家!

SpringBoot 实战电商我的项目 mall(50k+star)地址:https://github.com/macrozheng/mall

SpringDoc 简介

SpringDoc 是一款能够联合 SpringBoot 应用的 API 文档生成工具,基于OpenAPI 3,目前在 Github 上已有1.7K+Star,更新发版还是挺勤快的,是一款更好用的 Swagger 库!值得一提的是 SpringDoc 不仅反对 Spring WebMvc 我的项目,还能够反对 Spring WebFlux 我的项目,甚至 Spring Rest 和 Spring Native 我的项目,总之十分弱小,上面是一张 SpringDoc 的架构图。

应用

接下来咱们介绍下 SpringDoc 的应用,应用的是之前集成 SpringFox 的 mall-tiny-swagger 我的项目,我将把它革新成应用 SpringDoc。

集成

首先咱们得集成 SpringDoc,在 pom.xml 中增加它的依赖即可,开箱即用,无需任何配置。

<!--springdoc 官网 Starter-->
<dependency>
    <groupId>org.springdoc</groupId>
    <artifactId>springdoc-openapi-ui</artifactId>
    <version>1.6.6</version>
</dependency>

从 SpringFox 迁徙

  • 咱们先来看下常常应用的 Swagger 注解,看看 SpringFox 的和 SpringDoc 的有啥区别,毕竟比照已学过的技术能该快把握新技术;
SpringFox SpringDoc
@Api @Tag
@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")));
    }
}

测试

  • 接下来启动我的项目就能够拜访 Swagger 界面了,拜访地址:http://localhost:8088/swagger…

  • 咱们先通过登录接口进行登录,能够发现这个版本的 Swagger 返回后果是反对高亮显示的,版本显著比 SpringFox 来的新;

  • 而后通过认证按钮输出获取到的认证头信息,留神这里不必加 bearer 前缀;

  • 之后咱们就能够欢快地拜访须要登录认证的接口了;

  • 看一眼申请参数的文档阐明,还是相熟的 Swagger 款式!

罕用配置

SpringDoc 还有一些罕用的配置能够理解下,更多配置能够参考官网文档。

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/**

总结

在 SpringFox 的 Swagger 库良久不出新版的状况下,迁徙到 SpringDoc 的确是一个更好的抉择。明天体验了一把 SpringDoc,的确很好用,和之前相熟的用法差不多,学习老本极低。而且 SpringDoc 能反对 WebFlux 之类的我的项目,性能也更加弱小,应用 SpringFox 有点卡手的敌人能够迁徙到它试试!

如果你想理解更多 SpringBoot 实战技巧的话,能够试试这个带全套教程的实战我的项目(50K+Star):https://github.com/macrozheng/mall

参考资料

  • 我的项目地址:https://github.com/springdoc/…
  • 官网文档:https://springdoc.org/

我的项目源码地址

https://github.com/macrozheng…

退出移动版