Spring Boot之整合Swagger

前言

经过之前文章的学习,我们学会了:

有了这些基础知识,可以说,我们已经拥有Spring Boot项目开发的入门基础了。

现实场景中,我们往往需要知道API的文档,才能用于测试、交付等,土一点的做法是开发人员编写API文档,如Wiki、Word、其他在线文档等,实际上,有很多开源资源帮我们从技术上实现了API规格的文档化,而Swagger是Spring Boot生态中的佼佼者,也是官方推荐的。

Swagger的主要优点有:

  1. 免费开源,并且官网提供了丰富的工具和文档介绍;
  2. 通过代码添加注解,自动生成API文档;
  3. 提供在线API文档,API文档随API变化而自动同步更新;
  4. 可开启UI界面,界面美观、清晰;
  5. UI界面支持在线测试API;
  6. 支持多种语言(Java,PHP...);
  7. 采用OpenAPI 规范;
    (OpenAPI定义了RESTful APIs的编写规范。规范中指定了一套模式/模型。这套规范是Swagger为各种编程语言编写的服务提供接口文档奠定了基础。)
  8. ...

那么,我们今天就一起来学习:

Spring Boot中通过Swagger来做API文档。

本文使用的项目代码,是基于上一篇文章使用的Spring Boot项目:
Spring Boot数据库交互之Mybatis

主要步骤

  1. 添加依赖;
  2. 创建config包;
  3. 创建配置类;
  4. 编写配置类;
  5. 修饰Controller;
  6. 修饰项目启动类;
  7. 启动项目、使用Swagger;

1.添加依赖;

//在pom.xml文件中的dependencies节点内添加以下内容:
<dependency>
        <groupId>io.springfox</groupId>
        <artifactId>springfox-swagger2</artifactId>
        <version>2.9.2</version>
</dependency>
<dependency>
        <groupId>io.springfox</groupId>
        <artifactId>springfox-swagger-ui</artifactId>
        <version>2.9.2</version>
</dependency>

2.创建config包;

项目根目录下创建config包:

config包

3.创建配置类;

配置类

4.编写配置类;

1. 编写Swagger配置类;

该步骤的意义:

配置Swagger要扫描API的Controller位置、要显示的API path的样式、初始化的Swagger页面的一些基础信息(见apiInfo()方法);

  • 特别注意,要使用@EnableSwagger2注解;

  • 设置生成API接口信息的被扫描Controller路径,常见写法:

//生成指定包下的所有API接口信息
RequestHandlerSelectors.basePackage("com.mycompany.sample.controller")
//生成所有API接口
RequestHandlerSelectors.any()
//所有API接口都不生成
RequestHandlerSelectors.none()
//只生成被@Api 这个注解注解过的类接口
RequestHandlerSelectors.withClassAnnotation(Api.class)
//只生成被@ApiOperation 这个注解注解过的API接口
RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class)
  • 设置API路径筛选规则,常见写法有:

//所有API均允许生成
PathSelectors.any()
//所有API均不允许生成
PathSelectors.none()
//正则表达式匹配,匹配上则允许生成,如包含getLead的API:
PathSelectors.regex("^(?!getLead).*$")
//只扫描指定的路径下的请求,如只想扫描getLead API路径下的:
//getLead为API path的一部分
PathSelectors.ant("/getLead/**")

代码如下:

package com.mycompany.sample.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Contact;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;

/**
 * @author : dylanz
 * @since : 07/17/2020
 **/
@Configuration
@EnableSwagger2
public class SwaggerConfig {
    @Bean
    public Docket api() {//名字不限
        return new Docket(DocumentationType.SWAGGER_2)
                .apiInfo(apiInfo())
                .enable(true)
                .select()
                .apis(RequestHandlerSelectors.basePackage("com.mycompany.sample.controller"))
                .paths(PathSelectors.any())
                .build();
    }

    private ApiInfo apiInfo() {//名字不限
        Contact contact = new Contact("Dylan Zhang", "https://www.jianshu.com/u/56578a39a99a", "997604787@qq.com");
        return new ApiInfoBuilder()
                .title("Lead API")
                .contact(contact)
                .description("Lead related operations")
                .termsOfServiceUrl("http://127.0.0.1:8080/getLead/10130546")
                .version("1.0")
                .build();
    }
}
  • 如果想关闭Swagger UI:

enable设置为:.enable(false),之后访问Swagger UI时就会如下:

关闭Swagger UI

2. 编写WebMvcConfig配置类;

该步骤的意义:覆盖、重写项目配置的(如有)、默认的静态资源路径,这样才能读取到Swagger jar包内的静态文件(html、css文件等);

package com.mycompany.sample.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;

import javax.annotation.PostConstruct;

/**
 * @author : dylanz
 * @since : 07/17/2020
 **/
