Swagger介绍
Swagger是一套围绕Open API规范构建的开源工具,可以帮助设计、构建、记录和使用Restful API。它提供了一组工具,使得可以创造性的深入到API的设计、开发和测试阶段中,从而提高API的设计质量和开发效率。
Swagger的核心是Swagger Specification,这是一种定义API的标准格式,以OpenAPI Specification(前身是Swagger Specification)的形式存在。Swagger Specification中定义了API的基本信息、路径、HTTP方法、参数、响应等内容,以及其他关于API的详细描述。
Swagger的工具包括:
-
Swagger Editor:基于浏览器的编辑器,可以在里面编写Open API规范,类似Markdown具有实时预览描述文件的功能。
-
Swagger UI:将Open API规范呈现为交互式API文档,用可视化UI展示描述文件。
-
Swagger Codegen:将OpenAPI规范生成为服务器存根和客户端库,通过Swagger Codegen可以将描述文件生成html格式和cwiki形式的接口文档,同时也可以生成多种语言的客户端和服务端代码。
-
Swagger Inspector:和Swagger UI有点类似,但是可以返回更多信息,也会保存请求的实际参数数据。
Swagger的特点包括:
-
API规范:Swagger使用一种称为OpenAPI规范的JSON或YAML格式来定义API的结构、路径、参数、响应等信息。这个规范可以明确定义API的各个方面,使得API文档和代码生成变得更加一致和可靠。
-
自动生成文档:Swagger可以自动生成API的交互式文档,这些文档展示了API的终端用户如何使用它。这有助于开发人员、测试人员和其他利益相关者了解API的功能和用法。
-
交互式API测试:Swagger UI是Swagger的一个组件,它提供了一个用户界面,让您能够直接在文档中测试API的各个端点,以确保其正常工作。
-
代码生成:基于Swagger规范,您可以使用各种编程语言生成客户端和服务器端代码,以便更轻松地构建与API交互的应用程序。
-
API监控和分析:Swagger还支持API的监控和分析,您可以了解API的使用情况、性能和错误情况,以便及时进行调整和优化。
-
生态系统:Swagger有一个庞大的生态系统,包括各种工具和插件,可以帮助您集成Swagger到不同的开发环境和框架中,如Spring Boot、Express.js等。
总之,Swagger是一个有助于开发、文档和测试API的强大工具,它提供了一种标准化的方法来描述和管理API,从而提高了API的可维护性、可测试性和互操作性。这使得开发团队能够更容易地构建和维护高质量的RESTful Web服务。
SpringBoot整合Swagger文档
在Spring应用程序中,我们推荐使用Springfox Swagger。简单介绍下Swagger和Springfox Swagger,它们都是用于API文档管理的工具,但它们之间有一定的区别。
Swagger是一种规范,它定义了API的基本信息、路径、HTTP方法、参数、响应等内容,以及其他关于API的详细描述。Swagger还包括一些工具,如Swagger Editor、Swagger UI、Swagger Codegen和Swagger Inspector等,用于帮助设计和记录RESTful API。
Springfox Swagger是基于Spring生态系统的Swagger规范的实现。它提供了一种方便的方式来使用Swagger规范,特别是在使用Spring框架的情况下。Springfox Swagger包括Springfox Swagger-ui和Springfox Swagger Codegen等工具,用于在Spring应用程序中生成和管理API文档。
简单来说,Swagger是API文档管理规范,而Springfox Swagger是基于Spring生态系统的该规范的实现。
以spring boot(v2.5.3)应用为例,整合swagger,具体步骤如下示例:
首先,在pom文件中导入springfox swagger依赖,我们直接导入场景启动器
<!--swagger-->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-boot-starter</artifactId>
<version>3.0.0</version>
</dependency>
新建Swagger配置类,并添加@EnableSwagger2和@EnableOpenAi注解
package cn.chiatso.blog.configure;
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.oas.annotations.EnableOpenApi;
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 chiatso
* @date 2023/10/07
* @description swagger接口文档配置
* 接口文档生成地址访问:http://localhost:8088/swagger-ui/
*/
@Configuration
@EnableOpenApi // 开启swagger自动配置,可以看到ui界面
@EnableSwagger2
public class SwaggerConfigure {
/**
* swagger实例配置
* <p>
* DocumentationType.OAS_30:使用 3.0 版本的 Swagger API。
* OAS 是 OpenAPI Specification 的简称,翻译成中文就是 OpenAPI 说明书,Swagger 遵循的就是这套规范。
* </p>
* <p>
* ApiInfo: 配置 API 文档基本信息,标题、描述、作者、版本等
* </p>
* <p>
* select:指定生成接口文档的基础包路径
* paths: 指定要提取的url用于生成接口文档
* </p>
* @return Docket
*/
@Bean
public Docket docket() {
Docket docket = new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo())
.enable(true)
.select()
// swagger提取接口文档包路径
.apis(RequestHandlerSelectors.basePackage("cn.chiatso.blog.controller"))
// 匹配所有url
.paths(PathSelectors.any())
.build();
return docket;
}
/**
* 构建api作者信息
* @return ApiInfo api作者信息
*/
private ApiInfo apiInfo() {
return new ApiInfoBuilder()
.title("All known blog在线接口文档")
.description("都知道个人博客系统基于前后端分离架构进行设计,采用SpringBoot以及Vue框架进行实现,本页列出了所有后端接口文档")
.contact(new Contact("chiatso", "https://github.com/survivems","chiatso@163.com"))
.version("v1.0")
.build();
}
}
自此,springboot整合swagger已经配置完成,接下来,编写一个登录接口,查看接口文档生成示例
@Slf4j
@RestController
@Api("用户操作控制层")
@RequestMapping("api/user")
public class UserController {
@ApiOperation("用户登录")
@PostMapping("login")
public Result login(@ApiParam("登录参数实体") @Validated @RequestBody UserLoginDTO userLoginDTO) {
log.info("进入登录处理...");
return ResultUtil.success();
}
}
参数实体类如下:
@Data
@NoArgsConstructor
@AllArgsConstructor
@ApiModel(description = "登录数据封装实体")
public class UserLoginDTO {
@NotBlank(message = "用户登录名不允许为空")
@Length(max = 16, min = 6, message = "用户名长度介于6-16位区间")
@Pattern(regexp = "[A-Za-z0-9]{6,16}", message = "用户名仅限字母或者数字或组合")
private String userName;
@NotBlank(message = "用户登录密码不允许为空")
@Length(min = 6, max = 16, message = "用户密码长度介于6-16位区间")
@Pattern(regexp = "[A-Za-z0-9]{6,16}", message = "用户密码仅支持字母数字以及.*组合")
private String userPassword;
}
上述代码整合hibernate校验,不理解的可以删除@Validated以及@NotBlank、@Pattern和@Length注解,不影响swagger正常功能。
启动应用程序,访问根路径/swagger-ui/即可看到swagger文档主页。
一些注意的踩坑点
- 如果项目中用到过滤器或者拦截器对url进行保护,那么需要放行swagger相关的资源,下面以token解析过滤器为实例,给出示例代码如下
package cn.chiatso.blog.filter;
import cn.chiatso.blog.constant.SysCode;
import cn.chiatso.blog.exception.AuthenticationException;
import cn.chiatso.blog.util.JwtUtil;
import cn.chiatso.blog.util.ResponseUtil;
import cn.chiatso.blog.util.ResultUtil;
import cn.chiatso.blog.vo.Result;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.ObjectUtils;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
/**
* @author chiatso
* @date 2023/10/07
* @description 解析请求头令牌过滤器
* <p>该过滤器负责进行用户身份认证,主要流程如下:</p>
* 浏览器(客户端发起请求)
* ⬇
* axios拦截,添加令牌在请求头字段 Authentication
* ⬇
* 真实请求后端接口,该过滤器拦截需要认证的请求
* ⬇
* 解析令牌
*<p>
* 解析结果有2种:
* 1. 解析失败处理流程:跑出认证失败异常,由全局异常处理器拦截,相应结果给客户端(浏览器)
* 2. 解析成功处理流程:从令牌中获取用户数据,将用户数据存储到ThreadLocal,以便其他地方使用,
* 当本次请求完成,本ThreadLocal中取出,以免造成内存泄漏
*</p>
*/
public class TokenParseFilter implements Filter {
/**
* 需要放行的资源,支持模糊匹配
*/
public static final List<String> PERMIT_URL =
Arrays.asList("/api/user/login", "/swagger-resources/**", "/webjars/**", "/v2/**", "/swagger-ui/**","/swagger-ui.html/**", "doc.html");
private static final ThreadLocal<Map<String, Object>> THREAD_LOCAL = new ThreadLocal<>();
/**
* token解析需要用到的工具类
*/
private final JwtUtil jwtUtil;
public TokenParseFilter(JwtUtil jwtUtil) {
this.jwtUtil = jwtUtil;
}
/**
* 拦截所有请求,默认放行的请求需要配置在permitUrl当中
* @param servletRequest 请求
* @param servletResponse 响应
* @param chain 后续的过滤器执行链
* @throws IOException io异常
* @throws ServletException servlet异常
*/
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
// 判断该处理是否应该拦截
String uri = request.getRequestURI();
AntPathMatcher matcher = new AntPathMatcher();
for (String pattern : PERMIT_URL) {
boolean match = matcher.match(pattern, uri);
if (match) {
// 放行
chain.doFilter(request, response);
return;
}
}
String token = request.getHeader("Authentication");
if (ObjectUtils.isEmpty(token) || token.equalsIgnoreCase("")) {
// 异常抛出不能被全局处理器捕获,需要手动处理响应
Result<Object> result = ResultUtil.build(SysCode.USER_TOKEN_NOT_EXIST, "用户令牌未携带,请登录qwq~~", false, null);
ResponseUtil.response(request, response, result);
return;
}
// 解析token,有异常抛出
try {
Map<String, Object> userData = jwtUtil.validateToken(token);
// 将用户数据存放到ThreadLocal
THREAD_LOCAL.set(userData);
} catch (AuthenticationException exception) {
Result<Object> result = ResultUtil.build(exception.getCode(), exception.getMessage(), false, null);
ResponseUtil.response(request, response, result);
return;
}
// 执行后续过滤器链
chain.doFilter(request, response);
}
}
上述代码中的: "/swagger-resources/**", "/webjars/**", "/v2/**", "/swagger-ui/**","/swagger-ui.html/**", "doc.html"为swagger需要放行的资源
- 如果项目中使用了全局异常处理(@RestControllerAdvice),返回json以swagger需要的页面不匹配,需要指定@RestControllerAdvice扫描的包,解决方案如下
@RestControllerAdvice(basePackages = "cn.chiatso.blog.controller")
public class GlobalHandlerController {
/**
* 统一处理所有异常
* @param exception 接受到controller处理过程中所抛出的异常信息
* @return
*/
@ExceptionHandler
public Result<Object> handleError(Exception exception) {
exception.printStackTrace();
if (exception instanceof AuthenticationException) {
// TODO 认证异常不会在这里被处理,原因是过滤器中的执行不会被全局异常所捕获
AuthenticationException realEx = (AuthenticationException) exception;
return ResultUtil.error(realEx.getCode(), realEx.getMessage(), null);
}
return ResultUtil.error();
}
}
- 如果访问不到swagger文档主页,则可以指定静态资源的映射,如下示例
/**
* @author chiatso
* @date 2023/10/07
* @description webmvc自定义配置
*/
@Configuration
public class WebConfigure implements WebMvcConfigurer {
/**
* spring boot静态资源处理
* @param registry
*/
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
// swagger需要处理的静态资源
registry.addResourceHandler("/doc.html")
.addResourceLocations("classpath:/META-INF/resources/");
registry.addResourceHandler("/swagger-ui/**")
.addResourceLocations("classpath:/META-INF/resources/");
registry.addResourceHandler("/webjars/**")
.addResourceLocations("classpath:/META-INF/resources/webjars/");
}
}