使用swagger3.0 的坑
开发环境介绍
首先版本
名称 | 版本 |
---|---|
spring colud alibaba | 2021.0.4.0 |
spring boot | 2.6.11 |
spring-boot-starter-actuactor | 2.6.11 |
swagger | 3.0.0 |
knife-spring-boot | 3.0.3 |
jdk | 11.17 |
1.版本冲突
1.1 只使用 springboot+cloud+swagger
1.2 冲突提示信息
使用这个配置会有冲突,导致异常提示:
Failed to start bean 'documentationPluginsBootstrapper'; nested exception is java.lang.NullPointerException
1.3 解决方案
解决方案很简单,配置文件中添加如下:
spring:
mvc:
pathmatch:
matching-strategy: ant_path_matcher
问题就解决了。
这个时候使用访问http://127.0.0.1:8086/swagger-ui/
2. 添加spring-boot-starter-actuator时异常
使用了上述配置 ,然后在使用 项目监控 actuator 这个会有冲突,spring-boot-starter-actuator 与 springfox-boot-starter 冲突导致异常重新出现
Failed to start bean 'documentationPluginsBootstrapper'; nested exception is java.lang.NullPointerException
如果要解决这个问题 ,需要如下2步骤
- application.yml 中添加上述配置
- 自定义 webmvcEndpointhandlerMapping
2.1 添加配置
spring:
mvc:
pathmatch:
matching-strategy: ant_path_matcher
2.2 自定义 webmvcEndpointhandlerMapping
import org.springframework.boot.actuate.autoconfigure.endpoint.web.CorsEndpointProperties;
import org.springframework.boot.actuate.autoconfigure.endpoint.web.WebEndpointProperties;
import org.springframework.boot.actuate.autoconfigure.web.server.ManagementPortType;
import org.springframework.boot.actuate.endpoint.ExposableEndpoint;
import org.springframework.boot.actuate.endpoint.web.EndpointLinksResolver;
import org.springframework.boot.actuate.endpoint.web.EndpointMapping;
import org.springframework.boot.actuate.endpoint.web.EndpointMediaTypes;
import org.springframework.boot.actuate.endpoint.web.ExposableWebEndpoint;
import org.springframework.boot.actuate.endpoint.web.WebEndpointsSupplier;
import org.springframework.boot.actuate.endpoint.web.annotation.ControllerEndpointsSupplier;
import org.springframework.boot.actuate.endpoint.web.annotation.ServletEndpointsSupplier;
import org.springframework.boot.actuate.endpoint.web.servlet.WebMvcEndpointHandlerMapping;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import org.springframework.util.StringUtils;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
@Configuration
public class SwaggerConfig {
/**
* 解决springboot升到2.6.x之后 swagger与spring-boot-starter-actuator 的冲突
*/
@Bean
public WebMvcEndpointHandlerMapping webMvcEndpointHandlerMapping(WebEndpointsSupplier wes
, ServletEndpointsSupplier ses, ControllerEndpointsSupplier ces, EndpointMediaTypes emt
, CorsEndpointProperties cep, WebEndpointProperties wep, Environment env) {
List<ExposableEndpoint<?>> allEndpoints = new ArrayList<>();
Collection<ExposableWebEndpoint> webEndpoints = wes.getEndpoints();
allEndpoints.addAll(webEndpoints);
allEndpoints.addAll(ses.getEndpoints());
allEndpoints.addAll(ces.getEndpoints());
String basePath = wep.getBasePath();
EndpointMapping endpointMapping = new EndpointMapping(basePath);
boolean shouldRegisterLinksMapping = shouldRegisterLinksMapping(wep, env, basePath);
return new WebMvcEndpointHandlerMapping(endpointMapping, webEndpoints, emt
, cep.toCorsConfiguration(), new EndpointLinksResolver(
allEndpoints, basePath), shouldRegisterLinksMapping, null);
}
/**
* shouldRegisterLinksMapping
*
* @param wep
* @param env
* @param basePath
* @return
*/
private boolean shouldRegisterLinksMapping(WebEndpointProperties wep, Environment env, String basePath) {
return wep.getDiscovery().isEnabled() && (StringUtils.hasText(basePath) || ManagementPortType.get(env).equals(ManagementPortType.DIFFERENT));
}
}
- 完成上述步骤 即可,使用访问http://127.0.0.1:8086/swagger-ui/
3.使用knife4j 来优化界面显示
这里说一下这个knife4j,他是开源的一个增强工具,以前叫做swagger-bootstrap-ui,现在改为Knife4j。
版本最新为4.0 与3.0区别是,
3.0支持swagger3.0
4.0 支持swagger2.0和3.0 供用户自己选择。
详情看 [官方地址](https://doc.xiaominfo.com/docs/community/changelog)
添加依赖 knife4j-spring-boot-starter
swagger-ui 这个界面不友好,接口多了看起来比较麻烦,这个时候需要使用Knife4j。使用此插件步骤
- 引入依赖
- 自定义bean来处理冲突
- 使用新的接口来访问,禁用swagger-ui 访问 即可
3.1引入依赖
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>knife4j-spring-boot-starter</artifactId>
<version>3.0.3</version>
</dependency>
3.2 自定义bean
这里将2中的 解决冲突的bean 合并一起了
import com.github.xiaoymin.knife4j.spring.annotations.EnableKnife4j;
import org.springframework.boot.actuate.autoconfigure.endpoint.web.CorsEndpointProperties;
import org.springframework.boot.actuate.autoconfigure.endpoint.web.WebEndpointProperties;
import org.springframework.boot.actuate.autoconfigure.web.server.ManagementPortType;
import org.springframework.boot.actuate.endpoint.ExposableEndpoint;
import org.springframework.boot.actuate.endpoint.web.EndpointLinksResolver;
import org.springframework.boot.actuate.endpoint.web.EndpointMapping;
import org.springframework.boot.actuate.endpoint.web.EndpointMediaTypes;
import org.springframework.boot.actuate.endpoint.web.ExposableWebEndpoint;
import org.springframework.boot.actuate.endpoint.web.WebEndpointsSupplier;
import org.springframework.boot.actuate.endpoint.web.annotation.ControllerEndpointsSupplier;
import org.springframework.boot.actuate.endpoint.web.annotation.ServletEndpointsSupplier;
import org.springframework.boot.actuate.endpoint.web.servlet.WebMvcEndpointHandlerMapping;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import org.springframework.util.StringUtils;
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 java.util.ArrayList;
import java.util.Collection;
import java.util.List;
/**
* @ClassName t1
* @date 2023/3/13 0:03
* @Description TODO
* @Author [Miller-hw]
* @Version 1.0
*/
@Configuration
@EnableKnife4j
public class Knife4jConfig {
@Bean
public Docket createRestApi(Environment env) {
return new Docket(DocumentationType.OAS_30)
.useDefaultResponseMessages(false)
.groupName("1.0版本")
.apiInfo(apiInfo())
//只有当springboot配置文件为dev或test环境时,才开启swaggerAPI文档功能
// .enable(flag)
.enable(true)
.select()
// 这里指定Controller扫描包路径:设置要扫描的接口类,一般是Controller类
.apis(RequestHandlerSelectors.any()) //这里采用包扫描的方式来确定要显示的接口
// .apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class)) //这里采用包含注解的方式来确定要显示的接口
// 配置过滤哪些,设置对应的路径才获取
// .paths(PathSelectors.regex("/eid/.*?"))
.paths(PathSelectors.any())
.build();
//防止Controller中参数中没有实体类或者返回值不是实体类导致Swagger Models页面扫描不到的情况
// .additionalModels(typeResolver.resolve(EmailNotice.class))
// .additionalModels(typeResolver.resolve(SmsNotice.class))
// .additionalModels(typeResolver.resolve(WeChatNotice.class));
}
///配置相关的api信息
private ApiInfo apiInfo()
{
return new ApiInfoBuilder()
.description("API调试文档")
//作者信息
.contact(new Contact("努力财富自由", "http://127.0.0.1:8081/doc.html", "xk8703@163.com"))
.version("v1.0")
.title("Eid服务端API文档")
//服务Url
.termsOfServiceUrl("")
.build();
}
/**
* 解决springboot升到2.6.x之后,knife4j报错
* @param wes the web endpoints supplier
* @param ses the servlet endpoints supplier
* @param ces the controller endpoints supplier
* @param emt the endpoint media types
* @param cep the cors properties
* @param wep the web endpoints properties
* @param env the environment
* @return the web mvc endpoint handler mapping
*/
@Bean
public WebMvcEndpointHandlerMapping webMvcEndpointHandlerMapping(WebEndpointsSupplier wes
, ServletEndpointsSupplier ses, ControllerEndpointsSupplier ces, EndpointMediaTypes emt
, CorsEndpointProperties cep, WebEndpointProperties wep, Environment env) {
List<ExposableEndpoint<?>> allEndpoints = new ArrayList<>();
Collection<ExposableWebEndpoint> webEndpoints = wes.getEndpoints();
allEndpoints.addAll(webEndpoints);
allEndpoints.addAll(ses.getEndpoints());
allEndpoints.addAll(ces.getEndpoints());
String basePath = wep.getBasePath();
EndpointMapping endpointMapping = new EndpointMapping(basePath);
boolean shouldRegisterLinksMapping = shouldRegisterLinksMapping(wep, env, basePath);
return new WebMvcEndpointHandlerMapping(endpointMapping, webEndpoints, emt
, cep.toCorsConfiguration(), new EndpointLinksResolver(
allEndpoints, basePath), shouldRegisterLinksMapping, null);
}
/**
* shouldRegisterLinksMapping
*
* @param wep
* @param env
* @param basePath
* @return
*/
private boolean shouldRegisterLinksMapping(WebEndpointProperties wep, Environment env, String basePath) {
return wep.getDiscovery().isEnabled() && (StringUtils.hasText(basePath) || ManagementPortType.get(env).equals(ManagementPortType.DIFFERENT));
}
}
3.4 完成配置 关闭swagger-ui,使用knife4j
# 关闭 swagger-ui
springfox:
documentation:
swagger-ui:
enabled: false
3.5 展示界面
使用地址 http://localhost:8086/doc.html
4. 使用示例
4.1 添加DTO文档
@Getter
@Setter
@ApiModel(value = "hello",description = "value 的值为model中的标题")
public class Hello implements Serializable {
private static final long serialVersionUID = -6985352140066168201L;
@ApiModelProperty("名称")
private String name;
@ApiModelProperty(value = "年龄")
private int age;
@ApiModelProperty(value = "生日")
private String birth;
public Hello() {
}
public Hello(String name, int age) {
this.name = name;
this.age = age;
}
}
4.2 添加API接口文档
@RestController
@Api(tags = {"hello"}) // 这里标记可以将同一类的接口放一起, 便于查找和管理
public class HelloController {
@ApiOperation(value = "hello 接口",nickname = "hello-index",notes = "api接口备注") // nickname 基本没啥用,作用是文档中地址栏会多这么个路径
// 请求参数的第一种写法
@ApiImplicitParams({
@ApiImplicitParam(name = "name",value = "名称而已",required = true,defaultValue = "hello",dataTypeClass = String.class,paramType = "path"),
@ApiImplicitParam(name = "type",value = "类型",required = false,defaultValue = "null",dataTypeClass = Map.class,paramType = "query"),
@ApiImplicitParam(name = "h",value = "hello",required = false,defaultValue = "null",dataTypeClass = Hello.class,paramType = "query"),
})
@GetMapping("/hello")
public Object index(String name, String type, Hello h) {
HashMap<String, Object> map = new HashMap<>();
map.put("code", 200);
map.put("message", " hello world ");
return map;
}
@GetMapping("/hello/t1")
@ApiOperation(value = "显示model")
// 请求参数的第二种写法
public Hello hello(@ApiParam(name = "name", value = "名字",required = true,type = "String") String name,
@ApiParam(name = "age", value = "年龄",required = false,type = "int") int age) {
return new Hello(name, age);
}
}
4.3 关于@ApiModel 避坑的一点
比如你新增一个DTO,使用@ApiModel 做了标记,在Swagger Models 不会显示的。必须在controller 使用了此DTO且用到了@ApiOperation 标注才行如这种:
@GetMapping("/hello/t1")
@ApiOperation(value = "显示model")
public Hello hello() {
return new Hello();
}
搞了好久 这个有点坑