相信无论是前端还是后端开发,都或多或少地被接口文档折磨过。前端经常抱怨后端给的接口文档与实际情况不一致。后端又觉得编写及维护接口文档会耗费不少精力,经常来不及更新。
其实无论是前端调用后端,还是后端调用后端,都期望有一个好的接口文档。但是这个接口文档对于程序员来说,就跟注释一样,经常会抱怨别人写的代码没有写注释,然而自己写起代码起来,最讨厌的,也是写注释。
所以仅仅只通过强制来规范大家是不够的,随着时间推移,版本迭代,接口文档往往很容易就跟不上代码了。
Swagger是什么?它能干什么?
Swagger 是一个规范和完整的框架,用于生成、描述、调用和可视化 RESTful 风格的 Web 服务。
-
接口的文档在线自动生成。
-
功能测试。
框架说明
现在SWAGGER官网主要提供了几种开源工具,提供相应的功能。可以通过配置甚至是修改源码以达到你想要的效果。
-
Swagger Codegen(代码生成器): 通过Codegen 可以将描述文件生成html格式和cwiki形式的接口文档,同时也能生成多种语言的服务端和客户端的代码。支持通过jar包,docker,node等方式在本地化执行生成。也可以在后面的Swagger Editor中在线生成。
-
Swagger UI:提供了一个可视化的UI页面展示描述文件。接口的调用方、测试、项目经理等都可以在该页面中对相关接口进行查阅和做一些简单的接口请求。该项目支持在线导入描述文件和本地部署UI项目。
-
Swagger Editor: 类似于markendown编辑器的编辑Swagger描述文件的编辑器,该编辑支持实时预览描述文件的更新效果。也提供了在线编辑器和本地部署编辑器两种方式。
-
Swagger Inspector: 感觉和postman差不多,是一个可以对接口进行测试的在线版的postman。比在Swagger UI里面做接口请求,会返回更多的信息,也会保存你请求的实际请求参数等数据。
-
Swagger Hub:集成了上面所有项目的各个功能,你可以以项目和版本为单位,将你的描述文件上传到Swagger Hub中。在Swagger Hub中可以完成上面项目的所有工作,需要注册账号,分免费版和收费版。
PS(附录):
- Springfox Swagger: Spring 基于swagger规范,可以将基于SpringMVC和Spring Boot项目的项目代码,自动生成JSON格式的描述文件。本身不是属于Swagger官网提供的,在这里列出来做个说明,方便后面作一个使用的展开。
Spring Boot整合swagger
mvn
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.5.5</version>
<relativePath></relativePath>
</parent>
<groupId>org.example</groupId>
<artifactId>swagger2-study</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<springfox.version>2.9.2</springfox.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.4</version>
</dependency>
<!-- 文档生成 -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>${springfox.version}</version>
</dependency>
<!-- <dependency>-->
<!-- <groupId>io.springfox</groupId>-->
<!-- <artifactId>springfox-swagger-ui</artifactId>-->
<!-- <version>${springfox.version}</version>-->
<!-- </dependency>-->
<!-- 这里使用 swagger-bootstrap-ui 替代了原有丑陋的ui -->
<!-- swagger-bootstrap-ui是基于swagger接口api实现的一套UI,因swagger原生ui是上下结构的,在浏览接口时不是很清晰,
所以,swagger-bootstrap-ui是基于左右菜单风格的方式,适用与我们在开发后台系统左右结构这种风格类似,方便与接口浏览 -->
<!-- 这个依赖和官方的依赖的区别
1.访问地址的不同
官方:http://localhost:8763/swagger-ui.html#/
bootstrap-ui访问地址: http://localhost:8080/doc.html#/
2.界面展示不同
官方: 竖向展示参数
bootstrap-ui: 是横向展示参数
3.启动类添加注解的不同
官方: 需要添加@EnableSwagger2
bootstrap-ui: 不需要添加@EnableSwagger2 -->
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>swagger-bootstrap-ui</artifactId>
<version>1.9.0</version>
</dependency>
</dependencies>
</project>
配置类
package com.example.swagger.config;
import org.springframework.beans.factory.annotation.Value;
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
// 注解开启Swagger功能
@EnableSwagger2
public class SwaggerConfig {
@Value("${swagger.enabled}")
Boolean swaggerEnabled;
@Bean
public Docket apiDocket2() {
return new Docket(DocumentationType.SWAGGER_2).groupName("dev");
}
/**
* 通过createRestApi(apiDocket)函数创建Docket的Bean
*
* @return
*/
@Bean
public Docket apiDocket() {
// 也可以通过这种方式判断在哪个环境
// public Docket apiDocket(Environment environment) {
// Profiles profiles = Profiles.of("dev","test");
// boolean flag = environment.acceptsProfiles(profiles);
return new Docket(DocumentationType.SWAGGER_2) // 指定生成的文档的类型是Swagger2
.enable(swaggerEnabled) // 是否启动swagger,默认为true,false在页面中将不能访问swagger
// .pathMapping("/swagger") // 通过接口直接访问swagger,不配置的话默认使用/swagger-ui.html访问
.select() // 选择哪些路径和api会生成document
// 返回一个ApiSelectorBuilder实例用来控制哪些接口暴露给Swagger来展现
.apis(RequestHandlerSelectors.basePackage("com.example.swagger.controller")) // 需要应用的接口所在的包,可以添加多个在不同包下的controller
/**
// 生成接口api的方式一
.apis(RequestHandlerSelectors.basePackage("com.example.swagger.controller")) // 需要应用的接口所在的包,可以添加多个在不同包下的controller
// .apis(RequestHandlerSelectors.basePackage("org.example.ctrl"))
/
/ 生成接口api的方式二
// .apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class)) // 扫描所有使用了@Api注解的接口类,用这种方式生成api更灵活
withMethodAnnotation() 扫描方法上的注解,参数是一个注解的反射对象
withClassAnnotation() 扫描类上的注解
withMethodAnnotation(GetMapping.class) 扫面所有被GetMapping注解的方法
withClassAnnotation(RestController.class) 扫面所有被RestController注解的类
// 生成接口api的方式三 扫描所有 .apis(RequestHandlerSelectors.any())
*/
.paths(PathSelectors.any()) // 过滤,对所有路径进行监控
// .paths(PathSelectors.ant("/kuang/**")) 扫描com.example.swagger.controller下所有以kuang开头的请求路径
.build()
.apiInfo(apiInfo()) // 用来创建该Api的基本信息(这些基本信息会展现在文档页面中)
.groupName("狂神"); // 通过创建多个 Docket 来实现分组,通过扫描不同的包来实现不同的分组
}
/**
* 设置摘要信息
*
* @return
*/
private ApiInfo apiInfo() {
// 用ApiInfoBuilder进行摘要定制
return new ApiInfoBuilder()
.title("接口示例API") // 设置标题
.description("项目demo接口示例API模板") // 描述
.contact(new Contact("demoAuth", "https://blog.csdn.net/MrBInso343", "121@163.com")) // 设置作者信息、联系方式:Contact(String name, String blogUrl, String email)
.version("1.0.0") // 版本
.termsOfServiceUrl("") // 公司或者项目的地址
.build();
}
}
// 我做配置的时候是没有被拦截的,如果被拦截,可以添加这个配置类
package awu.demo.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
/**
* 解决swagger被拦截的问题
* @param registry
*/
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry){
// 解决静态资源无法访问
registry.addResourceHandler("/**")
.addResourceLocations("classpath:/static/");
// 解决swagger无法访问
registry.addResourceHandler("/swagger-ui.html")
.addResourceLocations("classpath:/META-INF/resources/");
// 解决swagger的js文件无法访问
registry.addResourceHandler("/webjars/**")
.addResourceLocations("classpath:/META-INF/resources/webjars/");
}
}
bootstrap-ui访问地址路径swagger-bootstrap-ui\1.9.0\swagger-bootstrap-ui-1.9.0.jar!\META-INF\resources\doc.html
pojo
package awu.demo.pojo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
* @program: jpademo
* @description: Employee
* @author: ZengGuangfu
* @create 2018-10-23 10:20
*/
@Data
@ApiModel(value = "用户对象模型")
public class Employee {
private Integer employeeId;
@ApiModelProperty(value = "用户名",required = true)
private String userName;
@ApiModelProperty(value = "年龄",required = true)
private Integer age;
@ApiModelProperty(value = "graId",required = true)
private Integer graId;
public interface Audit{};
public interface Children{};
}
controller
package awu.demo.controller;
import awu.demo.pojo.Employee;
import io.swagger.annotations.*;
import org.springframework.web.bind.annotation.*;
import java.util.ArrayList;
import java.util.List;
/**
*
* @Api : 用在类上,说明该类的主要作用。
* tags
* value 两者都表示说明
*
* @ApiOperation:用在方法上,给API增加方法说明。
* value用于方法描述
* notes用于提示内容
* tags可以重新分组(视情况而用)
*
* @ApiImplicitParams : 用在方法上,包含一组参数说明。
* @ApiImplicitParam:用来注解来给方法入参增加说明。
* name–参数ming
* value–参数说明
* dataType–数据类型
* paramType–参数类型
* example–举例说明
*
*
*
* @ApiResponses:用于表示一组响应。
*
* * @ApiResponse:用在@ApiResponses中,一般用于表达一个错误的响应信息
*
* * **code**:数字,例如400
*
* * **message**:信息,例如"请求参数没填好"
*
* * **response**:抛出异常的类
*
*
*
* * @ApiModel:用在返回对象类上,描述一个Model的信息(一般用在请求参数无法使用@ApiImplicitParam注解进行描述的时候)
* value–表示对象名
* description–描述
*
* * @ApiModelProperty:描述一个model的属性
* value–字段说明
* name–重写属性名字
* dataType–重写属性类型
* required–是否必填
* example–举例说明
* hidden–隐藏
*
*
*
*
* @ApiIgnore()用于类,方法,方法参数
* 表示这个方法或者类被忽略,可以不被swagger显示在页面上
*
*
*
*
*
* 以下仅仅是一个例子,其实我个人在开发中很少使用@ApiImplicitParam 作为参数的描述,这样描述在参数过多的条件下会有点麻烦。
*
* 个人一般是将参数封装为一个完整对象(特别是GET方法),并利用@ApiModel注解去定义参数,
* 如果不需要作为查询条件的,则加一个hidden = true,如果是必填属性,则增加一个required = true即可。
*/
@RestController
@RequestMapping("emp")
@Api(value = "用户管理类")
public class EmployeeController {
/**
* 增加人物
* @param employee
* @return
*/
@PostMapping(value = "employee")
@ApiOperation(value = "新增一个用户",notes = "新增之后返回对象")
@ApiImplicitParam(paramType = "query",name = "employee",value = "用户",required = true)
@ApiResponse(code = 400,message = "参数没有填好",response = String.class)
public String insert(Employee employee){
return null;
}
/**
* 删除单个用户
* @param id
* @return
*/
@DeleteMapping(value = "employee/{id}")
@ApiOperation(value = "删除用户",notes = "根据成员id删除单个用户")
@ApiImplicitParam(paramType = "path",name = "id",value = "用户id",required = true,dataType = "Integer")
@ApiResponse(code = 400,message = "参数没有填好",response = String.class)
public String delete(@PathVariable("id")Integer id){
try{
return null;
}catch (Exception e){
e.printStackTrace();
return null;
}
}
/**
* 修改单个成员
* @param employee
* @return
*/
@PutMapping(value = "employee/{id}")
@ApiOperation(value = "修改用户信息",notes = "根据成员id修改单个用户")
public String update(Employee employee){
return null;
}
/**
* 获取所有成员,升序排列
* @return
*/
@GetMapping(value = "employee/sort")
@ApiOperation(value = "查询全部用户",notes = "默认根据升序查询全部用户信息")
public List<Employee> findAll(){
return new ArrayList<Employee>();
}
/**
* 获取所有成员,升序排列
* @return
*/
@GetMapping(value = "employee/pageSort")
@ApiOperation(value = "查询用户信息",notes = "查询用户信息")
@ApiImplicitParams({
@ApiImplicitParam(paramType = "query",name = "sort",value = "排序方式:asc|desc",dataType = "String",required = true),
@ApiImplicitParam(paramType = "query",name = "pagenumber",value = "第几页",dataType = "Integer",required = true),
@ApiImplicitParam(paramType = "query",name = "pageSize",value = "分页数",dataType = "Integer",required = true)
})
public List<Employee> findAllByPage(String sort,Integer pagenumber,Integer pageSize){
return new ArrayList<Employee>();
}
/**
* 自定义拓展jpa,根据用户名查找单个用户
* @param username
* @return
*/
@GetMapping(value = "employee/find/{username}")
@ApiOperation(value = "查询用户信息",notes = "根据用户登录名查询该用户信息")
@ApiImplicitParam(paramType = "path",name = "username",value = "用户登录名",required = true,dataType = "String")
public Employee findByUsername(@PathVariable("username") String username){
return null;
}
/**
* 测试用
* @return
*/
@GetMapping(value = "employee/grade")
public List<Object[]> findEmployeeAndGrade(){
return new ArrayList<>();
}
/**
* 修改单个成员 传入model
* @param employee
* @return
*/
@PostMapping(value = "employee/model")
@ApiOperation(value = "修改用户信息model",notes = "根据成员id修改单个用户")
public String updateModel(@RequestBody Employee employee){
return null;
}
}
@ApiParam和@ApiImplicitParam的区别
@ApiParam和@ApiImplicitParam的功能是相同的,但是@ApiImplicitParam的适用范围更广。在非JAX-RS的场合(比如使用servlet提供HTTP接口),只能使用@ApiImplicitParam进行参数说明。我认为,这是因为接口并不显式展示入参的名称,所以没有地方去使用@ApiParam,只能在接口的方法声明下方写@ApiImplicitParam
不可以让用户看到公司的接口,这肯定是不安全的。
部分内容转载自:
https://blog.csdn.net/qq_39505065/article/details/107844220
https://blog.csdn.net/u014231523/article/details/76522486
https://www.bilibili.com/video/BV1PE411i7CV/?p=47&spm_id_from=pageDriver&vd_source=64c73c596c59837e620fed47fa27ada7
智哥不能给刘十三找到工作,不能借他钱,不能帮他买房子,但是智哥尊重他。
刘十三各方面都很平凡,如果想要这样的朋友,只要到天桥往下望,行走的全是刘十三。
他曾经想把刘十三写成一首歌,歌词是,我有个朋友叫刘十三。
开头这一句就没写下去了,刘十三完全没有什么可写的。
云边有个小卖部
张嘉佳