文章目录
一、初识Swagger
由于Spring Boot能够快速开发、便捷部署等特性,相信有很大一部分Spring Boot的用户会用来构建RESTful API。而我们构建RESTful API的目的通常都是由于多终端的原因,这些终端会共用很多底层业务逻辑,因此我们会抽象出这样一层来同时服务于多个移动端或者Web前端。
这样一来,我们的RESTful API就有可能要面对多个开发人员或多个开发团队:IOS开发、Android开发或是Web开发等。为了减少与其他团队平时开发期间的频繁沟通成本,传统做法我们会创建一份RESTful API文档来记录所有接口细节,然而这样的做法有以下几个问题:
由于接口众多,并且细节复杂(需要考虑不同的HTTP请求类型、HTTP头部信息、HTTP请求内容等),高质量地创建这份文档本身就是件非常吃力的事,下游的抱怨声不绝于耳。
随着时间推移,不断修改接口实现的时候都必须同步修改接口文档,而文档与代码又处于两个不同的媒介,除非有严格的管理机制,不然很容易导致不一致现象。
为了解决上面这样的问题,本文将介绍RESTful API的重磅好伙伴Swagger2,它可以轻松的整合到Spring Boot中,并与Spring MVC程序配合组织出强大RESTful API文档。它既可以减少我们创建文档的工作量,同时说明内容又整合入实现代码中,让维护文档和修改代码整合为一体,可以让我们在修改代码逻辑的同时方便的修改文档说明。另外Swagger2也提供了强大的页面测试功能来调试每个RESTful API。
作用总结:
- 接口的文档在线自动生成。
- 功能测试。
二、实战演练
2.1 添加依赖
在pom.xml中加入Swagger3的依赖
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-boot-starter</artifactId>
<version>3.0.0</version>
</dependency>
2.2 创建Swagger配置类
@Configuration
public class SwaggerConfig {
@Bean
public Docket initDocket(Environment env) {
//设置要暴漏接口文档的配置环境
Profiles profile = Profiles.of("dev", "test");
boolean flag = env.acceptsProfiles(profile);
return new Docket(DocumentationType.OAS_30)
.apiInfo(apiInfo())
.enable(flag)
.select()
.apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class))
.paths(PathSelectors.any())
.build();
}
private ApiInfo apiInfo() {
return new ApiInfoBuilder()
.title("Swagger3-Demo接口文档")
.description("XXXX团队")
.contact(new Contact("XXXX技术团队", "http://www.XXXX.com", "XXXX@qq.com "))
.version("1.0")
.build();
}
}
三、 常用注解
3.1 类
@Api()
:表示这个类是 swagger 资源
- tags:表示说明内容,只写 tags 就可以省略属性名
- value:同样是说明,不过会被 tags 替代,没卵用
3.2 方法上
@ApiOperation()
:对方法的说明,注解位于方法上
- value:方法的说明
- notes:额外注释说明
- response:返回的对象
- tags:这个方法会被单独摘出来,重新分组,若没有,所有的方法会在一个Controller分组下
3.3 方法入参
@ApiParam()
:对方法参数的具体说明,用在方法入参括号里,该注解在post请求参数时,参数名不显示
- name:参数名
- value:参数说明
- required:是否必填
@ApiImplicitParam
对方法参数的具体说明,用在方法上@ApiImplicitParams之内,该注解在get,post请求参数时,参数名均正常显示
- name 参数名称
- value 参数的简短描述
- required 是否为必传参数
- dataType 参数类型,可以为类名,也可以为基本类型(String,int、boolean等)指定也不起作用,没卵用
- paramType 参数的传入(请求)类型,可选的值有path, query, body, header or form。
3.4 实体
@ApiModel
描述一个Model的信息(一般用在请求参数无法使用@ApiImplicitParam注解进行描述的时候)
- value : model的别名,默认为类名
- description: model的详细描述
@ApiModelProperty
描述一个model的属性
- value 属性简短描述
- example 属性的示例值
- required 是否为必须值
3.5 header参数
@ApiImplicitParams({
@ApiImplicitParam(
paramType="header",
name="USERTOKEN",
dataType="String",
required=true,
value="用户token")
})
3.6 file入参
需要使用@RequestPart 注解
@ApiOperation(value = "上传文件接口",notes = "上传文件接口")
@ApiImplicitParams({
@ApiImplicitParam(name = "name", value = "上传人")
})
@PostMapping(value = "/uploadFile")
public String uploadFile(
@RequestParam("name") String name,
@RequestPart("file") MultipartFile file){
}
四、拦截器放行
若项目中有使用拦截器,放行以下路径
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Autowired
TokenInterceptor tokenInterceptor;
/**
* 拦截器
* @param registry
*/
@Override
public void addInterceptors(InterceptorRegistry registry) {
// 注册token拦截器
registry.addInterceptor(tokenInterceptor)
.addPathPatterns("/**")
.excludePathPatterns("/**/swagger-ui/**")
.excludePathPatterns("/**/swagger-resources/**")
.excludePathPatterns("/**/v3/**")
;
}
}
五、文档访问地址
http://ip:port/context-path/swagger-ui/
http://ip:port/context-path/swagger-ui/index.html
六、Swagger3 Demo
/**
* @Auther: lya
* @Description: swagger3-演示
* @Version: 1.0
*/
@RestController
@RequestMapping("/oss")
@Api(value = "swagger3-演示",description = "用来演示Swagger的一些注解")
public class TestController {
@ApiOperation(value="修改用户密码", notes="根据用户id修改密码")
@ApiImplicitParams({
@ApiImplicitParam(paramType="query", name = "userId", value = "用户ID", required = true, dataType = "Integer"),
@ApiImplicitParam(paramType="query", name = "password", value = "旧密码", required = true, dataType = "String"),
@ApiImplicitParam(paramType="query", name = "newPassword", value = "新密码", required = true, dataType = "String")
})
@RequestMapping("/updatePassword")
public String updatePassword(@RequestParam(value="userId") Integer userId, @RequestParam(value="password") String password,
@RequestParam(value="newPassword") String newPassword){
if(userId <= 0 || userId > 2){
return "未知的用户";
}
if(StringUtils.isEmpty(password) || StringUtils.isEmpty(newPassword)){
return "密码不能为空";
}
if(password.equals(newPassword)){
return "新旧密码不能相同";
}
return "密码修改成功!";
}
}
七、Swagger2 Demo
pom.xml
<!-- Swagger依赖 -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.9.2</version>
<!-- 排除下面两个依赖,解决 For input string: "" 异常 -->
<exclusions>
<exclusion>
<groupId>io.springfox</groupId>
<artifactId>swagger-annotations</artifactId>
</exclusion>
<exclusion>
<groupId>io.swagger</groupId>
<artifactId>swagger-models</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- 单独引用版本偏低的两个依赖 -->
<dependency>
<groupId>io.swagger</groupId>
<artifactId>swagger-annotations</artifactId>
<version>1.5.21</version>
</dependency>
<dependency>
<groupId>io.swagger</groupId>
<artifactId>swagger-models</artifactId>
<version>1.5.21</version>
</dependency>
<!-- UI页面展示 -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.9.2</version>
</dependency>
config.java
import io.swagger.annotations.ApiOperation;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import org.springframework.core.env.Profiles;
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;
@Configuration
@EnableSwagger2
public class SwaggerConfig {
@Bean
public Docket initDocket(Environment env) {
//设置要暴漏接口文档的配置环境
Profiles profile = Profiles.of("dev", "test");
boolean flag = env.acceptsProfiles(profile);
return new Docket(DocumentationType.SWAGGER_2)
.groupName("test1")
.apiInfo(apiInfo())
.host("base url")
.enable(flag)
.select()
.apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class))
.paths(PathSelectors.any())
.build();
}
@Bean
public Docket docket1() {
return new Docket(DocumentationType.SWAGGER_2)
//分组
.groupName("test2")
//当前分组信息
.apiInfo(apiInfoByLYA())
//扫描范围select开始 build结束
.select()
//根据给定的包的位置进行扫描
.apis(RequestHandlerSelectors.basePackage("com.yeyoo.support.swagger.controller2"))
//类上有对应注解会被扫描
// .apis(RequestHandlerSelectors.withClassAnnotation(RestController.class))
//路径过滤
.paths(PathSelectors.any())
.build();
}
private ApiInfo apiInfo() {
return new ApiInfoBuilder()
//标题
.title("Swagger-Demo接口文档")
//简介
.description("平台支撑小组")
//服务条款
// .termsOfServiceUrl("www.fuwutiaokuan.com")
//作者个人信息
.contact(new Contact("平台支撑小组", "http://www.baidu.com", "9945@qq.com "))
//版本
.version("1.0")
.build();
}
private ApiInfo apiInfoByLYA() {
return new ApiInfoBuilder()
//标题
.title("SwaggerByYEYOO接口文档")
//简介
.description("lya")
//作者个人信息
.contact(new Contact("lya", "http://www.baidu.com", "9945@qq.com "))
//版本
.version("1.0")
.build();
}
}
dao
@Data
@ApiModel(value=" user ", description=" 用户实体类 ")
public class User {
@ApiModelProperty(value = "用户名")
private String name;
@ApiModelProperty(value = "年龄")
private Integer age;
@ApiModelProperty(value = "性别")
private Integer sex;
}
controller
import com.yeyoo.support.swagger.pojo.User;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@Api(value = "用户", tags = {"用户测试"})
@RestController
public class UserController {
@ApiOperation(value = "用户测试用例")
@RequestMapping(value = "/user",method = RequestMethod.POST)
public void userTest(User user){
user.setAge(1);
user.setName("1");
System.out.println(user.toString());
}
@ApiOperation(value="修改用户密码", notes="根据用户id修改密码")
@ApiImplicitParams({
@ApiImplicitParam(paramType="query", name = "userId", value = "用户ID", required = true, dataType = "Integer"),
@ApiImplicitParam(paramType="query", name = "password", value = "旧密码", required = true, dataType = "String"),
@ApiImplicitParam(paramType="query", name = "newPassword", value = "新密码", required = true, dataType = "String")
})
@RequestMapping("/updatePassword")
public String updatePassword(@RequestParam(value="userId") Integer userId,
@RequestParam(value="password") String password,
@RequestParam(value="newPassword") String newPassword){
if(userId <= 0 || userId > 2){
return "未知的用户";
}
if(StringUtils.isEmpty(password) || StringUtils.isEmpty(newPassword)){
return "密码不能为空";
}
if(password.equals(newPassword)){
return "新旧密码不能相同";
}
return "密码修改成功!";
}
}
TestApplication
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;
@ComponentScan(basePackages = {"com.support.swagger","com.support.redis"})
@SpringBootApplication(scanBasePackages = "com.yeyoo.support")
public class TestApplication {
public static void main(String[] args) {
SpringApplication.run(TestApplication.class, args);
}
}