SpringBoot 集成并使用 Swagger

使用 Swagger 和 Spring Boot 集成,快速构建 REST API 文档。这些步骤可以帮助你的 API 更加易于使用和理解,从而提高开发效率。

用 Apifox,节省研发团队的每一分钟

SpringBoot 集成并使用 Swagger

免费使用 Apifox

相关推荐

最新文章

API

一体化协作平台

API 设计

API 文档

API 调试

自动化测试

API Mock

API Hub

立即体验 Apifox
目录

Swagger 是一个规范,是 OpenAPI 规范的前身。Swagger 规范已于 2015 年捐赠给 Linux 基金会后改名为 OpenAPI,并定义最新的规范为 OpenAPI 3.0。所以现在的 Swagger 3.0 就是 OpenAPI 3.0。

通常我们所说的 Swagger 是指由 SmartBear Software 开发维护的一套用于实现 OpenAPI 规范的工具组合名称。Swagger 工具包含开源、免费和商用工具的组合,可用于整个 API 生命周期的开发。

Swagger 倡导的是设计优先的原则, 输出文档。对于这个过程有几种使用习惯,第一种是利用 Swagger Editor 在线编辑 Api 文档, 第二种是先写代码,利用注解或者其他方式自动化地输出文档。大家可以按照自己的使用习惯选择。

SpringBoot 项目集成 Swagger 比较成熟了,其中有两个优秀的开源库 SpringfoxSpringdoc

Springfox 和 Springdoc 都是基于 Spring 框架的 Swagger 实现,旨在帮助开发者更方便地生成 API 文档。下面分别介绍它们的优缺点,以及相应的搜索结果。

Springfox 简介

Springfox 是 Swagger 的一个 Java 实现,它支持生成 Swagger 2 规范的 API 文档。Springfox 提供了一系列注解,通过在 Controller 层的方法上标注这些注解,就能够生成相应的 API 文档。同时,Springfox 还提供了一系列可扩展接口,使开发者可以自定义生成 API 文档的过程,以满足特殊需求。优点在于使用广泛,可以生成Swagger 2 规范的 API 文档。缺点在于其版本更新较慢,与新版本 SpringBoot 的兼容性有待提高。

Springdoc 简介

Springdoc 是一个开源的 API 文档生成框架,支持生成 Swagger 3 规范的 API 文档,与 Spring Boot 深度集成,支持 Spring WebMvc、Spring WebFlux、Spring Data Rest 和 Spring Security 等项目。相比Springfox,Springdoc 的更新速度更快,且可以生成 Swagger 3 规范的 API 文档。此外,Springdoc 还提供了一系列可扩展接口,允许用户自定义生成API文档的过程,缺点在于目前支持的插件相对较少。

Springfox 适用于生成 Swagger 2 规范的 API 文档,并且有一定的可扩展性,但是版本更新较慢。Springdoc 则适用于生成 Swagger 3 规范的 API 文档,并且支持多个 Spring 项目,且更新速度较快,但目前支持的插件较少。

springdoc-openapi 作为一个优秀的 Java 库,它帮助使用 Spring Boot 项目自动化生成 API 文档。它通过在运行时检查应用程序的 Spring 配置、类结构和各种注释来推断 API 语义。

该库自动生成 JSON / YAML 和 HTML 格式的 API 文档,可以使用 swagger-api 注释来补充注释。

此库支持:

  • OpenAPI 3
  • Spring Boot (v1、v2和v3)
  • JSR-303,特别是@NotNull、@Min、@Max和@Size。
  • Swagger-UI
  • OAuth 2
  • GraalVM本地镜像
swagger
Springdoc

3 分钟上手 SpringDoc

Step1. 安装依赖

最新的版本可以从这里看 https://mvnrepository.com/artifact/org.springdoc/springdoc-openapi-ui

<dependency>
   <groupId>org.springdoc</groupId>
   <artifactId>springdoc-openapi-ui</artifactId>
   <version>1.5.3</version>
</dependency>

Step2. 启用 swagger

swagger3的开启和关闭
application.yml
springdoc:api-docs:enabled: true #默认为true开启

Step3. 配置 SwaggerController

更多配置请参看 spring doc propotiest

package com.szh.demo.common.config;

import org.springdoc.core.GroupedOpenApi;
import org.springdoc.core.customizers.OperationCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.method.HandlerMethod;