@Configuration
public class WebMvcConfig extends WebMvcConfigurationSupport {
    @PostConstruct
    public void init() {
        System.out.println("Init in WebMvcConfig...");
    }

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/**")
                .addResourceLocations("classpath:/statics/");
        registry.addResourceHandler("swagger-ui.html")
                .addResourceLocations("classpath:/META-INF/resources/");
        registry.addResourceHandler("/webjars/**")
                .addResourceLocations("classpath:/META-INF/resources/webjars/");
    }
}

5.修饰Controller;

package com.mycompany.sample.controller;

import com.mycompany.sample.domain.Lead;
import com.mycompany.sample.service.LeadService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

/**
 * @author : dylanz
 * @since : 07/07/2020
 **/
@RestController
@Api
public class LeadController {
    @Autowired
    private LeadService leadService;

    @GetMapping("/getLead/{leadId}")
    @ResponseBody
    @ApiOperation(value = "get lead by leadId", httpMethod = "GET")
    @ApiImplicitParams(
            {@ApiImplicitParam(name = "leadId", value = "leadId", required = true, paramType = "path", dataType = "Long", example = "10130546")}
    )
    public Lead getLead(@PathVariable(name = "leadId") Long leadId) {
        return leadService.getLeadByLeadId(leadId);
    }
}
1. @Api 注解用来描述该服务的信息,如果不使用则显示类名称;
2. @ApiOperation 注解用于描述接口信息;
3. @ApiParam,@ApiImplicitParam,@ApiImplicitParams 注解用于描述接口的参数;
4. @ApiResponse,@ApiResponses 注解用于描述API返回的信息,不使用会使用默认的、根据接口情况自动生成的描述信息,可在此处自定义错误返回信息,如:

@ApiResponse(code = 400, message = "Bad Request", response = ErrorResponse.class),

5. @ApiVersion 注解用于描述接口的版本信息;

6.修饰项目启动类;

特别注意,只增加使用@EnableSwagger2注解即可;

package com.mycompany.sample;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import springfox.documentation.swagger2.annotations.EnableSwagger2;

/**
 * @author : dylanz
 * @since : 07/07/2020
 **/
@SpringBootApplication
@EnableSwagger2
@MapperScan(basePackages = "com.mycompany.sample.dao")
public class App {
    public static void main(String[] args) {
        SpringApplication.run(App.class, args);
    }
}

7.启动项目、使用Swagger;

  • 启动项目;

启动项目
  • 访问Swagger UI;

Swagger UI HTML站点:http://localhost:8080/swagger-ui.html

HTML站点

页面组成:

1. 标题;
2. API文档JSON入口:http://localhost:8080/v2/api-docs
3. 作者信息;
4. API文档主体;
5. 等。

打开折叠的API列表,我们能看到API的规格:

API规格

以及API使用的实体对象信息:

实体对象信息

我们还可以编辑实体类,修改、细化实体的展示:

package com.mycompany.sample.domain;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;

import java.io.Serializable;

/**
 * @author : dylanz
 * @since : 07/07/2020
 **/
@Data
@JsonIgnoreProperties(ignoreUnknown = true)
@ApiModel
public class Lead implements Serializable {
    private static final long serialVersionUID = 1L;

    @ApiModelProperty(value = "leadId", dataType = "Long", name = "id", example = "10130546")
    private Long leadId;
    @ApiModelProperty(value = "email", dataType = "String", name = "email", example = "dylan.zhang@xxxx.com")
    private String email;
}

@ApiModel:生成swagger时扫描的实体注解;
@ApiModelProperty:属性注解,可用于描述实体属性;

修改后,效果如下:

实体注解

还有我特别喜欢的测试API的功能

1. 点击API规格右上角的"Try it out"按钮:

Try it out

2. 点击Execute按钮,测试API:

Execute

3. API测试返回:

API返回

我们可以在页面上修改API参数,如本例为leadId,进行不同参数值的测试(Request Body也支持),而不需要其他测试工具或测试代码,相当方便;

(本例中,我们在controller内@ApiImplicitParam注解中设置了默认值:example = "10130546")

使用Swagger来管理API文档是十分方便、快捷的,界面美观、并且有测试API的功能,对开发人员、测试人员十分友好,满足了绝大多数API文档化的需求。
凡事也要从不同的角度来看待,Swagger强大的背后,也有其一定的局限性:
  1. 仅对于Restful API支持较为友好,其他类型的API支持得不是很好;
  2. 对代码有一定的侵入性;
  3. Swagger与代码需要强绑定;
    (每个API都需要人为添加注解,如果有人偷懒,在API开发时没有加上Swagger对应的注解,那么Swagger UI上是看不到该API的)
  4. ...
综合来看,我还是很喜欢Swagger的,使用简单,功能强大!!!

你Get到了吗?

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,132评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,802评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,566评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,858评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,867评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,695评论 1 282
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,064评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,705评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,915评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,677评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,796评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,432评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,041评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,992评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,223评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,185评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,535评论 2 343