springboot整合swagger2:
1、依赖包:
<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配置:
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。结果:
开始百度搜索解决大法:
基本网上说的解决方法不都是静态资源映射的问题,解决方式为:
@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也是一样的道理。):
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); } }
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接口,也是可以正常使用的, 不知道为何,待研究,也可能是我本地缓存,没有认真试验过。
踩坑之路,备注,方便日后查阅。