Swagger是一款RESTful接口的文档在线自动生成、功能测试功能框架。一个规范和完整的框架,用于生成、描述、调用和可视化RESTful风格的Web服务,加上swagger-ui,可以有很好的呈现。
当我们在后台的接口修改了后,swagger可以实现自动的更新,而不需要人为的维护这个接口进行测试。
knife4j是一款对swagger功能增强的插件,优化展示页面及接口调试功能,具体介绍参见-----Knife4j官方介绍 下一篇 介绍集成过程
例如:
本知识点在springboot使用基于Mybatis注解方式实现的CRUD的基础上进行的。
第一步:(单独集成swagger2)jar包的引入:
io.springfox
springfox-swagger-ui
2.4.0
io.springfox
springfox-swagger2
2.4.0
第二步:swagger的配置启动类编写:
要使用swagger要进行一些配置,这个在界面的图上是可以显示的:类似于说明书:在这个类中我们会使用注解来进行启动 swagger:
具体配置如下:
单独配置 swagger 如下配置 没有权限校验的
package cn.xdf.springboot;
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;
@Configuration
@EnableSwagger2
public class Swagger2 {
@Value("${swagger.enable}")
private boolean swagger;
// swagger2的配置文件,这里可以配置swagger2的一些基本的内容,比如扫描的包等等
@Bean
public Docket createRestApi() {
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo())
.select()
// 为当前包路径
.apis(RequestHandlerSelectors.basePackage("cn.xdf.springboot.controller"))
.paths(PathSelectors.any())
.build();
}
// 构建 api文档的详细信息函数,注意这里的注解引用的是哪个
private ApiInfo apiInfo() {
return new ApiInfoBuilder()
// 页面标题
.title("Spring Boot 测试使用 Swagger2 构建RESTful API")
// 创建人信息
.contact(new Contact("MrZhang", "https://www.cnblogs.com/zs-notes/category/1258467.html", "[email protected]"))
// 版本号
.version("1.0")
// 描述
.description("API 描述")
.build();
}
}
如果有权限校验 则修改
ParameterBuilder ticketPar = new ParameterBuilder();
List pars = new ArrayList();
ticketPar.name("Authorization").description("token")
.modelRef(new ModelRef("string"))
.parameterType("header")
.defaultValue("Bearer-")
.required(false)
.build();
pars.add(ticketPar.build());
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo())
.select()
.apis(RequestHandlerSelectors.basePackage("cn.xdf.springboot.controller"))
.paths(PathSelectors.any())
.build()
.globalOperationParameters(pars);
此外,放开swagger资源
@Configuration
@EnableWebMvc
@Log4j2
public class InterceptorConfiguration implements WebMvcConfigurer {
@Value("${image_url}")
private String imageUrl;
@Resource
private AuthorizationInterceptor authorizationInterceptor;
@Autowired
private IgnoredUrlsProperties ignoredUrlsProperties;
//解决跨域问题配置
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowCredentials(true)
.allowedHeaders("*")
.allowedOrigins("*")
.allowedMethods("GET","POST","PUT","DELETE");
}
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/**").addResourceLocations("classpath:/resources/",imageUrl).setCachePeriod(0);
// registry.addResourceHandler("swagger-ui.html")
// .addResourceLocations("classpath:/META-INF/resources/");
registry.addResourceHandler("/webjars/**")
.addResourceLocations("classpath:/META-INF/resources/webjars/");
registry.addResourceHandler("doc.html").addResourceLocations("classpath:/META-INF/resources/");
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
// 注册拦截器
InterceptorRegistration ir = registry.addInterceptor(authorizationInterceptor);
// 配置拦截的路径
ir.addPathPatterns("/**");
// 配置不拦截的路径
ir.excludePathPatterns(ignoredUrlsProperties.getUrls());
}
}
security 的配置类 放开 过滤资源 配置在配置文件中 jgnoreUrls
@Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
//忽略路径
List urls = ignoredUrlsProperties.getUrls();
String [] ignoreUrls = urls.toArray(new String [urls.size()]);
httpSecurity.csrf().disable()
.httpBasic().authenticationEntryPoint(authenticationEntryPoint)
//session状态 无状态
.and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
//请求之前的认证过滤器 token校验
.and().authorizeRequests()
.antMatchers(ignoreUrls).permitAll()
// 所有请求都需要认证
.anyRequest().authenticated().and().cors();
httpSecurity
.headers()
.frameOptions().sameOrigin() // required to set for H2 else H2 Console will be blank.
.cacheControl();
//httpSecurity.authorizeRequests().requestMatchers(CorsUtils::isPreFlightRequest).permitAll();
httpSecurity.exceptionHandling().accessDeniedHandler(authAccessDeniedHandler);
httpSecurity.addFilterBefore(authenticationRequestFilter, UsernamePasswordAuthenticationFilter.class);
}
修改添加application.properties文件 设置接口api生效的环境 参数
#是否激活 swagger true or false
swagger.enable=true
# 忽略鉴权url controller 不鉴权
ignored:
urls:
- /swagger-ui.html
- /swagger-ui.html/**
- /swagger-resources/**
- /webjars/**
- /swagger/**
- /v2/**
第三步:使用swagger来进行模拟测试:
使用swagger2来进行测试接口主要是在哪些类中使用:这里我们依然选择在controller层:
package cn.xdf.springboot.controller;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.validation.Valid;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import cn.xdf.springboot.mapper.CategoryMapper;
import cn.xdf.springboot.pojo.Category;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiOperation;
/**
* 控制层 简单演示增删改查及分页
*
*/
@RestController
@RequestMapping("api")
@Api("swaggerDemoController相关的api")
public class SwaggerDemoController {
@Autowired
CategoryMapper categoryMapper;
private static final Logger logger= LoggerFactory.getLogger(SwaggerDemoController.class);
//1.商品添加
//@PutMapping("add") 添加方法--restful风格
@PutMapping("add")
@ApiOperation(value="商品新增")
//正常业务时, 需要在category类里或者server层进行事务控制,控制层一般不进行业务控制的。
//@Transactional(rollbackFor = Exception.class)
//@RequestParam 接收页面中的请求的参数
public Map addCategory(@RequestParam String name){
Category category = new Category();
category.setName(name);
categoryMapper.save(category);
logger.info("开始新增某个商品信息");
Map result = new HashMap();
result.put("respCode", "01");
result.put("respMsg", "新增成功!");
result.put("data", category);
return result;
}
//2.商品修改
//@PostMapping("update") 修改方法--restful风格
@PostMapping("update")
@ApiOperation(value = "商品修改", notes = "修改数据库中某个的商品信息")
//@RequestBody 接收页面中的请求的参数对象(适用于post请求)
//当入参为实体对象时,需要在方法上加@Valid或@Validated或者在参数前加@Valid或@Validated,或者在类上加@Validated
public Map updateCategory(@Valid @RequestBody Category category) {
Map result = new HashMap();
Category categoryGet = categoryMapper.get(category.getId());
if(categoryGet == null || "".equals(categoryGet)){
try {
throw new Exception("修改的该商品不存在!");
} catch (Exception e) {
e.printStackTrace();
}
result.put("respCode", "03");
result.put("respMsg", "修改的该商品不存在!");
result.put("data", category);
return result;
}
categoryMapper.update(category);
logger.info("开始修改某个商品信息");
result.put("respCode", "03");
result.put("respMsg", "修改成功!");
result.put("data", category);
return result;
}
//3.商品删除
//@DeleteMapping("/delete/{id}") 删除方法--restful风格
@DeleteMapping("/delete/{id}")
@ApiOperation(value = "根据id删除商品", notes = "商品删除")
@ApiImplicitParam(name = "id", value = "商品ID", paramType = "path", required = true, dataType = "Integer")
public Map deleteCategory(@PathVariable int id)throws Exception{ //@PathVariable 获取/delete/{id}中id
Category category = categoryMapper.get(id);
Map result = new HashMap();
if (category == null) {
try {
throw new Exception("用户ID:" + id + ",未找到");
} catch (Exception e) {
e.printStackTrace();
}
result.put("respCode", "02");
result.put("respMsg", "数据库无该商品信息,删除失败!");
result.put("data", category);
return result;
}else{
categoryMapper.delete(id);
logger.info("开始删除某个商品信息");
result.put("respCode", "01");
result.put("respMsg", "删除成功!");
result.put("data", category);
return result;
}
}
//4.根据ID查询商品信息
//@GetMapping("") 查询方法--restful风格
@GetMapping("/get/{id}")
@ApiOperation(value = "根据id查询商品", notes = "查询数据库中某个的商品信息")
@ApiImplicitParam(name = "id", value = "商品ID", paramType = "path", required = true, dataType = "Integer")
public Map getCategory(@PathVariable int id) { //@PathVariable 获取/get/{id}中id
Category category = categoryMapper.get(id);
logger.info("开始查询某个商品信息");
Map result = new HashMap();
if (category == null) {
try {
throw new Exception("用户ID:" + id + ",未找到");
} catch (Exception e) {
e.printStackTrace();
}
result.put("respCode", "02");
result.put("respMsg", "数据库无该商品信息");
result.put("data", category);
return result;
}else{
result.put("respCode", "01");
result.put("respMsg", "查询成功!");
result.put("data", category);
return result;
}
}
//5.分页查询
//@GetMapping("") 查询方法--restful风格
@GetMapping("/page")
@ApiOperation(value="商品查询(分页)")
public Map pageCategory(@RequestParam(value="start",defaultValue="0")int start,@RequestParam(value = "size", defaultValue = "5") int size) throws Exception {
//1. 在参数里接受当前是第几页 start ,以及每页显示多少条数据 size。 默认值分别是0和5。
//2. 根据start,size进行分页,并且设置id 倒排序
PageHelper.startPage(start,size,"id desc");
//3. 因为PageHelper的作用,这里就会返回当前分页的集合了
List cs = categoryMapper.findAll();
logger.info("开始分页查询商品信息");
//4. 根据返回的集合,创建PageInfo对象
PageInfo page = new PageInfo<>(cs);
Map result = new HashMap();
result.put("respCode", "01");
result.put("respMsg", "成功");
result.put("data", page);
return result;
}
}
这样swagger2与springboot就集成完毕了。
看下最终效果吧:
访问路径: http://localhost:8080/swagger-ui.html
调试:点击需要访问的api列表,点击try it out!按钮,表示 执行。
----------------------------------------------------------------------------------------------------------------------------------------------------------------
而且这些方法是实时更新的!!!
接下来测试一个新增方法:
查询方法:
另外,大家可下载示例,查看自定义的字符出现的位置,这样可以对其有个大致了解,各字段的作用领域是哪里
@Api:用于类;表示标识这个类是swagger的资源
属性名称 | 数据类型 | 默认值 | 说明 |
value | String | "" | 字段说明 |
tags | String[] | "" | 标签说明 |
description | String | "" | 详情描述 |
basePath |
String | "" | 基本路径可以不配置 |
position | int | "" | 如果配置多个Api 想改变显示的顺序位置 |
produces | String | "" | 提供者 (For example, "application/json, application/xml") |
consumes | String | "" | 消费者(For example, "application/json, application/xml") |
protocols | String | "" | 协议(Possible values: http, https, ws, wss.) |
authorizations | Authorization[] | "" | 高级特性认证时配置 |
hidden | boolean | "" | 配置为true 将在文档中隐藏 |
@ApiResponses:在 Rest 接口上使用,用作返回值的描述
属性名称 | 数据类型 | 默认值 | 说明 |
value | ApiResponse[] | "" | 访问对象 |
属性名称 | 数据类型 | 默认值 | 说明 |
code | String | "" | 响应的HTTP状态码 |
message | String | "" | 响应的信息内容 |
response | Class> | "" | 用于描述消息有效负载的可选响应类,对应于响应消息对象的 schema 字段 |
reference | String | "" | 指定对响应类型的引用,指定的应用可以使本地引用,也可以是远程引用,将按原样使用,并将覆盖任何指定的response()类 |
responseHeaders | ResponseHeader[] | "" | 声明包装响应的容器,有效值为List或Set,任何其他值都将被覆盖 |
responseContainer | String | "" | 声明响应的容器,有效值为List,Set,Map,任何其他值都将被忽略 |
examples | Example | "" | 例子 |
@ApiOperation:用在方法上,说明方法的作用,每一个url资源的定义
属性名称 | 数据类型 | 默认值 | 说明 |
value | String | "" | url的路径值 |
notes | String | "" | 文本说明 |
tags | String[] | "" | 如果设置这个值、value的值会被覆盖 |
response | Class> | "" | 返回的对象 |
responseContainer | String | "" | 声明响应的容器,有效值为List,Set,Map,任何其他值都将被忽略 |
responseReference | String | "" | 声明包装响应的容器,有效值为List或Set,任何其他值都将被覆盖 |
httpMethod | String | "" | "GET", "HEAD", "POST", "PUT", "DELETE", "OPTIONS" and "PATCH" |
position | int | "" | 如果配置多个Api 想改变显示的顺序位置 |
nickname | String | "" | 昵称 |
produces | String | "" | 提供者 (For example, "application/json, application/xml") |
consumes | String | "" | 消费者(For example, "application/json, application/xml") |
protocols | String | "" | 协议(Possible values: http, https, ws, wss.) |
authorizations | Authorization[] | "" | 高级特性认证时配置 |
hidden | boolean | "" | 隐藏 |
responseHeaders | ResponseHeader[] | "" | 声明包装响应的容器,有效值为List或Set,任何其他值都将被覆盖 |
code | String | "" | http的状态码 默认 200 |
extensions | Extension[] | "" | 扩展属性 |
ignoreJsonView | boolean | "" | 是否忽略显示 |
@PathVariable:是获取get方式,url后面参数,进行参数绑定(单个参数或两个以内参数使用)
属性名称 | 数据类型 | 默认值 | 说明 |
value | String | "" | url的路径值 |
name | String | "" | 重写属性名字 |
required | String | "" | 是否必填 |
@RequestBody :在当前对象获取整个http请求的body里面的所有数据(两个以上参数封装成对象使用)
属性名称 | 数据类型 | 默认值 | 说明 |
required | String | "" | 是否必填 |
备注:实体类收参
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import java.io.Serializable;
@ApiModel(description = "登录表单")
public class LoginFormParams implements Serializable {
/**
* 用户账号
*/
@ApiModelProperty(value = "登录账号", name = "account", required = true, example = "admin")
@NotBlank(message = "账号不允许为空,请输入")
private String account;
/**
* 用户密码
*/
@ApiModelProperty(value = "登录密码", name = "password", required = true, example = "123456")
@NotBlank(message = "密码不允许为空,请输入")
private String password;
/**
* 图片验证码
*/
@ApiModelProperty(value = "验证码", name = "validCode", required = false, example = "TG20")
/*@NotBlank(message = "验证码不允许为空,请输入")*/
private String validCode;
/**
* 记住我
*/
@ApiModelProperty(value = "记住我", name = "rememberMe", required = false, example = "false")
private boolean rememberMe;
public String getAccount() {
return account;
}
public void setAccount(String account) {
this.account = account;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getValidCode() {
return validCode;
}
public void setValidCode(String validCode) {
this.validCode = validCode;
}
public boolean isRememberMe() {
return rememberMe;
}
public void setRememberMe(boolean rememberMe) {
this.rememberMe = rememberMe;
}
}
/**
* 账号登录
* @param form
* @param bindingResult
* @param session
* @return
*/
@PostMapping("/ajaxLogin")
@ApiOperation(value = "用户登录", notes = "用户异步登录处理")
@ApiImplicitParam(name = "form", value = "用户登录表单", required = true, dataType = "LoginFormParams")
@ResponseBody
public JsonResult ajaxLogin(@Valid @RequestBody LoginFormParams form, BindingResult bindingResult, HttpSession session) throws Exception{
if(bindingResult.hasErrors()){
for (ObjectError error : bindingResult.getAllErrors()){
return JsonResult.error(GlobalParams.EX_CODE4.getCode(), error.getDefaultMessage(), null);
}
}
// 获取当前用户对象
Subject subject = SecurityUtils.getSubject();
// 生成令牌
UsernamePasswordToken token = new UsernamePasswordToken(form.getAccount(),form.getPassword());
if(form.isRememberMe()){
token.setRememberMe(true);
}else{
token.setRememberMe(false);
}
subject.login(token);
// 登录成功的用户对象
SysUser user = (SysUser)subject.getPrincipal();
// 将信息存储至Redis
session.setAttribute("user", user);
return JsonResult.ok(GlobalParams.EX_CODE5, user);
}
【注意】两处地方
@RequestBody 以及
@ApiImplicitParam(name = "form", value = "用户登录表单", required = true, dataType = "LoginFormParams")
其中name 对应的是参数名称,value 是描述,dataType 对应的是参数类型