Swagger 是一个规范和完整的框架,用于生成、描述、调用和可视化 RESTful 风格的 Web 服务。
Swagger Swagger
1.Swagger 是一款RESTFUL接口的文档在线自动生成与功能测试功能软件。总体目标是使客户端和文件系统作为服务器以同样的速度来更新。 文件的方法,参数和模型紧密集成到服务器端的代码,允许API来始终保持同步。 Swagger 让部署管理和使用功能强大的API从未如此简单。
2.Swagger将项目中接口展现在页面上,并进行接口调用和测试,Swagger遵循OpenAPI(The OpenAPI Specification , previously known as the Swagger Specification , is a specification for machine-readable interface files for describing, producing, consuming, and visualizing RESTful web services )规范。
项目中接口展现在页面上
接口更新只需要修改代码中的 Swagger 描述就可实时生成新接口文档
通过Swagger页面可直接进行接口调用和测试
3.Swagger 是一套基于 OpenAPI 规范(OpenAPI Specification,OAS)构建的开源工具,后来成为了 Open API 标准的主要定义者,现在最新的版本为17年发布的 Swagger3(Open Api3)。 国内绝大部分人还在用过时的swagger2(17年停止维护并更名为swagger3)。
4.Swagger 提供了一套通过代码和注解自动生成文档的方法。 SpringFox是 spring 社区维护的一个项目(非官方),帮助使用者将 swagger2 集成到 Spring 中。
SpringBoot集成 Springfox Reference Documentation
io.swagger.annotations
1.导入依赖
SpringFox 的前身是 swagger-springmvc,是一个开源的 API doc 框架,可以将 Controller 的方法以文档的形式展现。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 <dependency > <groupId > io.springfox</groupId > <artifactId > springfox-swagger2</artifactId > <version > 3.0.0</version > </dependency > <dependency > <groupId > io.springfox</groupId > <artifactId > springfox-swagger-ui</artifactId > <version > 3.0.0</version > </dependency >
1 2 3 4 5 6 <dependency > <groupId > io.springfox</groupId > <artifactId > springfox-boot-starter</artifactId > <version > 3.0.0</version > </dependency >
1.NullPointerException(springboot2.6.0中将SpringMVC默认路径匹配策略从AntPathMatcher更改为PathPatternParser)
1 2 3 4 5 6 7 8 9 10 11 12 <parent > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-parent</artifactId > <version > 2.5.6</version > <relativePath /> </parent > <plugin > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-maven-plugin</artifactId > <version > ${project.parent.version}</version > </plugin >
Project'org.springframework.boot:spring-boot-starter-parent:' not found: File -> Invalidate Caches / Restart…
1 spring.mvc.pathmatch.matching-strategy =ANT_PATH_MATCHER
2.swagger-ui.html 无法打开(在swagger3.x(/resources/webjars/springfox-swagger-ui/index.html
)与swagger2.x(/resources/swagger-ui.html
)命名及位置变化)
1 2 3 4 5 6 7 8 9 10 11 <dependency > <groupId > io.springfox</groupId > <artifactId > springfox-swagger2</artifactId > <version > 2.9.2</version > </dependency > <dependency > <groupId > io.springfox</groupId > <artifactId > springfox-swagger-ui</artifactId > <version > 2.9.2</version > </dependency >
配置类(需要去掉主启动类的@EnableWebMvc注解)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 @Configuration public class WebMVCConfig implements WebMvcConfigurer { @Override public void addResourceHandlers (ResourceHandlerRegistry registry) { registry.addResourceHandler("/**" ).addResourceLocations("classpath:/static/" ); registry.addResourceHandler("swagger-ui.html" ) .addResourceLocations("classpath:/META-INF/resources/" ); registry.addResourceHandler("/webjars/**" ) .addResourceLocations("classpath:/META-INF/resources/webjars/" ); } }
1.主启动类添加@EnableOpenApi
注解
2.访问http://localhost:8080/swagger-ui/index.html
1 2 3 4 5 6 <dependency > <groupId > io.springfox</groupId > <artifactId > springfox-boot-starter</artifactId > <version > 3.0.0</version > </dependency >
2.测试
1 2 3 4 @Configuration @EnableSwagger2 public class SwaggerConfig {}
1 2 3 4 5 6 7 @RestController public class HelloController { @RequestMapping(value = "/hello",method = RequestMethod.GET) public String hello () { return "Hello Swagger" ; } }
配置Swagger 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 public class Docket implements DocumentationPlugin { public static final String DEFAULT_GROUP_NAME = "default" ; private final DocumentationType documentationType; private final List<SecurityContext> securityContexts = new ArrayList(); private final Map<RequestMethod, List<ResponseMessage>> responseMessages = new HashMap(); private final Map<HttpMethod, List<Response>> responses = new HashMap(); private final List<Parameter> globalOperationParameters = new ArrayList(); private final List<Function<TypeResolver, AlternateTypeRule>> ruleBuilders = new ArrayList(); private final Set<Class> ignorableParameterTypes = new HashSet(); private final Set<String> protocols = new HashSet(); private final Set<String> produces = new LinkedHashSet(); private final Set<String> consumes = new LinkedHashSet(); private final Set<ResolvedType> additionalModels = new HashSet(); private final Set<Tag> tags = new HashSet(); private final List<Server> servers = new ArrayList(); private PathProvider pathProvider; private List<SecurityScheme> securitySchemes; private Comparator<ApiListingReference> apiListingReferenceOrdering; private Comparator<ApiDescription> apiDescriptionOrdering; private Comparator<Operation> operationOrdering; private ApiInfo apiInfo; private String groupName; private boolean enabled; private GenericTypeNamingStrategy genericsNamingStrategy; private boolean applyDefaultResponseMessages; private String host; private Optional<String> pathMapping; private ApiSelector apiSelector; private boolean enableUrlTemplating; private final List<VendorExtension> vendorExtensions; private final List<RequestParameter> globalRequestParameters; public Docket (DocumentationType documentationType) { this .apiInfo = ApiInfo.DEFAULT; this .groupName = "default" ; this .enabled = true ; this .genericsNamingStrategy = new DefaultGenericTypeNamingStrategy(); this .applyDefaultResponseMessages = true ; this .host = "" ; this .pathMapping = Optional.empty(); this .apiSelector = ApiSelector.DEFAULT; this .enableUrlTemplating = false ; this .vendorExtensions = new ArrayList(); this .globalRequestParameters = new ArrayList(); this .documentationType = documentationType; } ...... }
1 2 3 4 5 6 7 8 9 10 public class DocumentationType extends SimplePluginMetadata { public static final DocumentationType SWAGGER_12 = new DocumentationType("swagger" , "1.2" ); public static final DocumentationType SWAGGER_2 = new DocumentationType("swagger" , "2.0" ); public static final DocumentationType OAS_30 = new DocumentationType("openApi" , "3.0" ); @Deprecated public static final DocumentationType SPRING_WEB = new DocumentationType("spring-web" , "5.2" ); private final MediaType mediaType; ....... }
1 2 3 4 5 6 7 8 9 10 11 12 13 public class ApiInfo { public static final Contact DEFAULT_CONTACT = new Contact("" , "" , "" ); public static final ApiInfo DEFAULT; private final String version; private final String title; private final String description; private final String termsOfServiceUrl; private final String license; private final String licenseUrl; private final Contact contact; private final List<VendorExtension> vendorExtensions; ..... }
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 @Configuration @EnableSwagger2 public class SwaggerConfig { @Bean public Docket docketOther (Environment environment) { return new Docket(DocumentationType.SWAGGER_2).groupName("Stone" ); } @Bean public Docket docket (Environment environment) { Profiles profiles = Profiles.of("dev" ,"test" ); boolean flag = environment.acceptsProfiles(profiles); return new Docket(DocumentationType.SWAGGER_2) .apiInfo(apiInfo()) .enable(flag) .groupName("Zero" ) .select() .apis(RequestHandlerSelectors.basePackage("com.example.swaggerdemo.controller" )) .build(); } private ApiInfo apiInfo () { Contact contact = new Contact("Zero" ,"http://zerostone.cn" ,"example@qq.com" ); return new ApiInfo( "Zero" , "API文档" , "1.0.0" , "http://zerostone.cn" , contact, "Apache 2.0" , "http://www.licenseUrl.com" , new ArrayList() ); } }
1 2 spring.mvc.pathmatch.matching-strategy =ANT_PATH_MATCHER spring.profiles.active =dev
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 66 67 68 @Component @ConfigurationProperties("swagger") public class SwaggerProperties { private Boolean enable; private String applicationName; private String applicationVersion; private String applicationDescription; private String tryHost; public Boolean getEnable () { return enable; } public void setEnable (Boolean enable) { this .enable = enable; } public String getApplicationName () { return applicationName; } public void setApplicationName (String applicationName) { this .applicationName = applicationName; } public String getApplicationVersion () { return applicationVersion; } public void setApplicationVersion (String applicationVersion) { this .applicationVersion = applicationVersion; } public String getApplicationDescription () { return applicationDescription; } public void setApplicationDescription (String applicationDescription) { this .applicationDescription = applicationDescription; } public String getTryHost () { return tryHost; } public void setTryHost (String tryHost) { this .tryHost = tryHost; } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 spring: mvc: pathmatch: matching-strategy: ANT_PATH_MATCHER application: name: Swagger Demo swagger: enable: true application-name: ${spring.application.name} application-version: 5.0 application-description: Springfox Swagger try-host: http://localhost:${server.port} server: port: 8080
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 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 @EnableOpenApi @Configuration public class SwaggerConfiguration implements WebMvcConfigurer { private final SwaggerProperties swaggerProperties; public SwaggerConfiguration (SwaggerProperties swaggerProperties) { this .swaggerProperties = swaggerProperties; } @Bean public Docket createRestApi () { Docket docket = new Docket(DocumentationType.OAS_30).pathMapping("/" ) .enable(swaggerProperties.getEnable()) .apiInfo(apiInfo()) .host(swaggerProperties.getTryHost()) .select() .apis(RequestHandlerSelectors.any()) .paths(PathSelectors.any()) .build() .protocols(newHashSet("https" , "http" )) .securitySchemes(securitySchemes()) .securityContexts(securityContexts()); return docket; } private ApiInfo apiInfo () { return new ApiInfoBuilder().title(swaggerProperties.getApplicationName() + " Api Doc" ) .description(swaggerProperties.getApplicationDescription()) .contact(new Contact("lighter" , null , "123456@gmail.com" )) .version("Application Version: " + swaggerProperties.getApplicationVersion() + ", Spring Boot Version: " + SpringBootVersion.getVersion()) .build(); } private List<SecurityScheme> securitySchemes () { ApiKey apiKey = new ApiKey("BASE_TOKEN" , "token" , In.HEADER.toValue()); return Collections.singletonList(apiKey); } private List<SecurityContext> securityContexts () { return Collections.singletonList( SecurityContext.builder() .securityReferences(Collections.singletonList(new SecurityReference("BASE_TOKEN" , new AuthorizationScope[]{new AuthorizationScope("global" , "" )}))) .build() ); } @SafeVarargs private final <T> Set<T> newHashSet (T... ts) { if (ts.length > 0 ) { return new LinkedHashSet<>(Arrays.asList(ts)); } return null ; } @SuppressWarnings("unchecked") @Override public void addInterceptors (InterceptorRegistry registry) { try { Field registrationsField = FieldUtils.getField(InterceptorRegistry.class, "registrations" , true ); List<InterceptorRegistration> registrations = (List<InterceptorRegistration>) ReflectionUtils.getField(registrationsField, registry); if (registrations != null ) { for (InterceptorRegistration interceptorRegistration : registrations) { interceptorRegistration .excludePathPatterns("/swagger**/**" ) .excludePathPatterns("/webjars/**" ) .excludePathPatterns("/v3/**" ) .excludePathPatterns("/doc.html" ); } } } catch (Exception e) { e.printStackTrace(); } } }
1 2 3 4 5 6 <dependency > <groupId > org.apache.commons</groupId > <artifactId > commons-lang3</artifactId > <version > 3.12.0</version > </dependency >
Swagger注解
注意:swagger注解对于代码的侵入性较强
1.Api
用在Controller类上,说明该类的作用
属性名称
含义
value
Implicitly sets a tag for the operations
tags
类作用,非空时会覆盖value的值
description
api描述,在 1.5 版本后不再支持
basePath
基本路径,在 1.5 版本后不再支持
position
如果配置多个 Api 想改变显示的顺序位置,在 1.5 版本后不再支持
produces
设置 MIME 类型列表(output),如application/json, application/xml
consumes
设置 MIME 类型列表(input),如application/json, application/xml
protocols
设置特定协议,如http, https, ws, wss
authorizations
获取授权列表(安全声明),未设置则返回空的授权值
hidden
默认为 false,配置为 true 将在文档中隐藏
2.ApiOperation
用在方法上,说明方法的作用
属性名称
含义
value
Corresponds to the summary
field of the operation
notes
方法备注说明
tags
操作标签,非空时将覆盖value的值
response
响应类型(返回对象)
responseContainer
声明包装的响应容器(返回对象类型)。有效值为List Set Map
responseReference
指定对响应类型的引用
httpMethod
指定HTTP方法
position
改变显示的顺序位置, 1.5 版本后不再支持
nickname
第三方工具唯一标识,默认为空
responseHeaders
响应头列表
code
响应的HTTP状态代码
extensions
扩展属性列表数组
produces
设置 MIME 类型列表(output),如application/json, application/xml,默认为空
consumes
设置 MIME 类型列表(input),如application/json, application/xml,默认为空
protocols
设置特定协议,如http, https, ws, wss
authorizations
获取授权列表(安全声明)
hidden
默认为 false,配置为 true 将在文档中隐藏
3.ApiParam
一般用在请求体参数上,描述请求体信息
属性名称
含义
name
参数名称,参数名称可覆盖方法参数名称,路径参数必须与方法参数一致
value
参数说明
required
参数是否必须传,默认为 false (路径参数必填)
defaultValue
参数默认值
allowableValues
限制参数的可接受值
access
允许从API文档中过滤参数
allowMultiple
指定参数是否可通过具有多个事件接受多个值,默认为 false
example
单个示例
examples
参数示例,仅适用于 BodyParameters
hidden
默认为 false,配置为 true 将在文档中隐藏
4.@ApiImplicitParams/@ApiImplicitParam
@ApiImplicitParams
用在请求方法上,表示一组参数说明,里面是@ApiImplicitParam
列表,ApiImplicitParam
用于请求参数的说明
属性名称
含义
name
参数名称,参数名称可以覆盖方法参数名称,路径参数必须与方法参数一致
value
参数说明
required
参数是否必须传,默认为 false (路径参数必填)
paramType
参数的位置header:@RequestHeader
query:@RequestParam
path(restful 接口):@PathVariable
body:@RequestBody
form:@ModelAttribute
dataType
参数类型,默认 String
defaultValue
参数的默认值
allowableValues
限制参数的可接受值。1.以逗号分隔的列表 2.范围值 3.设置最小值/最大值
access
允许从API文档中过滤参数
allowMultiple
指定参数是否可以通过具有多个事件接受多个值,默认为 false
example
单个示例
examples
参数示例,仅适用于 BodyParameters
5.@ApiResponses/@ApiResponse
@ApiResponses
用在请求的方法上,表示一组响应(@ApiResponse
),@ApiResponse
表达一个错误的响应信息
属性名称
含义
code
响应状态码
message
信息
response
抛出异常的类
6.@ApiModel
用在实体类上,表示相关实体的描述。
注解属性
描述
value
实体类名称
description
类说明
7.@ApiModelProperty
用在实体类属性上,表示属性的相关描述。
属性名称
描述
value
属性简要说明
name
重写属性名称
dataType
重写属性类型
required
参数是否必传,默认为 false
example
属性示例
hidden
是否在文档中隐藏该属性,默认false
allowEmptyValue
是否允许为空,默认false
allowableValues
限制参数的可接受值。1.以逗号分隔的列表 2.范围值 3.设置最小值/最大值
readOnly
将属性设定为只读,默认false
reference
指定对相应类型定义的引用,覆盖指定的任何参数值
8.@ApiIgnore
用于类,忽略该 Controller类,即不对当前类做扫描
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 @ApiModel(value = "User", description = "微信端用户") public class User { @ApiModelProperty(value = "用户id") private long id; @ApiModelProperty(value = "用户名") private String name; @ApiModelProperty(value = "用户号码") private String phone; public User (long id, String name, String phone) { this .id = id; this .name = name; this .phone = phone; } public long getId () { return id; } public void setId (long id) { this .id = id; } public String getName () { return name; } public void setName (String name) { this .name = name; } public String getPhone () { return phone; } public void setPhone (String phone) { this .phone = phone; } @Override public String toString () { return "User{" + "id=" + id + ", name='" + name + '\'' + ", phone='" + phone + '\'' + '}' ; } }
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 @RestController @Api(tags = "微信端controller") @RequestMapping("/wechat") public class WeChatController { @GetMapping(value = "/index") @ApiOperation(value = "获取用户列表") public String getUser () { return JSON.toJSONString(new ArrayList<User>().add(new User(1l ,"Zero" ,"123445" ))); } @PostMapping(value = "/add") @ApiOperation(value = "新增用户") public Boolean addUser (@RequestBody @ApiParam(name="User",value = "新增用户参数") User user) { System.out.println(user.toString()); return true ; } @PostMapping(value = "/page") @ApiOperation(value = "分页查询用户") @ApiImplicitParams({ @ApiImplicitParam(name = "pageNum", value = "当前页"), @ApiImplicitParam(name = "pageSize", value = "页记录数") }) @ApiResponses({ @ApiResponse(code = 400, message = "参数有误"), @ApiResponse(code = 404, message = "请求路径错误") }) public List<User> getPageUser (@RequestParam(defaultValue = "1", required = false) Integer pageNum, @RequestParam(defaultValue = "10", required = false) Integer pageSize) { return new ArrayList<User>(); } }