import io.swagger.v3.oas.annotations.enums.ParameterIn;
import io.swagger.v3.oas.models.ExternalDocumentation;
import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.Operation;
import io.swagger.v3.oas.models.info.Info;
import io.swagger.v3.oas.models.info.License;
import io.swagger.v3.oas.models.media.StringSchema;
import io.swagger.v3.oas.models.parameters.Parameter;

@Configuration
public class Swagger3Config {
        
        //配置一个test-public组
        @Bean
        public GroupedOpenApi publicApi() {
                return GroupedOpenApi.builder()
                                .group("test-public")
                                .packagesToScan("com.szh.demo.material")
//                                .pathsToMatch("/material/**")
                                .addOperationCustomizer(operationCustomizer())
                                .build();
        }
        //配置其他组
        @Bean
        public GroupedOpenApi otherApi() {
                return GroupedOpenApi.builder()
                                .group("test-other")
                                .packagesToScan("com.szh.demo.other")
//                                .pathsToMatch("/other/**")
                                .build();
        }
        
        /**
         * 给所有@Operation注释的接口添加一个tellerno请求头参数
         * @return
         */
        @Bean
        public OperationCustomizer operationCustomizer () {
                return new OperationCustomizer() {
                        
                        @Override
                        public Operation customize(Operation operation, HandlerMethod handlerMethod) {
                                operation.addParametersItem(
                                                new Parameter()
                                                .in(ParameterIn.HEADER.toString())
                                                .name("tellerno")
                                                .description("登录用户账号")
                                                .schema(new StringSchema())
                                                .required(false)
                                                );
                                return operation;
                        }
                };
        }
        
        @Bean
        public OpenAPI openAPI() {
                return new OpenAPI()
                      .info(new Info().title("Swagger3 test API")
                      .description("Swagger3 test sample application")
                      .version("v1.0.0")
                      .license(new License().name("Apache 2.0").url("http://springdoc.org")))
                      .externalDocs(new ExternalDocumentation()
                      .description("SpringShop Wiki Documentation")
                      .url("https://springshop.wiki.github.org/docs"));
    }
    
}

Step4. 实体类的注解

package com.szh.demo.material.entity;

import java.io.Serializable;
import java.time.LocalDateTime;

import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Null;

import com.szh.demo.common.validate.ByteSize;
import com.szh.demo.common.validate.In;

import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Schema(name = "素材表实体", description = "素材表实体description")
public class Material implements Serializable {
        
        public interface Save {}
        private static final long serialVersionUID = 1L;

        @Null(groups = {Save.class}, message = "id必须为null")
        @Schema(description = "主键", example = "0")
        private Long id;
        @Schema(description = "适用机构号")
        private String orgcode;
        
        //@In是自定义的Bean Validation注解,未展示
        @In(groups = {Save.class}, value = {"00", "01", "02"}, message = "素材类型必须是{value}之一")
        @NotBlank(groups = {Save.class}, message = "素材类型不能为空")
        @Schema(description = "素材分类 00图片 01视频 02PDF", allowableValues = {"00", "01", "02"})
        private String materialType;
        
        @Schema(description = "素材类型名称", accessMode = Schema.AccessMode.READ_ONLY)
        private String materialTypeName;
        
        //@ByteSize是自定义的Bean Validation注解,未展示
        @NotBlank(groups = {Save.class}, message = "素材名称不能为空")
        @ByteSize(groups = {Save.class}, min = 1, max = 200, message = "素材名称字节长度必须在{min}-{max}之间")
        @Schema(description = "素材名称")
        private String materialName;
        
        @NotBlank(groups = {Save.class}, message = "素材路径必须包含可见字符")
        @Schema(description = "素材路径")
        private String materialPath;
        
        @Null(groups = {Save.class}, message = "vertical必须为null")
        @Schema(description = "素材方向", accessMode = Schema.AccessMode.READ_ONLY)
        private String vertical;
        
        @Schema(description = "素材方向名称", accessMode = Schema.AccessMode.READ_ONLY)
        private String verticalName;
        
        @Null(groups = {Save.class}, message = "converted必须为null")
        @Schema(description = "视频是否已完成转码", accessMode = Schema.AccessMode.READ_ONLY)
        private String converted;
        
        @Null(groups = {Save.class}, message = "failcount必须为null")
        @Schema(description = "转码失败次数", accessMode = Schema.AccessMode.READ_ONLY)
        private Integer failcount;
        
