spring-cloud.version 2021.0.8 springboot-version 2.6.7
步骤:一.先添加需要pom依赖,因为时微服务所以先到项目的pom管理中添加已下依赖
<!--微服务文档聚合中间件--> <dependency> <groupId>com.github.xiaoymin</groupId> <artifactId>knife4j-aggregation-spring-boot-starter</artifactId> <version>2.0.9</version> </dependency> <!--springboot-swagger--> <dependency> <groupId>com.github.xiaoymin</groupId> <artifactId>knife4j-micro-spring-boot-starter</artifactId> <version>2.0.9</version> </dependency>
二.原本时每个需要用到的springboot都需要配置knife4j相关参数,这里时有多个服务用到所以把它抽成公共的模块
yml中相关配置(够用)具体参考https://doc.xiaominfo.com/docs
pom.xml
三.相关配置代码也是在网上找的
1.SwaggerAutoConfiguration package com.plan.common.swagger.config; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.function.Predicate; import com.github.xiaoymin.knife4j.spring.annotations.EnableKnife4j; import com.github.xiaoymin.knife4j.spring.extension.OpenApiExtensionResolver; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; 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.context.annotation.Import; import springfox.documentation.builders.ApiInfoBuilder; import springfox.documentation.builders.PathSelectors; import springfox.documentation.builders.RequestHandlerSelectors; import springfox.documentation.service.ApiInfo; import springfox.documentation.service.ApiKey; import springfox.documentation.service.AuthorizationScope; import springfox.documentation.service.Contact; import springfox.documentation.service.SecurityReference; import springfox.documentation.service.SecurityScheme; import springfox.documentation.spi.DocumentationType; import springfox.documentation.spi.service.contexts.SecurityContext; import springfox.documentation.spring.web.plugins.ApiSelectorBuilder; import springfox.documentation.spring.web.plugins.Docket; import springfox.documentation.swagger2.annotations.EnableSwagger2WebMvc; import javax.annotation.Resource; //@Configuration @EnableSwagger2WebMvc @EnableKnife4j @EnableConfigurationProperties(SwaggerProperties.class) @ConditionalOnProperty(name = "swagger.enabled",matchIfMissing = true) @Slf4j public class SwaggerAutoConfiguration { /** * 默认的排除路径,排除Spring Boot默认的错误处理路径和端点 * */ /*引入Knife4j提供的扩展类*/ private final OpenApiExtensionResolver openApiExtensionResolver; public SwaggerAutoConfiguration(OpenApiExtensionResolver openApiExtensionResolver) { this.openApiExtensionResolver = openApiExtensionResolver; } private static final List<String> DEFAULT_EXCLUDE_PATH = Arrays.asList("/error", "/actuator/**"); private static final String BASE_PATH = "/**"; @Bean public Docket api(SwaggerProperties swaggerProperties) { log.info("swagger初始化开始"); // base-path处理 if (swaggerProperties.getBasePath().isEmpty()) { swaggerProperties.getBasePath().add(BASE_PATH); } // noinspection unchecked List<Predicate<String>> basePath = new ArrayList<Predicate<String>>(); swaggerProperties.getBasePath().forEach(path -> basePath.add(PathSelectors.ant(path))); // exclude-path处理 if (swaggerProperties.getExcludePath().isEmpty()) { swaggerProperties.getExcludePath().addAll(DEFAULT_EXCLUDE_PATH); } List<Predicate<String>> excludePath = new ArrayList<>(); swaggerProperties.getExcludePath().forEach(path -> excludePath.add(PathSelectors.ant(path))); ApiSelectorBuilder builder = new Docket(DocumentationType.SWAGGER_2) .host(swaggerProperties.getHost()) .apiInfo(apiInfo(swaggerProperties)) .select() .apis(RequestHandlerSelectors.basePackage(swaggerProperties.getBasePackage())) ; swaggerProperties.getBasePath().forEach(p -> builder.paths(PathSelectors.ant(p))); swaggerProperties.getExcludePath().forEach(p -> builder.paths(PathSelectors.ant(p).negate())); //builder.build().extensions(openApiExtensionResolver.buildExtensions("")); log.info("swagger初始化结束"); return builder.build().extensions(openApiExtensionResolver.buildExtensions("")).securityContexts(securityContexts()) .pathMapping("/"); // .securitySchemes(securitySchemes()) // .securityContexts( // securityContexts() // ).pathMapping("/"); } /** * 安全模式,这里指定token通过Authorization头请求头传递 */ private List<SecurityScheme> securitySchemes() { List<SecurityScheme> apiKeyList = new ArrayList<SecurityScheme>(); apiKeyList.add(new ApiKey("Authorization", "Authorization", "header")); return apiKeyList; } /** * 安全上下文 */ private List<SecurityContext> securityContexts() { List<SecurityContext> securityContexts = new ArrayList<>(); securityContexts.add( SecurityContext.builder() // .securityReferences(defaultAuth()) // .operationSelector(o -> o.requestMappingPattern().matches("/.*")) .build()); return securityContexts; } /** * 默认的全局鉴权策略 * * @return */ private List<SecurityReference> defaultAuth() { AuthorizationScope authorizationScope = new AuthorizationScope("global", "accessEverything"); AuthorizationScope[] authorizationScopes = new AuthorizationScope[1]; authorizationScopes[0] = authorizationScope; List<SecurityReference> securityReferences = new ArrayList<>(); securityReferences.add(new SecurityReference("Authorization", authorizationScopes)); return securityReferences; } private ApiInfo apiInfo(SwaggerProperties swaggerProperties) { return new ApiInfoBuilder() .title(swaggerProperties.getTitle()) .description(swaggerProperties.getDescription()) .license(swaggerProperties.getLicense()) .licenseUrl(swaggerProperties.getLicenseUrl()) .termsOfServiceUrl(swaggerProperties.getTermsOfServiceUrl()) //.contact(new Contact(swaggerProperties.getContact().getName(), swaggerProperties.getContact().getUrl(), swaggerProperties.getContact().getEmail())) .version(swaggerProperties.getVersion()) .build(); } } /** * @Bean * public Docket docket(){ * return new Docket(DocumentationType.SWAGGER_2) * .apiInfo(apiInfo()) * .select() * //RequestHandlerSelectors,配置要扫描接口的方式 * //basePackage:指定要扫描的包 * //any():扫描全部 * //none():不扫描 * //withClassAnnotation():扫描类上的注解,参数是一个注解的反射对象 * //withMethodAnnotation():扫描方法上的注解 * .apis(RequestHandlerSelectors.basePackage("com.example.swagger.controller"))//指定某个包去扫描 * //paths():过滤什么路径 * .paths(PathSelectors.ant("/example/**")) * .build();//build * } */
2.SwaggerBeanPostProcessor
package com.plan.common.swagger.config; import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.util.ReflectionUtils; import org.springframework.web.servlet.mvc.method.RequestMappingInfoHandlerMapping; import springfox.documentation.spring.web.plugins.WebMvcRequestHandlerProvider; import java.lang.reflect.Field; import java.util.List; import java.util.stream.Collectors; /** * swagger 在 springboot 2.6.x 不兼容问题的处理 * * @author ruoyi */ public class SwaggerBeanPostProcessor implements 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) { throw new IllegalStateException(e); } } }
3.SwaggerProperties
package com.plan.common.swagger.config; import io.swagger.annotations.Authorization; import io.swagger.models.Contact; import org.springframework.boot.context.properties.ConfigurationProperties; import java.util.ArrayList; import java.util.List; @ConfigurationProperties("swagger") public class SwaggerProperties { /** * 是否开启swagger */ private Boolean enabled; /** * swagger会解析的包路径 **/ private String basePackage = ""; /** * swagger会解析的url规则 **/ private List<String> basePath = new ArrayList<>(); /** * 在basePath基础上需要排除的url规则 **/ private List<String> excludePath = new ArrayList<>(); /** * 标题 **/ private String title = ""; /** * 描述 **/ private String description = ""; /** * 版本 **/ private String version = ""; /** * 许可证 **/ private String license = ""; /** * 许可证URL **/ private String licenseUrl = ""; /** * 服务条款URL **/ private String termsOfServiceUrl = ""; /** * host信息 **/ private String host = ""; /** * 联系人信息 */ private Contact contact = new Contact(); /** * 全局统一鉴权配置 **/ private Authorization authorization = new Authorization(); public Boolean getEnabled() { return enabled; } public void setEnabled(Boolean enabled) { this.enabled = enabled; } public String getBasePackage() { return basePackage; } public void setBasePackage(String basePackage) { this.basePackage = basePackage; } public List<String> getBasePath() { return basePath; } public void setBasePath(List<String> basePath) { this.basePath = basePath; } public List<String> getExcludePath() { return excludePath; } public void setExcludePath(List<String> excludePath) { this.excludePath = excludePath; } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } public String getVersion() { return version; } public void setVersion(String version) { this.version = version; } public String getLicense() { return license; } public void setLicense(String license) { this.license = license; } public String getLicenseUrl() { return licenseUrl; } public void setLicenseUrl(String licenseUrl) { this.licenseUrl = licenseUrl; } public String getTermsOfServiceUrl() { return termsOfServiceUrl; } public void setTermsOfServiceUrl(String termsOfServiceUrl) { this.termsOfServiceUrl = termsOfServiceUrl; } public String getHost() { return host; } public void setHost(String host) { this.host = host; } public Contact getContact() { return contact; } public void setContact(Contact contact) { this.contact = contact; } public Authorization getAuthorization() { return authorization; } public void setAuthorization(Authorization authorization) { this.authorization = authorization; } public static class Contact { /** * 联系人 **/ private String name = ""; /** * 联系人url **/ private String url = ""; /** * 联系人email **/ private String email = ""; public String getName() { return name; } public void setName(String name) { this.name = name; } public String getUrl() { return url; } public void setUrl(String url) { this.url = url; } public String getEmail() { return email; } public void setEmail(String email) { this.email = email; } } public static class Authorization { /** * 鉴权策略ID,需要和SecurityReferences ID保持一致 */ private String name = ""; /** * 需要开启鉴权URL的正则 */ private String authRegex = "^.*$"; /** * 鉴权作用域列表 */ private List<AuthorizationScope> authorizationScopeList = new ArrayList<>(); private List<String> tokenUrlList = new ArrayList<>(); public String getName() { return name; } public void setName(String name) { this.name = name; } public String getAuthRegex() { return authRegex; } public void setAuthRegex(String authRegex) { this.authRegex = authRegex; } public List<AuthorizationScope> getAuthorizationScopeList() { return authorizationScopeList; } public void setAuthorizationScopeList(List<AuthorizationScope> authorizationScopeList) { this.authorizationScopeList = authorizationScopeList; } public List<String> getTokenUrlList() { return tokenUrlList; } public void setTokenUrlList(List<String> tokenUrlList) { this.tokenUrlList = tokenUrlList; } } public static class AuthorizationScope { /** * 作用域名称 */ private String scope = ""; /** * 作用域描述 */ private String description = ""; public String getScope() { return scope; } public void setScope(String scope) { this.scope = scope; } public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } } }
4.SwaggerWebConfiguration
package com.plan.common.swagger.config; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; /** * swagger 资源映射路径 * registry.addResourceHandler("doc.html").addResourceLocations("classpath:/META-INF/resources/"); * registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/"); * super.addResourceHandlers(registry); * * @author ruoyi */ @Configuration public class SwaggerWebConfiguration implements WebMvcConfigurer { @Override public void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler("doc.html").addResourceLocations("classpath:/META-INF/resources/"); registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/"); /** swagger-ui 地址 */ registry.addResourceHandler("/swagger-ui/**") .addResourceLocations("classpath:/META-INF/resources/webjars/springfox-swagger-ui/"); } }
5.
四.聚合文档启动服务
1.新建一个服务doc专门来管理和启动文档服务,只要启动类其它不要
2.yml配置(基于cloud模式的微服务聚合)其它基于nacos也类似具体可参考https://doc.xiaominfo.com/docs/middleware-sources/aggregation-nacos
3.记得在gateway放开文档相关路径v2/api-doc
4.使用
接口请求方式一定要具体post,get,要不然doc文档会自动为每个接口生成7个请求方式文档
然后启动访问doc服务http://localhost:端口/doc.html查看效果