Swagger
学习目标:
- 了解Swagger的作用和概念
- 了解前后端分离
- 在SpringBoot中继承Swagger
1. 简介
但是前后端分离产生一个问题:
- 前后端集成联调,前端人员和后端人员无法做到“及时协商,所以需要尽早解决”,最终导致问题集中爆发;
解决方案:
- 首先指定schema[计划的提纲],实时更新最新API,降低集成的风险;
- 早些年: 制定word计划文档;
- 前后端分离:
- 前端测试后端接口, 相关工具: PostMan
- 后端提供接口, 需要实时更新最新的消息改动
于是Swagger应运而生
- 号称世界上最流行的Api框架;
- RestFul Api文档在线自动生成工具=>Api文档与API定义同步更新
- 直接运行,可以在线测试API接口;
- 支持多种语言: (Java,php…)
官网: https://swagger.io/
在项目中使用Swagger需要 springbox
- swagger2
- ui
2. Springboot 集成 Swagger
2.1 集成
-
新建一个SpringBoot - web 项目
-
导入相关依赖
<dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger-ui</artifactId> <version>2.9.2</version> </dependency> <dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger2</artifactId> <version>2.9.2</version> </dependency>
-
编写一个Hello工程
-
集成配置Swagger --> Config
package com.xz.swagger.config; import org.springframework.context.annotation.Configuration; import springfox.documentation.swagger2.annotations.EnableSwagger2; /** * @author 许正 * @version 1.0 */ @Configuration @EnableSwagger2 //开启Swagger2 public class SwaggerConfig { }
-
测试运行(访问下面的页面)
http://localhost:8080/swagger-ui.html
2.2 配置Swagger信息
package com.xz.swagger.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
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;
import java.util.ArrayList;
/**
* @author 许正
* @version 1.0
*/
@Configuration
@EnableSwagger2 //开启Swagger2
public class SwaggerConfig {
//配置Swagger 的 Docket 的 bean 实例
@Bean
public Docket docket() {
return new Docket(DocumentationType.SWAGGER_2).apiInfo(apiInfo());
}
//配置Swagger信息 = apiInfo
private ApiInfo apiInfo() {
//作者信息
Contact contact = new Contact("许正", "https://blog.csdn.net/WXxz0", "550628942@qq.com");
/**
* String title,
* String description,
* String version,
* String termsOfServiceUrl,
* Contact contact,
* String license,
* String licenseUrl,
* Collection<VendorExtension> vendorExtensions
*/
return new ApiInfo(
"许正的SwaggerAPI文档",
"我亦无他惟手熟尔",
"v1.0",
"https://blog.csdn.net/WXxz0",
contact,
"Apache 2.0",
"http://www.apache.org/licenses/LICENSE-2.0",
new ArrayList());
}
}
效果图:
2.3 Swagger配置扫描接口
Docket.select()
//配置Swagger 的 Docket 的 bean 实例
@Bean
public Docket docket() {
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo())
// .enable(false)//enable是否启动Swagger, 如果为False, 则swagger不能再浏览器中访问
.select()
//RequestHandlerSelectors: 配置要扫描接口的方式
//basePackage: 指定要扫描的包
//any(): 扫描全部
//none(): 不扫描
//withClassAnnotation: 扫描类上的注解,参数是一个注解的反射对象
//withMethodAnnotation: 扫描方法上的注解
.apis(RequestHandlerSelectors.basePackage("com.xz.swagger.controller"))
//paths() 过滤什么路径
.paths(PathSelectors.ant("/xz/**"))
.build();
}
2.4 其他配置
如何实现Swagger在生产环境中使用,在发布的时候不使用?
- 判断是不是生产环境 flag = false
- 注入enable (flag)
@Bean
public Docket docket(Environment environment) {
//设置要显示的Swagger环境
Profiles profiles = Profiles.of("dev", "test");
//通过environment.acceptsProfiles判断是否处在自己设定的环境当中
boolean flag = environment.acceptsProfiles(profiles);
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo())
.enable(flag)//enable是否启动Swagger, 如果为False, 则swagger不能再浏览器中访问
.select()
.apis(RequestHandlerSelectors.basePackage("com.xz.swagger.controller"))
// .paths(PathSelectors.ant("/xz/**"))
.build();
}
配置API文档的分组
.apiInfo(apiInfo())
.groupName("许正")//修改默认分组
配置多个分组, 多个Docket实例即可
@Bean
public Docket docket1() {
return new Docket(DocumentationType.SWAGGER_2).groupName("A");
}
@Bean
public Docket docket2() {
return new Docket(DocumentationType.SWAGGER_2).groupName("B");
}
@Bean
public Docket docket3() {
return new Docket(DocumentationType.SWAGGER_2).groupName("C");
}
实体类配置
实体类(User)
package com.xz.swagger.pojo;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @author 许正
* @version 1.0
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
//@Api //注释
@ApiModel("用户实体类")
public class User {
@ApiModelProperty("用户名")
private String username;
@ApiModelProperty("密码")
private String password;
}
controller
package com.xz.swagger.controller;
import com.xz.swagger.pojo.User;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @author 许正
* @version 1.0
*/
@RestController
public class HelloController {
@GetMapping("/hello")
public String hello() {
return "Hello!";
}
//只要我们的接口中,返回值中存在实体类,他就会被扫描到swagger中
@PostMapping("/user")
public User user() {
return new User();
}
//Operation接口,放在方法上(不是放在类上的)
@ApiOperation("Hello测试")
@GetMapping("/hello2")
public String hello2(@ApiParam("用户名") String username) {
return "Hello!" + username;
}
//Operation接口,放在方法上(不是放在类上的)
@ApiOperation("Post测试")
@PostMapping("/postTest")
public User postTest(@ApiParam("用户") User user) {
return user;
}
}
总结:
- 我们可以通过Swagger给一些比较难理解的属性或者接口, 增加注释信息
- 接口文档实时更新
- 可以在线测试
Swagger是一个优秀的工具,几乎所有大公司都有使用它 - **[注意点] **在正式发布的时候,关闭Swagger! ! !出于安全考虑。而且节省运行的内存;
3. knife4j
3.1 简介
使用Swagger你只需要按照它的规范去定义接口及接口相关的信息,再通过Swagger衍生出来的一系列项目和工具,就可以做到生成各种格式的接口文档,以及在线接口调试页面等等。
官网: https://swagger.io/
knife4j是为Java MVC框架集成Swagger生成Api文档的增强解决方案。
3.2 使用方式
操作步骤:
-
导入knife4j的maven坐标
<dependency> <groupId>com.github.xiaoymin</groupId> <artifactId>knife4j-spring-boot-starter</artifactId> <version>3.0.3</version> </dependency>
-
导入knife4j相关配置类(WebMvcConfig.java).
-
设置静态资源,否则接口文档页面无法访问
package com.itheima.reggie.config; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.module.SimpleModule; import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer; import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer; import com.fasterxml.jackson.datatype.jsr310.deser.LocalTimeDeserializer; import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer; import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer; import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer; import com.github.xiaoymin.knife4j.spring.annotations.EnableKnife4j; import com.itheima.reggie.common.JacksonObjectMapper; import com.itheima.reggie.entity.Employee; import lombok.extern.slf4j.Slf4j; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.http.converter.HttpMessageConverter; import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport; import springfox.documentation.builders.ApiInfoBuilder; import springfox.documentation.builders.PathSelectors; import springfox.documentation.builders.RequestHandlerSelectors; import springfox.documentation.service.ApiInfo; import springfox.documentation.spi.DocumentationType; import springfox.documentation.spring.web.plugins.Docket; import springfox.documentation.swagger2.annotations.EnableSwagger2; import java.math.BigInteger; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.LocalTime; import java.time.format.DateTimeFormatter; import java.util.List; import static com.fasterxml.jackson.databind.DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES; @Slf4j @Configuration @EnableSwagger2 @EnableKnife4j public class WebMvcConfig extends WebMvcConfigurationSupport { /** * 设置静态资源映射 * @param registry */ @Override protected void addResourceHandlers(ResourceHandlerRegistry registry) { log.info("开始进行静态资源映射..."); registry.addResourceHandler("doc.html").addResourceLocations("classpath:/META-INF/resources/"); registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/"); registry.addResourceHandler("/backend/**").addResourceLocations("classpath:/backend/"); registry.addResourceHandler("/front/**").addResourceLocations("classpath:/front/"); } /** * 扩展mvc框架的消息转换器 * @param converters */ @Override protected void extendMessageConverters(List<HttpMessageConverter<?>> converters) { log.info("扩展消息转换器..."); //创建消息转换器对象 MappingJackson2HttpMessageConverter messageConverter = new MappingJackson2HttpMessageConverter(); //设置对象转换器,底层使用Jackson将Java对象转为json messageConverter.setObjectMapper(new JacksonObjectMapper()); //将上面的消息转换器对象追加到mvc框架的转换器集合中 converters.add(0,messageConverter); } @Bean public Docket createRestApi() { // 文档类型 return new Docket(DocumentationType.SWAGGER_2) .apiInfo(apiInfo()) .select() .apis(RequestHandlerSelectors.basePackage("com.itheima.reggie.controller")) .paths(PathSelectors.any()) .build(); } private ApiInfo apiInfo() { return new ApiInfoBuilder() .title("瑞吉外卖") .version("1.0") .description("瑞吉外卖接口文档") .build(); } }
-
在LoginCheckFilter中设置不需要处理的请求路径
//定义不需要处理的请求路径 String[] urls = new String[]{ "/employee/login", "/employee/logout", "/backend/**", "/front/**", "/common/**", "/user/sendMsg",//移动端发送短信 "/user/login",//移动端登陆 "/doc.html", "/webjars/**", "/swagger-resources", "/v2/api-docs" };