        @Null(groups = {Save.class}, message = "createId必须为null")
        @Schema(description = "创建用户", accessMode = Schema.AccessMode.READ_ONLY)
        private Long createId;
        
        @Null(groups = {Save.class}, message = "createDatetime必须为null")
        @Schema(description = "创建时间", accessMode = Schema.AccessMode.READ_ONLY)
        private LocalDateTime createDatetime;
        
        @Null(groups = {Save.class}, message = "updateId必须为null")
        @Schema(description = "修改用户", accessMode = Schema.AccessMode.READ_ONLY)
        private Long updateId;
        
        @Null(groups = {Save.class}, message = "updateDatetime必须为null")
        @Schema(description = "修改时间", accessMode = Schema.AccessMode.READ_ONLY)
        private LocalDateTime updateDatetime;
        
}

Step5. Controller 的注解

package com.szh.demo.material.controller;

import java.util.ArrayList;
import java.util.List;

import javax.validation.constraints.NotNull;

import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import com.szh.demo.common.dto.Pagination;
import com.szh.demo.material.entity.Material;

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.tags.Tag;
import lombok.extern.slf4j.Slf4j;

@RestController
@RequestMapping("/material/")
@Tag(name = "素材表", description = "素材表接口")
@Validated
@Slf4j
public class MaterialController {

        @Operation(summary = "保存素材表", description = "保存素材表description")
        @PostMapping("save")
        public void save(@Validated({Material.Save.class}) @RequestBody Material material) {
                log.info(material.toString());
        }

        @Operation(summary = "查询素材表详情", description = "根据主键查询description")
        @Parameter(name = "id", description = "主键", required = true)
        @GetMapping("detail")
        public Material detail(@NotNull(message = "id不能为空") Long id) {
                log.info(id.toString());
                return null;
        }

        @Operation(summary = "删除素材表", description = "根据主键删除description")
        @Parameter(name = "id", description = "主键", required = true)
        @GetMapping("delete")
        public void delete(@NotNull(message = "id不能为空") Long id) {
                log.info(id.toString());
        }

        //@Parameter(in = ParameterIn.QUERY) 表示接口参数不是application/json的形式,而是form-data的形式
        @Operation(summary = "查询素材表列表", description = "基础列表查询,只包含所有属性and.eq条件description")
        @PostMapping("list")
        public List<Material> list(@Parameter(in = ParameterIn.QUERY) Material material, @Parameter(in = ParameterIn.QUERY) @Validated Pagination pagination) {
                log.info(material.toString());
                log.info(pagination.toString());
                return new ArrayList<Material>();
        }
}

Step6. 得到 Swagger 文档链接

http://server:port/context-path/swagger-ui.html
swagger

更好的方式: Apifox Helper

上述步骤可得到 document url 之后, 如果是公网,记得处理鉴权的问题, 毕竟安全很重要。有很多小伙伴说这个 springdoc 简直不能更方便,我就说其实有更好的方式, 使用 Apifox Helper 更方便。你可以在 IDEA 中自动同步 Swagger 注解到 Apifox, 这样就可以迅速分享 API 给其他小伙伴。

Apifox Helper
Apifox Helper

Apifox 是一个提供 API 设计和文档、API 测试和模拟以及 API 协作功能的平台。它可以自动解析代码注释,并基于 Javadoc、KDoc 和 ScalaDoc 生成 API 文档。该平台支持 Spring Boot、Swagger 和 JAX-RS 等协议和框架。通过 IntelliJ IDEA 的 Apifox Helper 插件,开发人员可以在不切换工具的情况下将他们的文档与 Apifox 项目同步。Apifox 还提供了一个浏览和管理 API 的用户界面。该平台为 Web 和桌面应用程序提供了客户端解决方案。

Apifox Helper 插件下载
Apifox Helper 下载

当在 IDEA 项目中有接口信息变动,只需右键点击「 Upload to Apifox」一键即可完成同步,无需奔走相告。 团队成员可在 Apifox 中看到同步后的最新内容。

Apifox Helper 插件 一键同步
Apifox Helper 一键同步

此外,使用 Apifox Helper 可以在 IDEA 中一键发起接口内测,同时支持导出 Markdown 格式文档和 cURL,适应不同团队的协作方式。

现在出发去 JetBrains 插件市场 下载试试吧!,或直接在 IDEA 内 「plugin」入口直接搜索 Apifox Helper。

知识扩展: