一、Swagger简介

Swagger是什么

Swagger官网
关于swagger是什么的问题,swagger的首页说的再清楚不过:

Swagger is the world’s largest framework of API developer tools for the OpenAPI Specification(OAS), enabling development across the entire API lifecycle, from design and documentation, to test and deployment.

因为swagger庞大的生态系统,我们在api的整个生命周期中都能从中受益: from design and documentation, to test and deployment
前头我的easy-mock属于test部分,同时它也有效地帮助前后端分离
如果你想对Swagger有更深的了解,可以看下对Tony Tam的这段采访(Tony Tam影响了Swagger的诞生):通过Swagger进行API设计,与Tony Tam的一次对话

为何需要用它

如果你在为一个系统设计API,你希望更好的管理你的API,你希望有一个工具能一站式地解决API相关的所有事情,从设计到文档再到mock,甚至能直接从设计文档中生成代码(声明式编程),这确实是可能的,如果你的描述信息是完备的,自动化生成mock接口,同时也可生成各种语言与api交互的SDK
这些便是你选择Swagger的理由

特点

  • 号称世界上最流行的API框架
  • RestFul API文档在线自动生成工具 —> API文档与API定义同步更新
  • 直接运行,可以在线测试API接口
  • 支持多种语言(java、php)

二、快速入门—使用SpringBoot集成Swagger

1. 导入依赖

  1. <!--Swagger相关依赖-->
  2. <dependency>
  3. <groupId>io.springfox</groupId>
  4. <artifactId>springfox-boot-starter</artifactId>
  5. <version>3.0.0</version>
  6. </dependency>

image.png
**

2. 主启动类添加 @EnableOpenApi 注解开启Swagger2功能

@SpringBootApplication
@EnableOpenApi  //开启Swagger功能
public class SwaggerApplication {

    public static void main(String[] args) {
        SpringApplication.run(SwaggerApplication.class, args);
    }

}

3. 启动测试

页面访问:http://localhost:8080/swagger-ui/index.html
image.png

三、Swagger相关配置

1. 可以通过配置定制化Swagger首页

/**
 * @className: SwaggerConfig
 * @description 定制化Swagger
 * @author GMF
 * @date 2021/3/4
 * @time 11:07
*/
@Configuration
public class SwaggerConfig {
    //配置了Swagger的Docket Bean实例
    @Bean
    public Docket docket() {
        return new Docket(DocumentationType.OAS_30).apiInfo(apiInfo());
    }

    //配置Swagger需要一个ApiInfo
    @Bean
    public ApiInfo apiInfo() {
        //作者信息
        Contact contact = new Contact("GMF","http://localhost:8080","2373271519@qq.com");

        ApiInfo apiInfo = new ApiInfo("GMF Swagger Documentation", "没什么好笑的", "1.0", "urn:tos"
                , contact, "Apache 2.0", "http://www.apache.org/licenses/LICENSE-2.0", new ArrayList());
        return apiInfo;
    }
}

Swagger相关配置源码
Docket

public class Docket implements DocumentationPlugin {
    public Docket(DocumentationType documentationType) {
        //需要一个DocumentationType类型的参数
        this.documentationType = documentationType;
      }
}

Documentation

//Docket需要的参数类型
public class DocumentationType extends SimplePluginMetadata {
    //其中已经内置了三个对象,直接使用即可
    public static final DocumentationType SWAGGER_12 = new DocumentationType("swagger", "1.2");
    public static final DocumentationType SWAGGER_2 = new DocumentationType("swagger", "2.0");
    public static final DocumentationType OAS_30 = new DocumentationType("openApi", "3.0");
    /** @deprecated */
    @Deprecated
    public static final DocumentationType SPRING_WEB = new DocumentationType("spring-web", "5.2");
    private final MediaType mediaType;

    public DocumentationType(String name, String version, MediaType mediaType) {
        super(name, version);
        this.mediaType = mediaType;
    }

    public DocumentationType(String name, String version) {
        this(name, version, MediaType.APPLICATION_JSON);
    }

    public MediaType getMediaType() {
        return this.mediaType;
    }

    public boolean equals(Object o) {
        if (this == o) {
            return true;
        } else if (!(o instanceof DocumentationType)) {
            return false;
        } else if (!super.equals(o)) {
            return false;
        } else {
            DocumentationType that = (DocumentationType)o;
            return super.equals(that) && this.mediaType.equals(that.mediaType);
        }
    }

    public int hashCode() {
        int result = super.hashCode();
        result = 31 * result + this.mediaType.hashCode();
        return result;
    }
}

ApiInfo

public class ApiInfo {
    public static final Contact DEFAULT_CONTACT = new Contact("", "", "");
    public static final ApiInfo DEFAULT;
    private final String version;
    private final String title;
    private final String description;
    private final String termsOfServiceUrl;
    private final String license;
    private final String licenseUrl;
    private final Contact contact;
    private final List<VendorExtension> vendorExtensions;

    public ApiInfo(String title, String description, String version, String termsOfServiceUrl, Contact contact, String license, String licenseUrl, Collection<VendorExtension> vendorExtensions) {
        this.title = title;
        this.description = description;
        this.version = version;
        this.termsOfServiceUrl = termsOfServiceUrl;
        this.contact = contact;
        this.license = license;
        this.licenseUrl = licenseUrl;
        this.vendorExtensions = new ArrayList(vendorExtensions);
    }

    static {
        DEFAULT = new ApiInfo("Api Documentation", "Api Documentation", "1.0", "urn:tos", DEFAULT_CONTACT, "Apache 2.0", "http://www.apache.org/licenses/LICENSE-2.0", new ArrayList());
    }

    ...
}

image.png

2. Swagger配置扫描接口

/**
 * @className: SwaggerConfig
 * @description 定制化Swagger
 * @author GMF
 * @date 2021/3/4
 * @time 11:07
*/
@Configuration
@Slf4j
public class SwaggerConfig {

    //查看当前Profile环境
    @Value("${spring.profiles.active}")
    private String profile;

    //配置了Swagger的Docket Bean实例
    @Bean
    public Docket docket(Environment environment) {
        log.info("当前的profile环境为{}",profile);
        return new Docket(DocumentationType.OAS_30)
                .apiInfo(apiInfo())
                //获取API选择构建器,再次调用build方法返回构建文档
                .select()
                //RequestHandlerSelectors:配置要扫描接口的方式
                //basePackage():指定要扫描的包
                .apis(RequestHandlerSelectors.basePackage("com.gmf.controller"))
                //指定要过滤的路径
                //.paths(PathSelectors.ant("/gmf/**"))
                .build();  //建造者模式
    }
}

ApiSelectorBuilder

public class ApiSelectorBuilder {
    //制定扫描接口的规则,使用时调用RequestHandlerSelectors类中的方法获取需要的参数
    public ApiSelectorBuilder apis(Predicate<RequestHandler> selector) {
        requestHandlerSelector = requestHandlerSelector.and(selector);
        return this;
      }

    //指定要过滤的路径,调用时使用PathSelectors类中的方法获取需要的参数
      public ApiSelectorBuilder paths(Predicate<String> selector) {
        pathSelector = pathSelector.and(selector);
        return this;
      }

    //返回一个构建文件Docket对象
    public Docket build() {
        return parent.selector(new ApiSelector(combine(requestHandlerSelector, pathSelector), pathSelector));
      }

    ...
}

RequestHandlerSelectors

//制定扫描接口的规则
public class RequestHandlerSelectors {
    private RequestHandlerSelectors() {
        throw new UnsupportedOperationException();
    }

    //扫描所有接口
    public static Predicate<RequestHandler> any() {
        return (each) -> {
            return true;
        };
    }

    //不再扫描任何接口
    public static Predicate<RequestHandler> none() {
        return (each) -> {
            return false;
        };
    }

    //扫描方法上使用指定注解的接口
    public static Predicate<RequestHandler> withMethodAnnotation(final Class<? extends Annotation> annotation) {
        return (input) -> {
            return input.isAnnotatedWith(annotation);
        };
    }

    //扫描类上使用指定注解的接口
    public static Predicate<RequestHandler> withClassAnnotation(final Class<? extends Annotation> annotation) {
        return (input) -> {
            return (Boolean)declaringClass(input).map(annotationPresent(annotation)).orElse(false);
        };
    }

    private static Function<Class<?>, Boolean> annotationPresent(final Class<? extends Annotation> annotation) {
        return (input) -> {
            return input.isAnnotationPresent(annotation);
        };
    }

    private static Function<Class<?>, Boolean> handlerPackage(final String basePackage) {
        return (input) -> {
            return ClassUtils.getPackageName(input).startsWith(basePackage);
        };
    }

    //扫描指定包路径下的接口
    public static Predicate<RequestHandler> basePackage(String basePackage) {
        return (input) -> {
            return (Boolean)declaringClass(input).map(handlerPackage(basePackage)).orElse(true);
        };
    }

    private static Optional<Class<?>> declaringClass(RequestHandler input) {
        return Optional.ofNullable(input.declaringClass());
    }
}

PathSelectors

public class PathSelectors {
    private PathSelectors() {
        throw new UnsupportedOperationException();
    }

