• 神器 SpringDoc 横空出世!最适合 SpringBoot 的API文档工具来了


    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中添加它的依赖即可,开箱即用,无需任何配置。
    1. <!--springdoc 官方Starter-->
    2. <dependency>
    3. <groupId>org.springdoc</groupId>
    4. <artifactId>springdoc-openapi-ui</artifactId>
    5. <version>1.6.6</version>
    6. </dependency>

    从SpringFox迁移

    • 我们先来看下经常使用的Swagger注解,看看SpringFox的和SpringDoc的有啥区别,毕竟对比已学过的技术能该快掌握新技术;

    • 接下来我们对之前Controller中使用的注解进行改造,对照上表即可,之前在@Api注解中被废弃了好久又没有替代的description属性终于被支持了!
    1. /**
    2. * 品牌管理Controller
    3. * Created by macro on 2019/4/19.
    4. */
    5. @Tag(name = "PmsBrandController", description = "商品品牌管理")
    6. @Controller
    7. @RequestMapping("/brand")
    8. public class PmsBrandController {
    9. @Autowired
    10. private PmsBrandService brandService;
    11. private static final Logger LOGGER = LoggerFactory.getLogger(PmsBrandController.class);
    12. @Operation(summary = "获取所有品牌列表",description = "需要登录后访问")
    13. @RequestMapping(value = "listAll", method = RequestMethod.GET)
    14. @ResponseBody
    15. public CommonResult> getBrandList() {
    16. return CommonResult.success(brandService.listAllBrand());
    17. }
    18. @Operation(summary = "添加品牌")
    19. @RequestMapping(value = "/create", method = RequestMethod.POST)
    20. @ResponseBody
    21. @PreAuthorize("hasRole('ADMIN')")
    22. public CommonResult createBrand(@RequestBody PmsBrand pmsBrand) {
    23. CommonResult commonResult;
    24. int count = brandService.createBrand(pmsBrand);
    25. if (count == 1) {
    26. commonResult = CommonResult.success(pmsBrand);
    27. LOGGER.debug("createBrand success:{}", pmsBrand);
    28. } else {
    29. commonResult = CommonResult.failed("操作失败");
    30. LOGGER.debug("createBrand failed:{}", pmsBrand);
    31. }
    32. return commonResult;
    33. }
    34. @Operation(summary = "更新指定id品牌信息")
    35. @RequestMapping(value = "/update/{id}", method = RequestMethod.POST)
    36. @ResponseBody
    37. @PreAuthorize("hasRole('ADMIN')")
    38. public CommonResult updateBrand(@PathVariable("id") Long id, @RequestBody PmsBrand pmsBrandDto, BindingResult result) {
    39. CommonResult commonResult;
    40. int count = brandService.updateBrand(id, pmsBrandDto);
    41. if (count == 1) {
    42. commonResult = CommonResult.success(pmsBrandDto);
    43. LOGGER.debug("updateBrand success:{}", pmsBrandDto);
    44. } else {
    45. commonResult = CommonResult.failed("操作失败");
    46. LOGGER.debug("updateBrand failed:{}", pmsBrandDto);
    47. }
    48. return commonResult;
    49. }
    50. @Operation(summary = "删除指定id的品牌")
    51. @RequestMapping(value = "/delete/{id}", method = RequestMethod.GET)
    52. @ResponseBody
    53. @PreAuthorize("hasRole('ADMIN')")
    54. public CommonResult deleteBrand(@PathVariable("id") Long id) {
    55. int count = brandService.deleteBrand(id);
    56. if (count == 1) {
    57. LOGGER.debug("deleteBrand success :id={}", id);
    58. return CommonResult.success(null);
    59. } else {
    60. LOGGER.debug("deleteBrand failed :id={}", id);
    61. return CommonResult.failed("操作失败");
    62. }
    63. }
    64. @Operation(summary = "分页查询品牌列表")
    65. @RequestMapping(value = "/list", method = RequestMethod.GET)
    66. @ResponseBody
    67. @PreAuthorize("hasRole('ADMIN')")
    68. public CommonResult> listBrand(@RequestParam(value = "pageNum", defaultValue = "1")
    69. @Parameter(description = "页码") Integer pageNum,
    70. @RequestParam(value = "pageSize", defaultValue = "3")
    71. @Parameter(description = "每页数量") Integer pageSize) {
    72. List brandList = brandService.listBrand(pageNum, pageSize);
    73. return CommonResult.success(CommonPage.restPage(brandList));
    74. }
    75. @Operation(summary = "获取指定id的品牌详情")
    76. @RequestMapping(value = "/{id}", method = RequestMethod.GET)
    77. @ResponseBody
    78. @PreAuthorize("hasRole('ADMIN')")
    79. public CommonResult brand(@PathVariable("id") Long id) {
    80. return CommonResult.success(brandService.getBrand(id));
    81. }
    82. }
    • 接下来进行SpringDoc的配置,使用OpenAPI来配置基础的文档信息,通过GroupedOpenApi配置分组的API文档,SpringDoc支持直接使用接口路径进行配置。
    1. /**
    2. * SpringDoc API文档相关配置
    3. * Created by macro on 2022/3/4.
    4. */
    5. @Configuration
    6. public class SpringDocConfig {
    7. @Bean
    8. public OpenAPI mallTinyOpenAPI() {
    9. return new OpenAPI()
    10. .info(new Info().title("Mall-Tiny API")
    11. .description("SpringDoc API 演示")
    12. .version("v1.0.0")
    13. .license(new License().name("Apache 2.0").url("https://github.com/macrozheng/mall-learning")))
    14. .externalDocs(new ExternalDocumentation()
    15. .description("SpringBoot实战电商项目mall(50K+Star)全套文档")
    16. .url("http://www.macrozheng.com"));
    17. }
    18. @Bean
    19. public GroupedOpenApi publicApi() {
    20. return GroupedOpenApi.builder()
    21. .group("brand")
    22. .pathsToMatch("/brand/**")
    23. .build();
    24. }
    25. @Bean
    26. public GroupedOpenApi adminApi() {
    27. return GroupedOpenApi.builder()
    28. .group("admin")
    29. .pathsToMatch("/admin/**")
    30. .build();
    31. }
    32. }

    结合SpringSecurity使用

    • 由于我们的项目集成了SpringSecurity,需要通过JWT认证头进行访问,我们还需配置好SpringDoc的白名单路径,主要是Swagger的资源路径;
    1. /**
    2. * SpringSecurity的配置
    3. * Created by macro on 2018/4/26.
    4. */
    5. @Configuration
    6. @EnableWebSecurity
    7. @EnableGlobalMethodSecurity(prePostEnabled = true)
    8. public class SecurityConfig extends WebSecurityConfigurerAdapter {
    9. @Override
    10. protected void configure(HttpSecurity httpSecurity) throws Exception {
    11. httpSecurity.csrf()// 由于使用的是JWT,我们这里不需要csrf
    12. .disable()
    13. .sessionManagement()// 基于token,所以不需要session
    14. .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
    15. .and()
    16. .authorizeRequests()
    17. .antMatchers(HttpMethod.GET, // Swagger的资源路径需要允许访问
    18. "/",
    19. "/swagger-ui.html",
    20. "/swagger-ui/",
    21. "/*.html",
    22. "/favicon.ico",
    23. "/**/*.html",
    24. "/**/*.css",
    25. "/**/*.js",
    26. "/swagger-resources/**",
    27. "/v3/api-docs/**"
    28. )
    29. .permitAll()
    30. .antMatchers("/admin/login")// 对登录注册要允许匿名访问
    31. .permitAll()
    32. .antMatchers(HttpMethod.OPTIONS)//跨域请求会先进行一次options请求
    33. .permitAll()
    34. .anyRequest()// 除上面外的所有请求全部需要鉴权认证
    35. .authenticated();
    36. }
    37. }
    • 然后在OpenAPI对象中通过addSecurityItem方法和SecurityScheme对象,启用基于JWT的认证功能。
    1. /**
    2. * SpringDoc API文档相关配置
    3. * Created by macro on 2022/3/4.
    4. */
    5. @Configuration
    6. public class SpringDocConfig {
    7. private static final String SECURITY_SCHEME_NAME = "BearerAuth";
    8. @Bean
    9. public OpenAPI mallTinyOpenAPI() {
    10. return new OpenAPI()
    11. .info(new Info().title("Mall-Tiny API")
    12. .description("SpringDoc API 演示")
    13. .version("v1.0.0")
    14. .license(new License().name("Apache 2.0").url("https://github.com/macrozheng/mall-learning")))
    15. .externalDocs(new ExternalDocumentation()
    16. .description("SpringBoot实战电商项目mall(50K+Star)全套文档")
    17. .url("http://www.macrozheng.com"))
    18. .addSecurityItem(new SecurityRequirement().addList(SECURITY_SCHEME_NAME))
    19. .components(new Components()
    20. .addSecuritySchemes(SECURITY_SCHEME_NAME,
    21. new SecurityScheme()
    22. .name(SECURITY_SCHEME_NAME)
    23. .type(SecurityScheme.Type.HTTP)
    24. .scheme("bearer")
    25. .bearerFormat("JWT")));
    26. }
    27. }

    测试

    • 接下来启动项目就可以访问Swagger界面了,访问地址:http://localhost:8088/swagger-ui.html

    • 我们先通过登录接口进行登录,可以发现这个版本的Swagger返回结果是支持高亮显示的,版本明显比SpringFox来的新;

    • 然后通过认证按钮输入获取到的认证头信息,注意这里不用加bearer前缀;

    • 之后我们就可以愉快地访问需要登录认证的接口了;

    • 看一眼请求参数的文档说明,还是熟悉的Swagger样式!

    常用配置

    SpringDoc还有一些常用的配置可以了解下,更多配置可以参考官方文档。

    1. springdoc:
    2. swagger-ui:
    3. # 修改Swagger UI路径
    4. path: /swagger-ui.html
    5. # 开启Swagger UI界面
    6. enabled: true
    7. api-docs:
    8. # 修改api-docs路径
    9. path: /v3/api-docs
    10. # 开启api-docs
    11. enabled: true
    12. # 配置需要生成接口文档的扫描包
    13. packages-to-scan: com.macro.mall.tiny.controller
    14. # 配置需要生成接口文档的接口路径
    15. paths-to-match: /brand/**,/admin/**

    总结

    在SpringFox的Swagger库好久不出新版的情况下,迁移到SpringDoc确实是一个更好的选择。今天体验了一把SpringDoc,确实很好用,和之前熟悉的用法差不多,学习成本极低。而且SpringDoc能支持WebFlux之类的项目,功能也更加强大,使用SpringFox有点卡手的朋友可以迁移到它试试!

  • 相关阅读:
    H - XYZZY(spfa最大路径,判断正环)
    C++ 练气期之一文看懂字符串
    数据分享|函数型数据分析部分省市新冠疫情数据
    第八章 通过 REST 使用 Web 会话(Sessions)
    密码学之对称加密体系(2):AES、SM4的 S 盒具体算法的实现
    Q-learning算法实战
    华为OD机试真题【不含 101 的数】
    插入排序(学习笔记)
    Android切圆角的几种方式
    CSS3基础:background背景
  • 原文地址:https://blog.csdn.net/mfmfmfo/article/details/126656229