赞
踩
Swagger
目前是比较主流的RESTful
风格的API
文档工具,做过开发的人应该都用过它
它提供了一套工具和规范,让开发人员能够更轻松地创建和维护可读性强、易于使用和交互的API文档(官方口吻)。
title: Swagger
desc: Swagger 官方网站
logo: https://static1.smartbear.co/swagger/media/assets/images/swagger_logo.svg
link: https://swagger.io/
以往在没有这样的API
文档工具,开发人员需要手动编写和维护功能API的文档。而且,由于API
变更往往难以及时更新到文档中,这可能会给依赖文档的开发者带来困惑。
Swagger
的特点:
API
文档,能生成的绝对不手写,而且API
文档与API定义会同步更新。Web
界面,支持API
在线测试,可以直接在界面上直接设置参数测试,不用额外的测试工具或插件。Java、PHP、Python
等语言都支持,喜欢什么语言构建API都行。Swagger2 ,访问路径:http://127.0.0.1:8080/swagger-ui.html
<!-- Swagger2 -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.9.0</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.9.0</version>
</dependency>
创建配置类SwaggerConfig
,类标注@EnableSwagger2
注解是关键,到这最简单的Swagger
文档环境就搭建好了。
import org.springframework.context.annotation.Configuration; import springfox.documentation.swagger2.annotations.EnableSwagger2; @Configuration @EnableSwagger2 public class SwaggerConfig { @Bean public Docket createRestApi() { return new Docket(DocumentationType.SWAGGER_2) .select() .apis(RequestHandlerSelectors.basePackage("cn.hr.controller")) .paths(PathSelectors.any()) .build(); } }
Swagger3,访问路径:http://127.0.0.1:8080/swagger-ui/index.html
<!-- swagger 3 配置 -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-boot-starter</artifactId>
<version>3.0.0</version>
</dependency>
@Configuration
@EnableSwagger2
public class SwaggerConfig {
@Bean
public Docket createRestApi() {
return new Docket(DocumentationType.OAS_30)
.select()
.apis(RequestHandlerSelectors.basePackage("cn.hr.controller"))
.paths(PathSelectors.any())
.build();
}
}
启动时可能会报如下的错误,这是由于高版本的 Springboot
与Swagger
版本使用的路径匹配策略冲突导致的。
Springfox
使用的路径匹配规则为AntPathMatcher
的,而SpringBoot2.7.6
使用的是PathPatternMatcher
,两者冲突了。
org.springframework.context.ApplicationContextException: Failed to start bean 'documentationPluginsBootstrapper'; nested exception is java.lang.NullPointerException at org.springframework.context.support.DefaultLifecycleProcessor.doStart(DefaultLifecycleProcessor.java:181) ~[spring-context-5.3.24.jar:5.3.24] at org.springframework.context.support.DefaultLifecycleProcessor.access$200(DefaultLifecycleProcessor.java:54) ~[spring-context-5.3.24.jar:5.3.24] at org.springframework.context.support.DefaultLifecycleProcessor$LifecycleGroup.start(DefaultLifecycleProcessor.java:356) ~[spring-context-5.3.24.jar:5.3.24] at java.lang.Iterable.forEach(Iterable.java:75) ~[na:1.8.0_341] at org.springframework.context.support.DefaultLifecycleProcessor.startBeans(DefaultLifecycleProcessor.java:155) ~[spring-context-5.3.24.jar:5.3.24] at org.springframework.context.support.DefaultLifecycleProcessor.onRefresh(DefaultLifecycleProcessor.java:123) ~[spring-context-5.3.24.jar:5.3.24] at org.springframework.context.support.AbstractApplicationContext.finishRefresh(AbstractApplicationContext.java:935) ~[spring-context-5.3.24.jar:5.3.24] at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:586) ~[spring-context-5.3.24.jar:5.3.24] at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:147) ~[spring-boot-2.7.6.jar:2.7.6] at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:731) [spring-boot-2.7.6.jar:2.7.6] at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:408) [spring-boot-2.7.6.jar:2.7.6] at org.springframework.boot.SpringApplication.run(SpringApplication.java:307) [spring-boot-2.7.6.jar:2.7.6] at org.springframework.boot.SpringApplication.run(SpringApplication.java:1303) [spring-boot-2.7.6.jar:2.7.6] at org.springframework.boot.SpringApplication.run(SpringApplication.java:1292) [spring-boot-2.7.6.jar:2.7.6] at com.springboot101.SwaggerApplication.main(SwaggerApplication.java:10) [classes/:na]
解决方案:
SpringBoot
版本降低到2.5.X
、springfox
降到3.X
以下可以解决问题SpringMVC
的匹配URL
路径的策略改为ant_path_matcher
,application.yml
文件增加如下的配置:spring:
mvc:
pathmatch:
matching-strategy: ant_path_matcher
BeanPostProcessor
Spring
容器中注册一个BeanPostProcessor
,在该处理器中对 HandlerMappings
进行定制。PatternParser
的映射,意味着我们可以将Swagger
特定的HandlerMappings
添加到HandlerMappings
列表中,从而使用自定义的设置来替代原有的HandlerMappings
。Swagger
无法正常使用的问题。@Bean public static BeanPostProcessor springfoxHandlerProviderBeanPostProcessor() { return new BeanPostProcessor() { @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { if (bean instanceof WebMvcRequestHandlerProvider || bean instanceof WebFluxRequestHandlerProvider) { customizeSpringfoxHandlerMappings(getHandlerMappings(bean)); } return bean; } private <T extends RequestMappingInfoHandlerMapping> void customizeSpringfoxHandlerMappings(List<T> mappings) { List<T> copy = mappings.stream() .filter(mapping -> mapping.getPatternParser() == null) .collect(Collectors.toList()); mappings.clear(); mappings.addAll(copy); } @SuppressWarnings("unchecked") private List<RequestMappingInfoHandlerMapping> getHandlerMappings(Object bean) { try { Field field = ReflectionUtils.findField(bean.getClass(), "handlerMappings"); field.setAccessible(true); return (List<RequestMappingInfoHandlerMapping>) field.get(bean); } catch (IllegalArgumentException | IllegalAccessException e) { log.warn("修改WebMvcRequestHandlerProvider的属性:handlerMappings出错,可能导致swagger不可用", e); throw new IllegalStateException(e); } } }; }
当我们访问的文档中展示的数据都是默认的配置,现在咱们来定制化一下文档。
Springfox
提供了一个 Docket
对象,供我们灵活的配置 Swagger
的各项属性。Docket
对象内提供了很多的方法来配置文档,下边介绍下常用的配置项。
select()
返回一个ApiSelectorBuilder
对象,是使用apis()
、paths()
两个方法的前提,用于指定Swagger
要扫描的接口和路径。
默认情况下,Swagger
会扫描整个项目中的接口,通过 apis()
方法,你可以传入一个RequestHandlerSelector
对象实例来指定要包含的接口所在的包路径。
@Bean
public Docket docket(Environment environment) {
return new Docket(DocumentationType.SWAGGER_2)
.select()
.apis(RequestHandlerSelectors.basePackage("com.test.controller"))
.build();
}
仅将某些特定请求路径的API
展示在Swagger
文档中,例如路径中包含/test
。可以使用 apis()
和 paths()
方法一起来过滤接口。
@Bean
public Docket docket(Environment environment) {
return new Docket(DocumentationType.SWAGGER_2)
.select()
.paths(PathSelectors.ant("/test/**"))
.build();
}
为生成的Swagger
文档指定分组的名称,用来区分不同的文档组。
@Bean
public Docket docket(Environment environment) {
return new Docket(DocumentationType.SWAGGER_2)
.groupName("用户分组")
.build();
}
实现文档的多个分组,则需创建多个 Docket
实例,设置不同的组名,和组内过滤 API
的条件。
@Bean
public Docket docket1(Environment environment) {
return new Docket(DocumentationType.SWAGGER_2)
.groupName("商家分组")
.select()
.paths(PathSelectors.ant("/test1/**"))
.build();
}
设置API文档的基本信息,例如标题、描述、版本等。可以使用ApiInfo对象自定义信息。
@Bean public Docket docket(Environment environment) { return new Docket(DocumentationType.SWAGGER_2) .apiInfo(apiInfo()); // 文档基础配置 } private ApiInfo apiInfo() { Contact contact = new Contact("测试", "http://127.0.0.1", "email@xx.com"); return new ApiInfoBuilder() .title("Swagger测试") .description("Swagger测试") .version("v1.0.1") .termsOfServiceUrl("http://127.0.0.1") .contact(contact) .license("许可证") .licenseUrl("许可链接") .extensions(Arrays.asList( new StringVendorExtension("我是", "测试"), new StringVendorExtension("你是", "谁") )) .build(); }
启用或禁用 Swagger
文档的生成,有时测试环境会开放API文档,但在生产环境则要禁用,可以根据环境变量控制是否显示。
@Bean
public Docket docket(Environment environment) {
// 可显示 swagger 文档的环境
Profiles of = Profiles.of("dev", "test","pre");
boolean enable = environment.acceptsProfiles(of);
return new Docket(DocumentationType.SWAGGER_2)
.enable(enable)
.apiInfo(apiInfo()); // 文档基础配置
}
API
文档显示的主机名称或IP地址,即在测试执行接口时使用的IP或域名。
@Bean
public Docket docket(Environment environment) {
return new Docket(DocumentationType.SWAGGER_2)
.host("http://test.com") // 请求地址
.apiInfo(apiInfo()); // 文档基础配置
}
配置API
安全认证方式,比如常见的在header
中设置如Bearer
、Authorization
、Basic
等鉴权字段,ApiKey
对象中字段含义分别是别名、鉴权字段key、鉴权字段添加的位置。
@Bean
public Docket docket(Environment environment) {
return new Docket(DocumentationType.SWAGGER_2)
.securitySchemes(
Arrays.asList(
new ApiKey("Bearer", "Bearer", "header"),
new ApiKey("Authorization", "Authorization", "header"),
new ApiKey("Basic", "Basic", "header")
)
);
}
这样配置后,Swagger
文档将会包含一个Authorize
按钮,供用户输入我们设定的Bearer
、Authorization
、Basic
进行身份验证。
securitySchemes
方法中虽然设置了鉴权字段,但此时在测试接口的时候不会自动在 header
中加上鉴权字段和值,还要配置API
的安全上下文,指定哪些接口需要进行安全认证。
@Bean public Docket docket(Environment environment) { return new Docket(DocumentationType.SWAGGER_2) .securitySchemes( Arrays.asList( new ApiKey("Bearer", "Bearer", "header"), new ApiKey("Authorization", "Authorization", "header"), new ApiKey("Basic", "Basic", "header") ) ) .securityContexts(Collections.singletonList(securityContext())); } private SecurityContext securityContext() { return SecurityContext.builder() .securityReferences( Arrays.asList( new SecurityReference("Authorization", new AuthorizationScope[0]), new SecurityReference("Bearer", new AuthorizationScope[0]), new SecurityReference("Basic", new AuthorizationScope[0]))) .build(); }
现在在测试调用API
接口时,header
中可以正常加上鉴权字段和值了。
注意
:SecurityReference
中构造方法的第一个参数要和Apikey
构造方法的第一个参数对应上去,否则会导致无法传递参数
为API
文档中的接口添加标签,标签可以用来对API
进行分类或分组,并提供更好的组织和导航功能。
@Bean
public Docket docket(Environment environment) {
return new Docket(DocumentationType.SWAGGER_2)
.tags(new Tag("测试接口", "测试接口"))
}
globalOperationParameters
是一个配置选项,用于定义全局的操作参数。它允许在所有 API
操作中共享相同的参数,而不需要在每个操作中重复定义。
globalOperationParameters
是一个数组,可以包含多个参数对象。
通过在 Swagger
配置文件中定义全局操作参数,可以确保这些参数在所有 API
操作中自动应用,并避免了重复定义相同的参数。这样可以提高代码维护性和可读性,并减少错误和冗余代码。
@Bean public Docket createRestApi() { return new Docket(DocumentationType.SWAGGER_2) .select() .apis(RequestHandlerSelectors.basePackage("com.test.controller")) .paths(PathSelectors.any()) .build() .globalOperationParameters(setHeaderToken()); } private List<Parameter> setHeaderToken() { ParameterBuilder tokenPar = new ParameterBuilder(); List<Parameter> pars = new ArrayList<>(); tokenPar.name("api_key") .description("令牌") .modelRef(new ModelRef("string")) .parameterType("header") .required(true).build(); pars.add(tokenPar.build()); return pars; }
出于对系统安全性的考虑,通常我们还会为API
文档增加登录功能。
点击此处了解spring security 权限控制
swagger
的安全登录是基于security
实现的,引入相关的maven
依赖。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
application.yml
文件中配置登录swagger的用户名和密码。
spring:
security:
user:
name: admin
password: 123456
再次访问文档就会出现如下的登录页
当我们希望在Swagger
文档中提供详细和完整的内容时,还可以使用其他许多Swagger
内置注解来进一步丰富和定制API文档。
原本可以根据指定路径或者包路径来提供API
,也可以使用粒度更细的@ApiIgnore
注解,来实现某个API
在文档中忽略
。
@ApiIgnore
@GetMapping("/user2/{id}")
public User test2(@PathVariable Integer id, @RequestBody User user) {
return user;
}
在接口中,只要使用实体作为参数或响应体,Swagger
就会自动扫描到它们,但发现目前这些实体缺乏详细的描述信息。为了让使用者通俗易懂,需要使用swagger
提供的注解为这些实体添加详细的描述。
@ApiModel
注解的使用在实体类
上,提供对Swagger Model
额外信息的描述。
@ApiModelProperty
注解为实体类中的属性添加描述,提供了字段名称、是否必填、字段示例等描述信息。
@ApiModel(value = "用户实体类", description = "用于存放用户登录信息")
@Data
public class User {
@ApiModelProperty(value = "用户名字段", required = true, example = "测试")
private String name;
@ApiModelProperty(value = "年龄", required = true, example = "19")
private Integer age;
@ApiModelProperty(value = "邮箱", required = true, example = "111@qq.com")
private String email;
}
@Api
注解用于标记一个控制器(controller
)类,并提供接口的详细信息和配置项:
value
:API
接口的描述信息,由于版本swagger
版本原因,value
可能会不生效可以使用description
hidden
:该 API
是否在 Swagger
文档中隐藏tags
:API
的标签,如果此处与 new Docket().tags
中设置的标签一致,则会将该 API
放入到这个标签组内authorizations
:鉴权配置,配合 @AuthorizationScope
注解控制权限范围或者特定密钥才能访问该API
produces
:API
的响应内容类型,例如 application/json。consumes
:API
的请求内容类型,例如 application/json。protocols
:API
支持的通信协议。@Api(value = "用户管理接口描述", description = "用户管理接口描述", hidden = false, produces = "application/json", consumes = "application/json", protocols = "https", tags = {"用户管理"}, authorizations = { @Authorization(value = "apiKey", scopes = { @AuthorizationScope(scope = "read:user", description = "读权限"), @AuthorizationScope(scope = "write:user", description = "写权限") }), @Authorization(value = "basicAuth") }) @RestController public class TestController { }
@ApiOperation
该注解作用在接口方法上,用来对一个操作或HTTP
方法进行描述:
value
:对接口方法的简单说明notes
:对操作的详细说明。httpMethod
:请求方式code
:状态码,默认为 200。可以传入符合标准的HTTP Status Code Definitions
。hidden
:在文档中隐藏该接口response
:返回的对象tags
:使用该注解后,该接口方法会单独进行分组produces
:API的响应内容类型,例如 application/json。consumes
:API的请求内容类型,例如 application/json。protocols
:API支持的通信协议。authorizations
:鉴权配置,配合 @AuthorizationScope
注解控制权限范围或者特定密钥才能访问该API。responseHeaders
:响应的header内容@ApiOperation( value = "获取用户信息", notes = "通过用户ID获取用户的详细信息", hidden = false, response = UserDto.class, tags = {"用户管理"}, produces = "application/json", consumes = "application/json", protocols = "https", authorizations = { @Authorization(value = "apiKey", scopes = {@AuthorizationScope(scope = "read:user", description = "读权限")}), @Authorization(value = "Basic") }, responseHeaders = {@ResponseHeader(name = "X-Custom-Header", description = "Custom header", response = String.class)}, code = 200, httpMethod = "GET" ) @GetMapping("/user1") public UserDto user1(@RequestBody User user) { return new UserDto(); }
@ApiImplicitParams
注解用在方法上,以数组方式存储,配合@ApiImplicitParam
注解使用。
@ApiImplicitParam
注解对API
方法中的单一参数进行注解。
注意这个注解@ApiImplicitParam
必须被包含在注解@ApiImplicitParams
之内:
name
:参数名称value
:参数的简短描述required
:是否为必传参数dataType
:参数类型,可以为类名,也可以为基本类型(String,int、boolean等)@ApiImplicitParams({
@ApiImplicitParam(name = "用户名", value = "用户名称信息", required = true, dataType = "String", paramType = "query")
})
@GetMapping("/user")
public String user(String name) {
return name;
}
@ApiParam()
也是对API
方法中的单一参数进行注解,其内部属性和@ApiImplicitParam
注解相似。
@GetMapping("/user4")
public String user4(@ApiParam(name = "主键ID", value = "@ApiParam注解测试", required = true) String id) {
return id;
}
@ApiResponses
注解可用于描述请求的状态码,作用在方法上,以数组方式存储,配合 @ApiResponse
注解使用。
@ApiResponse
注解描述一种请求的状态信息:
code
:HTTP
请求响应码。message
:响应的文本消息response
:返回类型信息。responseContainer
:如果返回类型为容器类型,可以设置相应的值。有效值为 List
、 Set
、Map
其他任何无效的值都会被忽略。@ApiResponses(value = {
@ApiResponse(code = 200, message = "@ApiResponse注解测试通过", response = String.class),
@ApiResponse(code = 401, message = "可能参数填的有问题", response = String.class),
@ApiResponse(code = 404, message = "可能请求路径写的有问题", response = String.class)
})
@GetMapping("/user4")
public String user4(@ApiParam(name = "主键ID", value = "@ApiParam注解测试", required = true) String id) {
return id;
}
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。