springboot整合swagger2:
1、依赖包:
代码语言:javascript复制<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.7.0</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.7.0</version>
</dependency>
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>swagger-bootstrap-ui</artifactId>
<version>1.8.3</version>
</dependency>
2、SwaggerConfig配置:
代码语言:javascript复制import io.swagger.annotations.Api;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.*;
import org.springframework.web.util.UriComponentsBuilder;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.ParameterBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.schema.ModelRef;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Parameter;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.paths.AbstractPathProvider;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
import java.util.ArrayList;
import java.util.List;
import static com.google.common.base.Strings.isNullOrEmpty;
import static springfox.documentation.spring.web.paths.Paths.removeAdjacentForwardSlashes;
@Configuration
@EnableSwagger2
@Slf4j
@EnableConfigurationProperties(SwaggerYaml.class)
public class SwaggerConfig implements WebMvcConfigurer {
private SwaggerYaml swaggerYaml;
public SwaggerConfig(SwaggerYaml swaggerYaml) {
this.swaggerYaml = swaggerYaml;
}
@Bean
@ConditionalOnProperty(prefix = "swagger", name = "enable", havingValue = "true")
public Docket api() {
log.info("swagger配置信息初始化");
return new Docket(DocumentationType.SWAGGER_2)
.forCodeGeneration(true)
.pathProvider(new CustRelativePathProvider())
.select()
// .apis(RequestHandlerSelectors.any()) //显示所有类
//只显示添加@Api注解的类
.apis(RequestHandlerSelectors.withClassAnnotation(Api.class))
.paths(PathSelectors.any())
.build()
.apiInfo(apiInfo()).globalOperationParameters(parameters());
}
private ApiInfo apiInfo() {
return new ApiInfoBuilder()
.title(swaggerYaml.getTitle())
.description(swaggerYaml.getDescription())
.version(swaggerYaml.getVersion())
.termsOfServiceUrl(swaggerYaml.getTermsOfServiceUrl())
.build();
}
/**
* 全局参数。 接口请求header中的token。 required false非必须
* @return
*/
private List<Parameter> parameters(){
List<Parameter> params = new ArrayList<>();
params.add(
new ParameterBuilder()
.name("token")
.description("请求令牌")
.modelRef(new ModelRef("String"))
.parameterType("header")
.required(false)
.build());
return params;
}
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/**").addResourceLocations("classpath:/static/");
registry.addResourceHandler("/swagger-ui.html").addResourceLocations("classpath:/META-INF/resources/");
registry.addResourceHandler("/doc.html").addResourceLocations("classpath:/META-INF/resources/");
registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");
super.addResourceHandlers(registry);
}
@Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}
/**
* 重新pathprovider,给所有url添加后缀
*/
public class CustRelativePathProvider extends AbstractPathProvider {
public static final String ROOT = "/";
@Override
public String getOperationPath(String operationPath) {
UriComponentsBuilder uriComponentsBuilder = UriComponentsBuilder.fromPath("/");
String uri = removeAdjacentForwardSlashes(uriComponentsBuilder.path(operationPath).build().toString());
return uri ".jhtml";
}
@Override
protected String applicationPath() {
return isNullOrEmpty(swaggerYaml.getContextPath()) ? ROOT : swaggerYaml.getContextPath();
}
@Override
protected String getDocumentationPath() {
return ROOT;
}
}
}
3、SpringApplication.run(xxx.class, args)。
由于我使用了swagger第三方ui:swagger-bootstrap-ui,所以我直接访问url:http://localhost:8080/doc.html。结果:
开始百度搜索解决大法:
基本网上说的解决方法不都是静态资源映射的问题,解决方式为:
代码语言:javascript复制@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/**").addResourceLocations("classpath:/static/");
registry.addResourceHandler("/swagger-ui.html").addResourceLocations("classpath:/META-INF/resources/");
registry.addResourceHandler("/doc.html").addResourceLocations("classpath:/META-INF/resources/");
registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");
super.addResourceHandlers(registry);
}
@Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}
但是我无效,而且我在springmvc上使用过swagger,自信配置应该是不缺。哈哈哈。
还有一个解决方式为:在application.yml上添加static资源映射,spring.resource.static-location:classpath:/resource/...等等类似,也无效。
最终:无意间在idea全局搜索WebMvcConfigurer发现自己曾经写的一个跨域的拦截器配置CrossOriginConfig同样实现了WebMvcConfigurer。于是抱着死马当活马的态度,将这个注释后,重启,发现熟悉的页面出来了:
于是百度了一圈,网上给出的解释大概为:springboot默认静态资源路径为
- classpath:/META-INF/resources
- classpath:/resources
- classpath:/static
- classpath:/public
这也就是为什么我们前面要添加资源文件映射到swagger-ui.html的原因吧, 个人理解。
然后,我们自己在容器中装配了自己的bean,springboot就不会帮我们自动装配(大概意思就是,CrossOriginConfig生成的baen把springboot的bean给覆盖了,菜鸟的白话理解,如有误,请指出)
这里更改后的两个配置,这里我是将CrossOriginConfig作为基类,供其他地方继承使用(当然,你可以直接在swaggerConfig中重写addCorsMappings也是一样的道理。):
代码语言:javascript复制import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/**
* 处理跨域请求
*
* 这里不自动装配,作为基类提供子类继承。避免重复装配,导致bean被覆盖。(如被覆盖后,swagger-ui出现404)
*
* @author asus
* @date 2020/4/8
*/
//@Configuration
public class CrossOriginConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**").allowedOrigins("*")
.allowedMethods("POST", "GET", "PUT", "OPTIONS", "DELETE")
.maxAge(3600)
.allowCredentials(true);
}
}
代码语言:javascript复制import com.kuria.config.cross.CrossOriginConfig;
import io.swagger.annotations.Api;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.*;
import org.springframework.web.util.UriComponentsBuilder;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.ParameterBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.schema.ModelRef;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Parameter;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.paths.AbstractPathProvider;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
import java.util.ArrayList;
import java.util.List;
import static com.google.common.base.Strings.isNullOrEmpty;
import static springfox.documentation.spring.web.paths.Paths.removeAdjacentForwardSlashes;
/**
* swagger2 配置
* @ConditionalOnProperty 用于控制当前config是否生效
* swagger-open: true生效,false则关闭
*
* 继承CrossOriginConfig, 原因CrossOriginConfig配置跨域拦截处理,为了避免bean被覆盖,导致swagger-ui 404.这里不重复装配
* 由CrossOriginConfig 作为基类,提供继承。当然,如果SwaggerConfig重写addCorsMappings也是可以的。
*
* @author pengyh
* @date 2020/4/10
*/
@Configuration
@EnableSwagger2
@Slf4j
@EnableConfigurationProperties(SwaggerYaml.class)
public class SwaggerConfig extends CrossOriginConfig {
private SwaggerYaml swaggerYaml;
public SwaggerConfig(SwaggerYaml swaggerYaml) {
this.swaggerYaml = swaggerYaml;
}
@Bean
@ConditionalOnProperty(prefix = "swagger", name = "enable", havingValue = "true")
public Docket api() {
log.info("swagger配置信息初始化");
return new Docket(DocumentationType.SWAGGER_2)
.forCodeGeneration(true)
.pathProvider(new CustRelativePathProvider())
.select()
// .apis(RequestHandlerSelectors.any()) //显示所有类
// .apis(RequestHandlerSelectors.basePackage("com.kuria.site.controller"))
//只显示添加@Api注解的类
.apis(RequestHandlerSelectors.withClassAnnotation(Api.class))
.paths(PathSelectors.any())
.build()
.apiInfo(apiInfo()).globalOperationParameters(parameters());
}
private ApiInfo apiInfo() {
return new ApiInfoBuilder()
.title(swaggerYaml.getTitle())
.description(swaggerYaml.getDescription())
.version(swaggerYaml.getVersion())
.termsOfServiceUrl(swaggerYaml.getTermsOfServiceUrl())
.build();
}
/**
* 全局参数。 接口请求header中的token。 required false非必须
* @return
*/
private List<Parameter> parameters(){
List<Parameter> params = new ArrayList<>();
params.add(
new ParameterBuilder()
.name("token")
.description("请求令牌")
.modelRef(new ModelRef("String"))
.parameterType("header")
.required(false)
.build());
return params;
}
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/**").addResourceLocations("classpath:/static/");
registry.addResourceHandler("/swagger-ui.html").addResourceLocations("classpath:/META-INF/resources/");
registry.addResourceHandler("/doc.html").addResourceLocations("classpath:/META-INF/resources/");
registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");
super.addResourceHandlers(registry);
}
@Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}
/**
* 重新pathprovider,给所有url添加后缀
*/
public class CustRelativePathProvider extends AbstractPathProvider {
public static final String ROOT = "/";
@Override
public String getOperationPath(String operationPath) {
UriComponentsBuilder uriComponentsBuilder = UriComponentsBuilder.fromPath("/");
String uri = removeAdjacentForwardSlashes(uriComponentsBuilder.path(operationPath).build().toString());
return uri ".jhtml";
}
@Override
protected String applicationPath() {
return isNullOrEmpty(swaggerYaml.getContextPath()) ? ROOT : swaggerYaml.getContextPath();
}
@Override
protected String getDocumentationPath() {
return ROOT;
}
}
}
问题解决。
备注:测试发现,如果SwaggerConfig继承WebMvcConfigurationSupport类。而CrossOriginConfig实现WebMvcConfigurer接口,也是可以正常使用的, 不知道为何,待研究,也可能是我本地缓存,没有认真试验过。
踩坑之路,备注,方便日后查阅。