    //过滤全部
    public static Predicate<String> any() {
        return (each) -> {
            return true;
        };
    }

    //不再过滤
    public static Predicate<String> none() {
        return (each) -> {
            return false;
        };
    }

    //使用正则表达式过滤指定接口
    public static Predicate<String> regex(final String pathRegex) {
        return new Predicate<String>() {
            public boolean test(String input) {
                return input.matches(pathRegex);
            }
        };
    }

    //过滤指定路径过滤指定接口
    public static Predicate<String> ant(final String antPattern) {
        return new Predicate<String>() {
            public boolean test(String input) {
                AntPathMatcher matcher = new AntPathMatcher();
                return matcher.match(antPattern, input);
            }
        };
    }
}

3. Profile环境切换开启关闭Swagger

在Swagger默认配置类中,引入了SpringfoxConfigurationProperties类的配置

@Configuration
@EnableConfigurationProperties({SpringfoxConfigurationProperties.class})
@ConditionalOnProperty(
    value = {"springfox.documentation.enabled"},
    havingValue = "true",
    matchIfMissing = true
)
@Import({OpenApiDocumentationConfiguration.class, SpringDataRestConfiguration.class, BeanValidatorPluginsConfiguration.class, Swagger2DocumentationConfiguration.class, SwaggerUiWebFluxConfiguration.class, SwaggerUiWebMvcConfiguration.class})
@AutoConfigureAfter({WebMvcAutoConfiguration.class, JacksonAutoConfiguration.class, HttpMessageConvertersAutoConfiguration.class, RepositoryRestMvcAutoConfiguration.class})
public class OpenApiAutoConfiguration {
    public OpenApiAutoConfiguration() {
    }
}

SpringfoxConfigurationProperties:Swagger相关配置

@ConfigurationProperties("springfox.documentation")
public class SpringfoxConfigurationProperties {
    //开启Swagger功能
    private boolean enabled = true;

    ...
}

建立不同环境下的配置文件
image.png

application-prod.yaml

#线上环境关闭Swagger
springfox:
  documentation:
    enabled: false

application-test.yaml

#开发环境开启Swagger功能
springfox:
  documentation:
    enabled: true

application.properties

#指定profile环境
spring.profiles.active=prod

测试线上环境Swagger运行情况
image.png

测试开发环境Swagger运行情况
image.png

4. Swagger分组

在容器中添加多个Docket Bean

@Bean
public Docket docket1(){
    return new Docket(DocumentationType.OAS_30).groupName("A");
}

@Bean
public Docket docket2(){
    return new Docket(DocumentationType.OAS_30).groupName("B");
}

@Bean
public Docket docket3(){
    return new Docket(DocumentationType.OAS_30).groupName("C");
}

出现了多个分组
image.png

5. Swagger对接口(Controlelr)的注释

如果需要在Swagger中显示Model实体,则需要将其添加到扫描的指定包下的Controller返回值中

Controller

@RestController
public class HelloController {
    /**
     * @param  
     * @return com.gmf.pojo.User
     * @description 创建的实体只要在Controller中有返回,则会添加到Swagger的Model中
     */
    @PostMapping("/user")
    public User user(){
        return new User();
    }
}

pojo

import io.swagger.annotations.ApiModel;
import lombok.Data;

//标注实体在Swagger Model中的显示名称
@ApiModel("用户实体类")
@Data
public class User {
    //标注实体属性在Swagger中显示的名称
    @ApiModelProperty("用户名")
    private String username;
    @ApiModelProperty("密码")
    private String password;
}

image.png

如果需要为接口(Controller)添加注释,在Swagger页面显示详细信息,则需要添加@ApiXxx注解

@RestController
@Api(tags = "Swagger接口测试")  //标注Controller名称
public class HelloController {
    @GetMapping("/hello")
    public String hello(){
        return "Hello";
    }

    /**
     * @param  
     * @return com.gmf.pojo.User
     * @description 创建的实体只要在Controller中有返回,则会添加到Swagger的Model中
     */
    @PostMapping("/user")
    public User user(){
        return new User();
    }

    //标注每一个接口的名称
    @ApiOperation("测试传递User参数的controller接口")
    @PostMapping
    //标注所需参数的名称
    public User user(@ApiParam("参数传递的User实体") User user){
        return user;
    }
}

在Swagger页面显示了扫描包下的所有接口,并附带有注解的详细信息
image.png

更厉害之处在于,前端人员能直接进行接口测试,**极大的方便了前端与后端之间的沟通**
image.png

注意:在项目正式发布之前,关闭Swagger,出于安全考虑,并且减少内存占用