@RestController
@RequestMapping("/users")
public class UserController {
@Autowired
private UserService userService;
* 查询单个用户
@GetMapping("/{userId}")
public ResponseResult getUserById(@PathVariable long userId) {...}
* 查询多个用户
@GetMapping()
public ResponseResult getUsers() {...}
* 添加多个用户
@PostMapping()
public ResponseResult addUsers(@RequestBody List<User> users) {...}
* 添加单个用户
@PostMapping("/user")
public ResponseResult addUser(@RequestBody User user) {...}
* 更新单个用户
@PutMapping("/user")
public ResponseResult updateUser(@RequestBody User user) {...}
* 删除单个用户
@DeleteMapping("/user")
public ResponseResult deleteUser(long userId) {...}
注意:此处我们只关注接口的定义、参数和返回值,具体实体不列出(详细可看源码:https://github.com/mianshenglee/my-example
)。
至此,示例工程可以正常运行,具有以下接口:
GET /users/{userId}
获取单个用户
GET /users
获取多个用户
POST /users
添加多个用户
POST /users/user
添加单个用户
PUT /users/user
更新单个用户
DELTE /users/user?userId=xx
删除单个用户
注意:接口的RequestMapping,如果不指定httpMethod,springfox会把这个方法的所有动作GET/POST/PUT/PATCH/DELETE的方法都列出来,因此,写接口时,请指定实际的动作。
3.2 引入swagger2与基本配置
3.2.1 添加springfox-swagger依赖
为了可以自动化生成符合swagger的接口描述文件,需要添加springfox的依赖,如下:
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>${swagger.version}</version>
</dependency>
复制代码
3.2.2 配置swagger
添加config包用于存放配置文件,添加以下Swagger2Config.java配置,如下:
@Configuration
@EnableSwagger2
public class Swagger2Config {
private ApiInfo apiInfo() {
@Bean
public Docket api(){
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo())
.select()
.apis(RequestHandlerSelectors.any())
.paths(PathSelectors.any())
.build();
private ApiInfo apiInfo() {
return new ApiInfoBuilder().title("接口说明文档")
.description("API文档描述")
.version("0.0.1-SNAPSHOT")
.build();
复制代码
此文件配置了Docket的Bean,包括api的基本信息,需要显示的接口(apis),需要过滤的路径(paths),这样,就可以生成符合规范的接口文档。
3.2.3 查看swagger自动生成的描述文档
使用上面的配置集成swagger2后,启动项目,输入http://localhost:8080/swaggerdemo/v2/api-docs
即可查看JSON格式的接口描述文档。如下所示:
此文档是符合OAS规范,但离可视化显示和使用还差一步,就是如何把这些信息展示在界面上。
3.3 添加swagger-ui界面交互
为了能可视化显示接口文档,springfox已提供界面显示组件,添加以下依赖:
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>${swagger.version}</version>
</dependency>
复制代码
添加此依赖后,引入的springfox-swagger-ui.jar包中有swagger-ui.html
页面,使用地址http://localhost:8080/swaggerdemo/swagger-ui.html
即可查看接口文档页面。在此文档中,可以查看接口定义、输入参数、返回值,也可以使用try it out
调用接口进行调试。如下:
至此,通过前面示例,已经以最简单的方式实现了使用swagger对接口文档的自动生成和UI显示,但相对于企业中实际开发使用接口文档,还是有一定距离,包括:
不能根据环境选择显示文档(可能开发环境需要显示,正式环境不需要)
接口没有功能说明和详细描述
输入参数详细说明和示例说明
返回值的详细说明和示例说明
所有controller接口都返回了,没有按需过滤需要的接口
所有接口都可以调试,存在权限认证问题
针对这些问题,在企业实践中,需要进行处理。
4. 【企业实践】配置参数化与包过滤
4.1 配置参数化
一份接口文档,一般都会有关于此文档的基本信息,包括文档标题、描述、接口版本、联系人等信息,swagger已提供了对应的配置项,如上面的示例中的apiInfo()
,把基本信息写在代码中进行配置,这样对修改不友好,因此,最好是把这些配置参数化,形成swagger的独立配置文件,避免修改基本信息导致代码的改动。
4.1.1 添加基本信息配置文件
在resources/config
目录下,添加swagger.properties
文件,把配置信息放到此文件中。
swagger.basePackage =me.mason.helloswagger.demo.controller
swagger.title = 应用API说明文档
swagger.description = API文档描述
swagger.version = 0.0.1-SNAPSHOT
swagger.enable = true
swagger.contactName = mason
swagger.contactEmail =
swagger.contactUrl =
swagger.license =
swagger.licenseUrl =
复制代码
对应的,在config
包下,添加SwaggerInfo对象,对应上述配置文件:
@Component
@ConfigurationProperties(prefix = "swagger")
@PropertySource("classpath:/config/swagger.properties")
@Data
public class SwaggerInfo {
private String basePackage;
private String antPath;
private String title = "HTTP API";
private String description = "Swagger 自动生成接口文档";
private String version ;
private Boolean enable;
private String contactName;
private String contactEmail;
private String contactUrl;
private String license;
private String licenseUrl;
复制代码
官方建议对于这种自定义的配置,最好添加以下spring-boot-configuration-processor
依赖,以生成配置元数据,否则在IDEA中,会有"spring boot configuration annotation processor not found in classpath"
的警告:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
复制代码
4.1.2 集成配置swagger文档
有了SwaggerInfo
配置类,在集成swagger2的apiInfo()
可以注入使用,以后需要改动时修改配置文件即可。
private ApiInfo apiInfo() {
return new ApiInfoBuilder().title(swaggerInfo.getTitle())
.description(swaggerInfo.getDescription())
.version(swaggerInfo.getVersion())
.licenseUrl(swaggerInfo.getLicenseUrl())
.contact(
new Contact(swaggerInfo.getContactName()
,swaggerInfo.getContactUrl()
,swaggerInfo.getContactEmail()))
.build();
复制代码
4.2 配置是否启用文档
对于不同环境,有可能需要启用和关闭接口文档,如在开发环境我们需要显示接口文档,但在正式线上环境,有时我们不希望对外公布接口。此需求可以使用enable
配置即可,结合配置文件中有swagger.enable
配置项(false关闭,true启用)。如下:
.enable(swaggerInfo.getEnable())
复制代码
4.3 包过滤
swagger组件会自动扫描有Controller
注解和RequestMapping
注解的接口,转换为接口描述。在开发过程中,我们不希望项目中所有的接口都显示出来,而是想把特定的包下的接口显示即可。使用方法apis()
,结合配置文件,可达到此效果。配置文件中swagger.basePackage
配置(多个包使用";"分隔)。添加以下过滤函数:
private List<Predicate<RequestHandler>> apisFilter() {
List<Predicate<RequestHandler>> apis = new ArrayList<>()
String basePackageStr = swaggerInfo.getBasePackage()
//包过滤
if (StrUtil.isNotEmpty(basePackageStr)) {
//支持多个包
String[] basePackages = basePackageStr.split(";")
if (null != basePackages && basePackages.length > 0) {
Predicate<RequestHandler> predicate = input -> {
// 按basePackage过滤
Class<?> declaringClass = input.declaringClass()
String packageName = declaringClass.getPackage().getName()
return Arrays.asList(basePackages).contains(packageName)
apis.add(predicate)
return apis
复制代码
上述功能是通过扫描到的接口与配置的包比较,在配置的包下则返回,否则过滤掉。然后在配置Docket时,添加此过滤。
builder = builder.apis(Predicates.or(apisFilter()))
复制代码
此处使用Predicates.or
,表示对返回的进行or操作,true的即显示。
5.【企业实践】使用注解添加文档内容
经过上面的配置化,我们已经可以灵活构建文档了。但文档的内容还不丰富,对接口的描述不详细,springfox
把对接口的描述都通过注解的方式来完成,主要包括以下几种注解:
数据模型注解:@ApiModel
,实体描述;@ApiModelProperty
,实体属性描述
接口注解:@ApiOperation
,接口描述;@ApiIgnore
,忽略此接口
控制器注解:@Api
,控制器描述
输入参数注解:@ApiImplicitParams
,接口多个参数;@ApiImplicitParam
,单个参数;@ApiParam
,单个参数描述
响应数据注解:@ResponseHeader
,响应值header;@ApiResponses
,响应值集;@ApiResponse
,单个响应值
下面我们对注解的使用进行说明,并用注解添加示例中的接口描述。
5.1 数据模型(model)注解说明
开发过程中,对于MVC模式的接口,使用M(Model)进行数据通信,因此,需要对此数据模型进行有效的描述。对应的注解有:
@ApiModel
:实体描述
@ApiModelProperty
:实体属性描述。
下表为@ApiModelProperty
的使用说明:
@ApiModel
public class User {
@ApiModelProperty(value="id",required = true,example = "1")
private long id;
@ApiModelProperty(value="姓名",required = true,example = "张三")
private String name;
@ApiModelProperty(value="年龄",example = "10")
private int age;
@ApiModelProperty(value="密码",required = true,hidden = true)
private String password;
复制代码
这样,在接口文档中,使用User
作为输入参数时,可以看到这个模型的描述,如下:
5.2 控制器及接口注解说明
springfox 在自动化生成文档时,如果不使用注解进行描述,控制器和接口基本是把代码的类名,方法名,接口映射作为显示的内容。对于控制器和具体某个接口的描述,分别有:
@ApiIgnore
: Swagger 文档不会显示拥有该注解的接口
@Api
: 对控制器的描述
在本示例中,在UserController
中进行相应描述,如下:
@Api(tags = "用户管理接口", description = "User Controller")
public class UserController {
@ApiOperation(value = "根据ID获取单个用户信息", notes = "根据ID返回用户对象")
...
@ApiOperation(value = "添加多个用户", notes = "使用JSON以数组形式添加多个用户")
...
复制代码
使用这些注解后,显示的文档如下:
5.3 接口输入参数注解说明
5.3.1 SpringMVC的控制器参数使用
在介绍Swagger的接口输入参数注解说明前,有必要对SpringMVC的接口参数有一个清晰的了解。我们知道SpringMVC是通过@RequestMapping
把方法与请求URL对应起来,而调用接口方法时需要把参数传给控制器。一般来说,SpringMVC的参数传递包括以下几种:
无注解参数
@RequestParam
注解参数
JSON对象参数
REST风格参数
为方便解释,在本示例中,提供了ParamDemoController
文件,里面对应给出了各种参数的示例,读者可参考查看,下面对这几种参数进行描述。
(1)无注解参数
无注解下获取参数,要求参数名称与HTTP请求参数名称一致,参数可以为空。如下接口:
@GetMapping("/no/annotation")
public Map<String,Object> noAnnotation(Integer intVal, Long longVal, String str){}
复制代码
调用时,可使用http://localhost:8080/no/annotation?inVal=10&longVal=100
,参数str
可为空。
(2)@RequestParam
注解参数
使用注解RequestParam获取参数,可以指定HTTP参数和方法参数名的映射,而且不能为空(但可通过required设置false来允许为空)。如下接口:
@GetMapping("/annotation")
public Map<String,Object> annotation(@RequestParam("int_val") Integer intVal,@RequestParam("long_val") Long longVal,@RequestParam(value="str_val", required = false) String str){}
复制代码
调用方法跟上面无注解的一样。但如果没有设置required=false
,则必须传入str
参数。
(3)数组参数
使用数组做为参数,调用接口时,数组元素使用逗号(,
)分隔。如下接口:
@GetMapping("/array")
public Map<String,Object> requestArray(int[]intArr,long[]longArr,String[] strArr){}
复制代码
调用此接口,可使用http://localhost:8080/array?intArr=10,11,12&longArr=100,101,102&strArr=a,b,c
。
(4)JSON对象参数
当传输的数据相对复杂,或者与定义的模型相关,则需要把参数组装成JSON进行传递,当前端调用接口时使用JSON
请求体,SpringMVC可通过@RequestBody
注解,实现JSON参数的映射,并进行合适的对象转换。如下:
@PostMapping("/add/user")
public User addUser(@RequestBody User user){}
复制代码
调用接口时,使用JSON传递参数,通过RequestBody注解得到JSON参数,映射转换为User对象。如果是数组(List)对象,同样支持。
(5)REST风格参数
对于REST风格的URL,参数是写在URL中,如users/1
,其中1
是用户ID参数。对于此类参数,SpringMVC使用@PathVariable
进行实现,其中使用{}
作为占位符。如下:
@GetMapping("/users/{id}")
public User getUser(@PathVariable("id") Long id){}
复制代码
5.3.2 swagger输入参数注解说明
对于上面提到的的SpringMVC参数,springfox已经做了自动处理,在swagger-ui
文档中显示时,也会根据参数类型进行显示。在swagger中,参数类型分为:
path:以地址的形式提交数据,根据 id 查询用户的接口就是这种形式传参
query:Query string 的方式传参,对应无注解和@RequestParam
注解参数
header:请求头(header)中的参数
form:以 Form 表单的形式提交,如上传文件,属于此类
body:以JSON方式传参
在描述输入参数时,swagger在以下注解:
@ApiImplicitParams
: 描述接口的参数集。
@ApiImplicitParam
: 描述接口单个参数,与 @ApiImplicitParams
组合使用。
@ApiParam
:描述单个参数,可选
其中,@ApiParam
与单个参数一起用,@ApiImplicitParam
使用在接口描述中,两者的属性基本一致。@ApiImplicitParam
的属性如下所示:
以下是对使用JSON对象数组作为参数的接口,如下:
@ApiOperation(value = "添加多个用户", notes = "使用JSON以数组形式添加多个用户")
@ApiImplicitParams({
@ApiImplicitParam(name = "users", value = "用户JSON数组", required = true, dataType = "User",allowMultiple = true)
@PostMapping()
public ResponseResult addUsers(@RequestBody List<User> users) {}
复制代码
此示例中,使用@RequestBody
注解,传递List<User>
的JSON数组参数,因此,使用了allowMultiple
属性,dataType为User
,而它的paramType
为body
。swagger文档如下:
5.4 接口响应数据注解说明
对于使用了@RestController
注解的控制器,SpringMVC会自动把返回的数据转为json数据。我们在开发过程中,一般都会把返回数据做统一封装,返回状态,信息,实际数据等等。如本示例中,以ResponseResult
作为统一返回结果,返回的数据使用泛型(若不使用泛型,后续swagger在显示返回结果时对于Model的属性则显示不出来),如下:
@NoArgsConstructor
@AllArgsConstructor
@Data
public class ResponseResult<T> {
@ApiModelProperty(value="返回状态",example = "true")
private boolean success;
@ApiModelProperty(value="错误信息代码")
private String errCode;
@ApiModelProperty(value="错误显示信息")
private String errShowMsg;
@ApiModelProperty(value="错误信息")
private String errMsg;
@ApiModelProperty(value = "返回数据",notes = "若返回错误信息,此数据为null或错误信息对象")
private T resultData;
复制代码
在接口返回值中,若不使用泛型指定具体Model,只返回ResponseResult
,则只会显示此对象中的属性,而具体的resultData中的内容无法显示,如下:
因此,对于统一返回,需要指定具体返回的模型,这样,对于实际返回的模型就有对应的描述。如下:
@ApiOperation(value = "获取所有用户", notes = "返回全部用户")
@GetMapping()
public ResponseResult<List<User>> getUsers() {}
对于响应消息,swagger提供了默认的401,403,404的响应消息,也可以自己针对接口进行指定,有以下注解可使用:
@ApiResponses
:响应消息集
@ApiResponses
:响应消息, 描述一个错误的响应信息 ,与@ApiResponses
一起使用
@ResponseHeader
:响应头设置
6.【企业实践】接口认证
对于对外发布的接口,一般都需要进行权限校验,需要登录的用户才可以调用,否则报权限不足。对于api的安全认证,一般有以下几种模式(可参考swagger文档:https://swagger.io/docs/specification/authentication/
):
Basic 认证:使用Authorization
请求头,用户名和密码使用Base64编码,如:Authorization: Basic ZGVtbzpwQDU1dzByZA==
Bearer 认证:使用Authorization
请求头,由服务端产生加密字符token,客户端发送请求时把此token的请求头发到服务端作为凭证,token格式是Authorization: Bearer <token>
API Keys认证:使用请求头,参数或cookie,把只能客户端和服务端知道的密钥进行传输。
OAuth2: 一个开放标准,允许用户授权第三方移动应用访问他们存储在另外的服务提供者上的信息,而不需要将用户名和密码提供给第三方移动应用或分享他们数据的所有内容 。
对于前后端分离的的springboot项目,现在很多采用jwt的认证方式(属于Bearer认证),需要先获取token,然后在调用接口时,添加类似Authorization: Bearer <token>
的请求头来对接口进行认证。针对这种方式,在Swagger中有以下三种方法来实现:
在接口参数描述中添加@ApiImplicitParam
,其中参数类型是ParamType=header
添加全局接口参数
添加安全认证上下文
6.1 添加接口认证参数
针对需要认证的接口,直接使用@ApiImplicitParam
,其中参数类型是ParamType=header
即可,参数的描述前面已有介绍。如下:
@ApiImplicitParam(name = "Authorization", value = "token,格式: Bearer <token>", required = false, dataType = "String",paramType = "header")
复制代码
这种方式的缺点是,针对每一个接口,都需要添加这个参数描述,而描述都是一样的,重复工作。
6.2 添加全局接口参数
swagger配置中,方法globalOperationParameters
可以设置全局的参数。
ParameterBuilder tokenPar = new ParameterBuilder();
List<Parameter> pars = new ArrayList<Parameter>();
tokenPar.name("Authorization").description("token令牌")
.modelRef(new ModelRef("string"))
.parameterType("header")
.required(true).build();
pars.add(tokenPar.build());
docket.globalOperationParameters(pars);
复制代码
这种方式缺点也明显,由于设置了全局参数,则所有接口都需要此参数,若有某些接口不需要,则需要进行特殊处理。
6.3 添加安全认证上下文
设置认证模式,并使用正则式对需要认证的接口进行筛选,这样swagger界面提供统一的认证界面。如下:
docket.securitySchemes(securitySchemes())
.securityContexts(securityContexts());
...
private List<ApiKey> securitySchemes() {
return Lists.newArrayList(
new ApiKey("Authorization", "Authorization", "header"));
private List<SecurityContext> securityContexts() {
return Lists.newArrayList(
SecurityContext.builder()
.securityReferences(defaultAuth())
.forPaths(PathSelectors.regex("^(?!login).*$"))
.build()
List<SecurityReference> defaultAuth() {
AuthorizationScope authorizationScope = new AuthorizationScope("global", "认证权限");
return Lists.newArrayList(
new SecurityReference("Authorization", new AuthorizationScope[]{authorizationScope}));
复制代码
设置后,输入登录即可,效果如下:
7. 总结
本篇文章针对swagger的使用和企业实践进行了详细描述,主要介绍了swagger的原理,如何使用springboot与swagger结合创建接口文档,对swagger进行参数化配置及包过滤,swagger注解的详细使用,接口认证方法等,本文中使用的示例代码已放在github:https://github.com/mianshenglee/my-example
,有兴趣的同学可以pull代码,结合示例一起学习。
swagger官网: https://swagger.io/
springfox官网: http://springfox.github.io/springfox/
在 Spring Boot 项目中使用 Swagger 文档:https://www.ibm.com/developerworks/cn/java/j-using-swagger-in-a-spring-boot-project/index.html
SpringBoot2 配置swagger2并统一加入认证参数:https://www.jianshu.com/p/7a24d202b395
一篇文章带你搞懂 Swagger 与 SpringBoot 整合: https://mp.weixin.qq.com/s/1KuBFfMugJ4pf3cSEFfXfw
查阅了十几篇学习资源后,我总结了这份AI学习路径
java应用监测(8)-阿里诊断工具arthas
java应用监测(7)-在线动态诊断神器BTrace
java应用监测(6)-第三方内存分析工具MAT
mongo同步-spring batch(8)的mongo读写组件使用
关注我的公众号,获取更多技术记录: