本文分享自天翼云开发者社区@《Springfox与SpringDoc——swagger如何选择(SpringDoc入门)》,作者: 才开始学技术的小白

0.引言

之前写过一篇关于swagger(实际上是springfox)的使用指南(https://www.ctyun.cn/developer/article/371704742199365),涵盖了本人在开发与学习的时候碰到的各种大坑。但由于springfox已经不更新了,很多项目都在往springdoc迁移

笔者也是花了一些时间试了一下这个号称“把springfox按在地下摩擦”的springdoc究竟好不好使,本文就来简单介绍下springdoc的使用以及优劣势

1.引入maven依赖

这里有个大坑一定要注意!!!

如果你跟我一样,现在使用的是springfox,但是想往springdoc迁移,结果试了一下发现还是springfox好用/懒得改那么多注解,还是想换回springfox,一定要把springdoc的maven依赖删掉!!!不然springboot会默认你用的是springdoc,导致swagger界面出不来

<dependency>

<groupId>org.springdoc</groupId>

<artifactId>springdoc-openapi-ui</artifactId>

<version>1.6.11</version>

</dependency>

2.springdoc配置类

实际上springdoc的配置非常简单,使用的是OpenAPI类与GroupedOpenApi来配置

/**

* SpringDoc API文档相关配置

* Created by macro on 2023/02/02.

*/@Configurationpublic class SpringDocConfig {

@Bean

public OpenAPI mallTinyOpenAPI() {

return new OpenAPI()

.info(new Info().title("CTYUN API")

.description("SpringDoc API 演示")

.version("v1.0.0")

.license(new License().name("Apache 2.0").url("https://github.com/")))

.externalDocs(new ExternalDocumentation()

.description("SpringBoot项目")

.url("http://www.ctyun.com"));

}

@Bean

public GroupedOpenApi adminApi() {

return GroupedOpenApi.builder()

.group("admin")

.pathsToMatch("/**")

.build();

}

//可以创建不同的GroupedOpenApi来判断不同的controller

@Bean

public GroupedOpenApi userApi() {

return GroupedOpenApi.builder()

.group("user")

.pathsToMatch("/user/**")

.build();

}}

3.启动

默认配置之后直接进入:http://localhost:8080/swagger-ui/index.html 即可

注意这里与springfox略有不同(http://localhost:8080/swagger-ui.html)

4.与SpringFox的注解对照

5.SpringDoc基本注解用法

这里转载一个写的非常全面的示例接口,原文可以去看:https://blog.csdn.net/zhenghongcs/article/details/123812583

/**

* 品牌管理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));

}}

6.与SpringSecurity的结合

如果项目中使用了SpringSecurity,需要做两个配置来让springdoc正常使用:

1.在SpringSecurity配置类中放行白名单:"/v3/api-docs/**", "/swagger-ui/**"

2.在SpringDoc配置中增加对应内容,如下:

@Configurationpublic class SpringDocConfig {

private static final String SECURITY_SCHEME_NAME = "BearerAuth";

@Bean

public OpenAPI managerOpenAPI() {

return new OpenAPI()

.info(new Info().title("Galaxy-Cluster-Manager后端接口文档")

.description("提供给前端界面(portal)的接口文档")

.version("v1.0.0")

.license(new License().name("galaxy 1.2.0").url("https://gitlab.ctyun.cn/hpc/galaxy-parent/-/tree/v1.2.0")))

.externalDocs(new ExternalDocumentation()

.description("弹性高性能计算(CTHPC)")

.url("http://www.ctyun.cn"))

//以下是针对SpringSecurity的设置,同时还有设置白名单

.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")));

}

@Bean

public GroupedOpenApi publicApi() {

return GroupedOpenApi.builder()

.group("portal")

.pathsToMatch("/api/**")

.build();

}

}

7.SpringDoc使用对象作为Query参数的问题

实际上springfox也会有这个问题,使用对象作为query传参的时候,页面通常是这样的:

就没有办法逐个描述参数,也不能逐个调试(只能用json调试),非常的麻烦;springdoc有一个解决这个问题非常方便的注解:@ParameterObject

比如某一个控制器的入参是User user,我们只需要在这前面加上注解变为:@ParameterObject User user 即可,结果如下;

这里也有一个大坑:参数的类型可能会不正确,比如这里我们的id参数实际上是int,但显示出来是string,这个时候就需要我们在User类的属性中加上对应的注解,比如:

@Parameter(description = "id传参",example = "6")

再重启UI就会发现参数类型正确了

8.SpringDoc配置扫包范围

有的时候仅仅使用@Hidden并不能满足我们的需要,因为可能需要配置不同group的controller类,这个时候就需要在配置类中取设置扫包范围代码如下:

9.SpringDoc的优劣势

优势:SpringDoc有着非常好看的UI,以及比Springfox更加完善的参数注解体系,看起来非常舒服,并且还在不断更新与维护中

劣势:一些冷门功能还不完善,比如:

a.有十个接口,他们的url是一样的但是可以通过query参数来分别(如:@PostMapping(params = "action=QueryUsers"))这个时候springdoc只能通过扫包范围配置,来写多个GroupOpenApi来解决,非常的麻烦;springfox可以在docket创建的时候使用:docket.enableUrlTemplating(true); 这个方法即可解决

b.springdoc的网络配置可能会与springfox冲突,如果迁移,需要逐个尝试网络配置是否合适(主要是GsonHttpMessageConverter的配置)

c.兼容性问题仍需要观望,相对于springfox,springdoc的兼容性并没有那么好,在许多时候可能会出现序列化的乱码问题

总结:如果当前项目/工程已经集成了完备的springfox,建议不要轻易尝试迁移到springdoc,尤其是接口类型比较复杂、springfox配置docket比较多的项目;但如果是从头开始的项目,由于接口相对比较简单,可以采用springdoc,毕竟可以获得更加清晰明了的显示界面与参数解释。

Springfox与SpringDoc——swagger如何选择(SpringDoc入门)的更多相关文章

  1. C#中缓存的使用 ajax请求基于restFul的WebApi(post、get、delete、put) 让 .NET 更方便的导入导出 Excel .net core api +swagger(一个简单的入门demo 使用codefirst+mysql) C# 位运算详解 c# 交错数组 c# 数组协变 C# 添加Excel表单控件(Form Controls) C#串口通信程序

    C#中缓存的使用   缓存的概念及优缺点在这里就不多做介绍,主要介绍一下使用的方法. 1.在ASP.NET中页面缓存的使用方法简单,只需要在aspx页的顶部加上一句声明即可:  <%@ Outp ...

  2. 人工智能对人类有哪些影响 选择Python入门怎样

    人工智能对人类有哪些影响?选择Python入门怎样?人工智能是科技时代进步的产物,也是目前人们非常关注的一个产业.那么,随着人工智能的发展,对人类生活的有哪些影响呢? 1.人工智能对文化产业影响 据了 ...

  3. Spring boot中使用springfox来生成Swagger Specification小结

    Rest接口对应Swagger Specification路径获取办法: 根据location的值获取api   json描述文件 也许有同学会问,为什么搞的这么麻烦,api json描述文件不就是h ...

  4. .net core api +swagger(一个简单的入门demo 使用codefirst+mysql)

    前言: 自从.net core问世之后,就一直想了解.但是由于比较懒惰只是断断续续了解一点.近段时间工作不是太忙碌,所以偷闲写下自己学习过程.慢慢了解.net core 等这些基础方面学会之后再用.n ...

  5. 选择适合入门的自动化测试框架TestNG 基于Java语言的入门选择之一

    对于测试工程师新手来说,最痛苦的莫过于入门,其实只要入门3个月左右,对于自动化测试,所有的测试工程师除了喜爱,就是更爱.自动化测试工作,是从根本上解放人性,不用重复去完成鼠标的点点点,例如以下测试常常 ...

  6. Ajax我选择这样入门

    什么是AJAX? AJAX的意思就是异步的JavaScript和XML.简而言之,它是使用XMLHttpRequest对象与服务器端通信的脚本语言.它可以发送及接收各种格式的信息,包括JSON.XML ...

  7. 一步步完成Maven+SpringMVC+SpringFox+Swagger整合示例

    本文给出一个整合Maven+SpringMVC+SpringFOX+Swagger的示例,并且一步步给出完成步骤. 本人在做实例时发现 http://blog.csdn.net/zth1002/art ...

  8. SpringMVC、SpringFox和Swagger整合项目实例

    目标 在做项目的时候,有时候需要提供其它平台(如业务平台)相关的HTTP接口,业务平台则通过开放的HTTP接口获取相关的内容,并完成自身业务~ 提供对外开放HTTP API接口,比较常用的是采用Spr ...

  9. Spring 5 中函数式web开发中的swagger文档

    Spring 5 中一个非常重要的更新就是增加了响应式web开发WebFlux,并且推荐使用函数式风格(RouterFunction和 HandlerFunction)来开发WebFlux.对于之前主 ...

  10. Swagger Annotation 详解(建议收藏)

    转载:https://www.jianshu.com/p/b0b19368e4a8 在软件开发行业,管理文档是件头疼的事.不是文档难于撰写,而是文档难于维护,因为需求与代码会经常变动,尤其在采用敏捷软 ...

随机推荐

  1. What is the Best Python IDE for Data Science?

    Created by Guido van Rossum, Python was first released back in 1991. The interpreted high-level prog ...

  2. 微信小程序通过经纬度计算两点之间距离

    小程序中通过经纬度计算两点之间的距离km 1.拾取两地经纬度坐标 . data:{ //当前定位位置 latitude: null, longitude: null, // 目的地坐标 latitud ...

  3. 前端上传获取excel文件后,如何读取excel文件的内容

    1.安装xlsx npm install xlsx --save-dev 2.引入xlsx并封装读取excel方法 import * as XLSX from "xlsx"; /* ...

  4. PLC入门笔记10

    梯形图电路之顺序控制 顺序控制功能图 顺序控制功能图的梯形图表达 编程原则 实例分析 SMo0.1西门子首次扫描时为ON,常用作初始化脉冲 这是台达的 这是优控的..

  5. 【C++小程序】《我要抽签》b1.0做好了~

    也许是的,得横空出世了 如你所见 这款基于\(C++\)能模仿Mrs. Yao抽签系统的cpp终于做完了啦~ 初期功能很少.\(BUG\)极多. 所以为了您的体验:) 请遵守格式 代码: #inclu ...

  6. docker 配置 anaconda + jupyter notebook 纪要

    首先,pull官方的镜像,名字叫:continuumio/anaconda3 首先的首先,如果默认还是需要sudo运行docker命令,可以考虑把当前用户加入到docker组(usermod -aG ...

  7. C语言初级阶段5——函数1

    C语言初级阶段5--函数1 函数的基本概念 1.函数:理解为封装功能的容器. 主函数是函数的入口 2.函数定义的基本格式: 返回值类型:常用的基本数据类型,执行完以后,函数会得到一个什么类型的值,如果 ...

  8. Nginx重启操作

    1.杀掉Nginx进程 killall nginx 2.启动Nginx /usr/local/openresty/nginx/sbin/nginx -c /usr/local/openresty/ng ...

  9. stm32 USART

    stm32 USART 1 USART基础知识 三种模式:轮询模式.中断模式.DMA模式 轮询模式属于阻塞模式 中断模式和DMA模式属于非阻塞模式 发送数据 接收数据 1.1 Polling mode ...

  10. 新搭建的禅道admin忘记密码

    /opt/zbox/run/mysql/mysql -uroot -p 禅道数据库root默认密码123456 MariaDB [(none)]> show databases; +------ ...