在API驱动的开发时代,维护实时、准确的接口文档仍是团队协作的核心痛点。Swagger通过自动化扫描与注解解析,将文档与代码深度绑定,彻底终结“文档滞后于代码”的困境。而掌握分组配置、安全策略及UI定制等高阶技巧,更能让文档引擎成为API治理的枢纽——从精准的环境隔离,到动态的Mock数据生成,直至无缝嵌入CI/CD流水线。本指南将深入解析Swagger的进阶实践,助你释放自动化文档的完整潜能
多分组配置
微服务模块化开发时,需按业务模块分离API文档。
package com.morris.swagger.demo.complex.group;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.oas.annotations.EnableOpenApi;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
@Configuration
@EnableOpenApi //Enable open api 3.0.3 spec
public class SwaggerConfig {
@Bean
public Docket userApi() {
return new Docket(DocumentationType.OAS_30)
.groupName("用户模块")
.select()
.paths(PathSelectors.ant("/user/**"))
// 也可以指定某个包
// .apis(RequestHandlerSelectors.basePackage("com.example.user"))
.build();
}
@Bean
public Docket orderApi() {
return new Docket(DocumentationType.OAS_30)
.groupName("订单模块")
.select()
.paths(PathSelectors.ant("/order/**"))
.build();
}
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
- 25.
- 26.
- 27.
- 28.
- 29.
- 30.
- 31.
- 32.
- 33.
可以根据具体的包名或者路径进行分割。
效果:访问swagger-ui.html
时通过顶部下拉菜单切换模块。
隐藏敏感参数
例如屏蔽实体类中的密码字段。
手动指定参数与响应
@ApiImplicitParam和@ApiImplicitParams用于精准描述请求参数
适用场景:非实体类参数(如@RequestParam、@PathVariable、@RequestHeader等),或无法通过实体类字段自动生成文档的参数。
@ApiResponse和@ApiResponses用于定义接口返回值规范。
适用场景:明确接口的响应状态码、业务含义及返回数据结构,尤其适用于异常状态(如400、500)
@ApiImplicitParam的局限性:
- 不替代校验注解,仅生成文档,不影响实际请求校验(如@RequestParam(required=true)仍需独立配置)
- 泛型支持弱:dataType不支持泛型(如List),需简写为List。
@ApiResponse的响应一致性:若方法返回String但response声明为User.class,文档与代码将不一致,导致误导
Knife4j替代原生UI
Knife4j界面更美观,支持离线文档导出。
集成只需要引入下面的依赖:
访问路径:http://localhost:8080/doc.html
每个接口header中增加token描述
现在的项目开发基本上都是前后端分离,很多API的调用都需要经过token的验证,本文就介绍怎么在swagger的header中自动添加token,以及各种实现方式的优缺点。
在每个接口上手动添加header
这种方式就是在每个接口上手动添加header。
package com.morris.swagger.web;
import com.morris.swagger.vo.R;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("header")
@Api(tags = "为单个接口添加header")
public class HeaderController {
@GetMapping("token")
@ApiImplicitParams({
@ApiImplicitParam(paramType = "header", name = "token", required = true),
})
@ApiOperation("token")
public R<Void> header() {
return R.ok();
}
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
效果如下:
这种方法的缺点就是需要手动在每个方法上添加@ApiImplicitParam注解来指定header参数,那么有没有一种方式一次性在所有的方法上面加上header参数呢?
全局对每个API都添加header字段
重点是在原先swagger的Docket对象后面添加globalOperationParameters方法。
package com.morris.swagger.demo.token.global;
import io.swagger.annotations.ApiOperation;
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.schema.ScalarType;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Contact;
import springfox.documentation.service.ParameterType;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import static java.util.Collections.singletonList;
@Configuration
@EnableOpenApi //Enable open api 3.0.3 spec
public class SwaggerConfig {
@Bean
public Docket createRestApi() {
return new Docket(DocumentationType.OAS_30)
.apiInfo(apiInfo())
.select()
.apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class))
.paths(PathSelectors.regex("(?!/error.*).*"))
.build()
.globalRequestParameters(
singletonList(new springfox.documentation.builders.RequestParameterBuilder()
// 不能叫Authorization
.name("token")
.description("token")
.in(ParameterType.HEADER)
.required(true)
.query(q -> q.model(m -> m.scalarModel(ScalarType.STRING)))
.build()));
}
private ApiInfo apiInfo() {
return new ApiInfoBuilder()
.title("Swagger API")
.description("this is a description")
.termsOfServiceUrl("http://springfox.io")
.contact(new Contact("springfox", "https://morris131.github.io", "morris131@163.com"))
.license("Apache License Version 2.0")
.licenseUrl("https://github.com/springfox/springfox/blob/master/LICENSE")
.version("3.0")
.build();
}
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
- 25.
- 26.
- 27.
- 28.
- 29.
- 30.
- 31.
- 32.
- 33.
- 34.
- 35.
- 36.
- 37.
- 38.
- 39.
- 40.
- 41.
- 42.
- 43.
- 44.
- 45.
- 46.
- 47.
- 48.
- 49.
- 50.
- 51.
- 52.
- 53.
- 54.
运行的效果如图:
全站统一header设置
这种方案就简化了第一种方案,在swagger UI上出现一个Authorize按钮,一次输入header参数,全站使用。
package com.morris.swagger.demo.token.site;
import io.swagger.annotations.ApiOperation;
import io.swagger.v3.oas.models.security.SecurityScheme;
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.*;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spi.service.contexts.SecurityContext;
import springfox.documentation.spring.web.plugins.Docket;
import java.util.Arrays;
import java.util.List;
import static java.util.Collections.singletonList;
@Configuration
@EnableOpenApi //Enable open api 3.0.3 spec
public class SwaggerConfig {
@Bean
public Docket createRestApi() {
return new Docket(DocumentationType.OAS_30)
.apiInfo(apiInfo())
.select()
.apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class))
.paths(PathSelectors.regex("(?!/error.*).*"))
.build()
.securityContexts(Arrays.asList(securityContext()))
// ApiKey的name需与SecurityReference的reference保持一致
.securitySchemes(Arrays.asList(new ApiKey("token", "token", SecurityScheme.In.HEADER.name())));
}
private SecurityContext securityContext() {
return SecurityContext.builder()
.securityReferences(defaultAuth())
//.forPaths(PathSelectors.regex("/*.*"))
.build();
}
private List<SecurityReference> defaultAuth() {
AuthorizationScope authorizationScope
= new AuthorizationScope("global", "accessEverything");
AuthorizationScope[] authorizationScopes = new AuthorizationScope[1];
authorizationScopes[0] = authorizationScope;
return singletonList(
new SecurityReference("token", authorizationScopes));
}
private ApiInfo apiInfo() {
return new ApiInfoBuilder()
.title("Swagger API")
.description("this is a description")
.termsOfServiceUrl("http://springfox.io")
.contact(new Contact("springfox", "https://morris131.github.io", "morris131@163.com"))
.license("Apache License Version 2.0")
.licenseUrl("https://github.com/springfox/springfox/blob/master/LICENSE")
.version("3.0")
.build();
}
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
- 25.
- 26.
- 27.
- 28.
- 29.
- 30.
- 31.
- 32.
- 33.
- 34.
- 35.
- 36.
- 37.
- 38.
- 39.
- 40.
- 41.
- 42.
- 43.
- 44.
- 45.
- 46.
- 47.
- 48.
- 49.
- 50.
- 51.
- 52.
- 53.
- 54.
- 55.
- 56.
- 57.
- 58.
- 59.
- 60.
- 61.
- 62.
- 63.
- 64.
- 65.
全局设置完这个参数后,每个方法都会带上header参数。
这种方式好像不传参数也能访问接口,没法做到必填校验。