diff --git a/.gitignore b/.gitignore index 6143e53..9ff7aa7 100644 --- a/.gitignore +++ b/.gitignore @@ -20,3 +20,6 @@ # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml hs_err_pid* +.idea/ +spring-boot-starter-swagger.iml +target/ diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..a322442 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,6 @@ +language: java + +jdk: + - oraclejdk8 + +install: mvn install -DskipTests=true -Dmaven.javadoc.skip=true diff --git a/README.md b/README.md index ac3eded..9f96805 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,7 @@ com.didispace spring-boot-starter-swagger - 1.3.0.RELEASE + 1.4.0.RELEASE ``` @@ -53,9 +53,11 @@ public class Bootstrap { ## 配置示例 ```properties +swagger.enabled=true + swagger.title=spring-boot-starter-swagger swagger.description=Starter for swagger 2.x -swagger.version=1.3.0.RELEASE +swagger.version=1.4.0.RELEASE swagger.license=Apache License, Version 2.0 swagger.licenseUrl=https://www.apache.org/licenses/LICENSE-2.0.html swagger.termsOfServiceUrl=https://github.com/dyc87112/spring-boot-starter-swagger @@ -65,6 +67,17 @@ swagger.contact.email=dyc87112@qq.com swagger.base-package=com.didispace swagger.base-path=/** swagger.exclude-path=/error, /ops/** + +swagger.globalOperationParameters[0].name=name one +swagger.globalOperationParameters[0].description=some description one +swagger.globalOperationParameters[0].modelRef=string +swagger.globalOperationParameters[0].parameterType=header +swagger.globalOperationParameters[0].required=true +swagger.globalOperationParameters[1].name=name two +swagger.globalOperationParameters[1].description=some description two +swagger.globalOperationParameters[1].modelRef=string +swagger.globalOperationParameters[1].parameterType=body +swagger.globalOperationParameters[1].required=false ``` ## 配置说明 @@ -72,6 +85,7 @@ swagger.exclude-path=/error, /ops/** ### 默认配置 ``` +- swagger.enabled=是否启用swagger,默认:true - swagger.title=标题 - swagger.description=描述 - swagger.version=版本 @@ -85,9 +99,19 @@ swagger.exclude-path=/error, /ops/** - swagger.base-path=需要处理的基础URL规则,默认:/** - swagger.exclude-path=需要排除的URL规则,默认:空 - swagger.host=文档的host信息,默认:空 +- swagger.globalOperationParameters[0].name=参数名 +- swagger.globalOperationParameters[0].description=描述信息 +- swagger.globalOperationParameters[0].modelRef=指定参数类型 +- swagger.globalOperationParameters[0].parameterType=指定参数存放位置,可选header,query,path,body.form +- swagger.globalOperationParameters[0].required=指定参数是否必传,true,false ``` -> host属性从1.3.0.RELEASE开始支持 + +> `1.3.0.RELEASE`新增:`swagger.host`属性,同时也支持指定docket的配置 +> +> `1.4.0.RELEASE`新增: +> - `swagger.enabled`:用于开关swagger的配置 +> - `swagger.globalOperationParameters`:用于设置全局的参数,比如:header部分的accessToken等。该参数支持指定docket的配置。 ### Path规则说明 @@ -129,10 +153,19 @@ swagger.exclude-path=/ops/**, /error - swagger.docket..base-package=swagger扫描的基础包,默认:全扫描 - swagger.docket..base-path=需要处理的基础URL规则,默认:/** - swagger.docket..exclude-path=需要排除的URL规则,默认:空 +- swagger.docket..name=参数名 +- swagger.docket..modelRef=指定参数类型 +- swagger.docket..parameterType=指定参数存放位置,可选header,query,path,body.form +- swagger.docket..required=true=指定参数是否必传,true,false +- swagger.docket..globalOperationParameters[0].name=参数名 +- swagger.docket..globalOperationParameters[0].description=描述信息 +- swagger.docket..globalOperationParameters[0].modelRef=指定参数存放位置,可选header,query,path,body.form +- swagger.docket..globalOperationParameters[0].parameterType=指定参数是否必传,true,false ``` 说明:``为swagger文档的分组名称,同一个项目中可以配置多个分组,用来划分不同的API文档。 + **分组配置示例** ```properties @@ -144,12 +177,16 @@ swagger.docket.aaa.contact.name=zhaiyongchao swagger.docket.aaa.contact.url=http://spring4all.com/ swagger.docket.aaa.contact.email=didi@potatomato.club swagger.docket.aaa.excludePath=/ops/** +swagger.docket.aaa.globalOperationParameters[0].name=name three +swagger.docket.aaa.globalOperationParameters[0].description=some description three override +swagger.docket.aaa.globalOperationParameters[0].modelRef=string +swagger.docket.aaa.globalOperationParameters[0].parameterType=header swagger.docket.bbb.title=group-bbb swagger.docket.bbb.basePackage=com.yonghui ``` -说明:默认配置与分组配置可以一起使用。在分组配置中没有配置的内容将使用默认配置替代,所以默认配置可以作为分组配置公共部分属性的配置。 +说明:默认配置与分组配置可以一起使用。在分组配置中没有配置的内容将使用默认配置替代,所以默认配置可以作为分组配置公共部分属性的配置。`swagger.docket.aaa.globalOperationParameters[0].name`会覆盖同名的全局配置。 ### JSR-303校验注解支持 diff --git a/pom.xml b/pom.xml index e021185..680f71e 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ com.didispace spring-boot-starter-swagger - 1.3.0.RELEASE + 1.4.0.RELEASE spring-boot-starter-swagger https://github.com/dyc87112/spring-boot-starter-swagger diff --git a/src/main/java/com/didispace/swagger/EnableSwagger2Doc.java b/src/main/java/com/didispace/swagger/EnableSwagger2Doc.java index 01b5741..0ea7844 100644 --- a/src/main/java/com/didispace/swagger/EnableSwagger2Doc.java +++ b/src/main/java/com/didispace/swagger/EnableSwagger2Doc.java @@ -1,8 +1,6 @@ package com.didispace.swagger; import org.springframework.context.annotation.Import; -import springfox.bean.validators.configuration.BeanValidatorPluginsConfiguration; -import springfox.documentation.swagger2.annotations.EnableSwagger2; import java.lang.annotation.*; @@ -15,8 +13,7 @@ @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited -@EnableSwagger2 -@Import({SwaggerAutoConfiguration.class, BeanValidatorPluginsConfiguration.class}) +@Import({SwaggerAutoConfiguration.class}) public @interface EnableSwagger2Doc { diff --git a/src/main/java/com/didispace/swagger/Swagger2Configuration.java b/src/main/java/com/didispace/swagger/Swagger2Configuration.java new file mode 100644 index 0000000..0703d28 --- /dev/null +++ b/src/main/java/com/didispace/swagger/Swagger2Configuration.java @@ -0,0 +1,21 @@ +package com.didispace.swagger; + +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; +import springfox.bean.validators.configuration.BeanValidatorPluginsConfiguration; +import springfox.documentation.swagger2.configuration.Swagger2DocumentationConfiguration; + +/** + * @author 翟永超 + * Create Date: 2017/9/7. + * My blog: http://blog.didispace.com + */ +@Configuration +@ConditionalOnProperty(name = "swagger.enabled", matchIfMissing = true) +@Import({ + Swagger2DocumentationConfiguration.class, + BeanValidatorPluginsConfiguration.class +}) +public class Swagger2Configuration { +} diff --git a/src/main/java/com/didispace/swagger/SwaggerAutoConfiguration.java b/src/main/java/com/didispace/swagger/SwaggerAutoConfiguration.java index d1689be..9529c64 100644 --- a/src/main/java/com/didispace/swagger/SwaggerAutoConfiguration.java +++ b/src/main/java/com/didispace/swagger/SwaggerAutoConfiguration.java @@ -2,31 +2,42 @@ import com.google.common.base.Predicate; import com.google.common.base.Predicates; +import com.google.common.collect.Lists; import org.springframework.beans.BeansException; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.BeanFactoryAware; import org.springframework.beans.factory.config.ConfigurableBeanFactory; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; 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.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.Contact; +import springfox.documentation.service.Parameter; import springfox.documentation.spi.DocumentationType; import springfox.documentation.spring.web.plugins.Docket; import java.util.ArrayList; import java.util.LinkedList; import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; /** * @author 翟永超 - * Create date :2017/8/7. + * Create date:2017/8/7. * My blog: http://blog.didispace.com */ @Configuration +@Import({ + Swagger2Configuration.class +}) public class SwaggerAutoConfiguration implements BeanFactoryAware { private BeanFactory beanFactory; @@ -39,11 +50,12 @@ public SwaggerProperties swaggerProperties() { @Bean @ConditionalOnMissingBean + @ConditionalOnProperty(name = "swagger.enabled", matchIfMissing = true) public List createRestApi(SwaggerProperties swaggerProperties) { ConfigurableBeanFactory configurableBeanFactory = (ConfigurableBeanFactory) beanFactory; // 没有分组 - if(swaggerProperties.getDocket().size() == 0) { + if (swaggerProperties.getDocket().size() == 0) { ApiInfo apiInfo = new ApiInfoBuilder() .title(swaggerProperties.getTitle()) .description(swaggerProperties.getDescription()) @@ -58,23 +70,26 @@ public List createRestApi(SwaggerProperties swaggerProperties) { // base-path处理 // 当没有配置任何path的时候,解析/** - if(swaggerProperties.getBasePath().isEmpty()) { + if (swaggerProperties.getBasePath().isEmpty()) { swaggerProperties.getBasePath().add("/**"); } List> basePath = new ArrayList(); - for(String path : swaggerProperties.getBasePath()) { + for (String path : swaggerProperties.getBasePath()) { basePath.add(PathSelectors.ant(path)); } // exclude-path处理 List> excludePath = new ArrayList(); - for(String path : swaggerProperties.getExcludePath()) { + for (String path : swaggerProperties.getExcludePath()) { excludePath.add(PathSelectors.ant(path)); } + Docket docket = new Docket(DocumentationType.SWAGGER_2) .host(swaggerProperties.getHost()) .apiInfo(apiInfo) + .globalOperationParameters(buildGlobalOperationParametersFromSwaggerProperties( + swaggerProperties.getGlobalOperationParameters())) .select() .apis(RequestHandlerSelectors.basePackage(swaggerProperties.getBasePackage())) .paths( @@ -91,7 +106,7 @@ public List createRestApi(SwaggerProperties swaggerProperties) { // 分组创建 List docketList = new LinkedList<>(); - for(String groupName : swaggerProperties.getDocket().keySet()) { + for (String groupName : swaggerProperties.getDocket().keySet()) { SwaggerProperties.DocketInfo docketInfo = swaggerProperties.getDocket().get(groupName); ApiInfo apiInfo = new ApiInfoBuilder() @@ -102,9 +117,9 @@ public List createRestApi(SwaggerProperties swaggerProperties) { .licenseUrl(docketInfo.getLicenseUrl().isEmpty() ? swaggerProperties.getLicenseUrl() : docketInfo.getLicenseUrl()) .contact( new Contact( - docketInfo.getContact().getName().isEmpty() ? swaggerProperties.getContact().getName() : docketInfo.getContact().getName(), - docketInfo.getContact().getUrl().isEmpty() ? swaggerProperties.getContact().getUrl() : docketInfo.getContact().getUrl(), - docketInfo.getContact().getEmail().isEmpty() ? swaggerProperties.getContact().getEmail() : docketInfo.getContact().getEmail() + docketInfo.getContact().getName().isEmpty() ? swaggerProperties.getContact().getName() : docketInfo.getContact().getName(), + docketInfo.getContact().getUrl().isEmpty() ? swaggerProperties.getContact().getUrl() : docketInfo.getContact().getUrl(), + docketInfo.getContact().getEmail().isEmpty() ? swaggerProperties.getContact().getEmail() : docketInfo.getContact().getEmail() ) ) .termsOfServiceUrl(docketInfo.getTermsOfServiceUrl().isEmpty() ? swaggerProperties.getTermsOfServiceUrl() : docketInfo.getTermsOfServiceUrl()) @@ -112,23 +127,25 @@ public List createRestApi(SwaggerProperties swaggerProperties) { // base-path处理 // 当没有配置任何path的时候,解析/** - if(docketInfo.getBasePath().isEmpty()) { + if (docketInfo.getBasePath().isEmpty()) { docketInfo.getBasePath().add("/**"); } List> basePath = new ArrayList(); - for(String path : docketInfo.getBasePath()) { + for (String path : docketInfo.getBasePath()) { basePath.add(PathSelectors.ant(path)); } // exclude-path处理 List> excludePath = new ArrayList(); - for(String path : docketInfo.getExcludePath()) { + for (String path : docketInfo.getExcludePath()) { excludePath.add(PathSelectors.ant(path)); } Docket docket = new Docket(DocumentationType.SWAGGER_2) .host(swaggerProperties.getHost()) .apiInfo(apiInfo) + .globalOperationParameters(assemblyGlobalOperationParameters(swaggerProperties.getGlobalOperationParameters(), + docketInfo.getGlobalOperationParameters())) .groupName(groupName) .select() .apis(RequestHandlerSelectors.basePackage(docketInfo.getBasePackage())) @@ -150,4 +167,49 @@ public List createRestApi(SwaggerProperties swaggerProperties) { public void setBeanFactory(BeanFactory beanFactory) throws BeansException { this.beanFactory = beanFactory; } + + private List buildGlobalOperationParametersFromSwaggerProperties( + List globalOperationParameters) { + List parameters = Lists.newArrayList(); + for (SwaggerProperties.GlobalOperationParameter globalOperationParameter : globalOperationParameters) { + parameters.add(new ParameterBuilder() + .name(globalOperationParameter.getName()) + .description(globalOperationParameter.getDescription()) + .modelRef(new ModelRef(globalOperationParameter.getModelRef())) + .parameterType(globalOperationParameter.getParameterType()) + .required(Boolean.parseBoolean(globalOperationParameter.getRequired())) + .build()); + } + return parameters; + } + + /** + * 局部参数按照name覆盖局部参数 + * + * @param globalOperationParameters + * @param docketOperationParameters + * @return + */ + private List assemblyGlobalOperationParameters( + List globalOperationParameters, + List docketOperationParameters) { + + if (docketOperationParameters == null || docketOperationParameters.isEmpty()) { + return buildGlobalOperationParametersFromSwaggerProperties(globalOperationParameters); + } + + Set docketNames = docketOperationParameters.stream() + .map(SwaggerProperties.GlobalOperationParameter::getName) + .collect(Collectors.toSet()); + + List resultOperationParameters = Lists.newArrayList(); + + for (SwaggerProperties.GlobalOperationParameter parameter : globalOperationParameters) { + if (!docketNames.contains(parameter.getName())) { + resultOperationParameters.add(parameter); + } + } + resultOperationParameters.addAll(docketOperationParameters); + return buildGlobalOperationParametersFromSwaggerProperties(resultOperationParameters); + } } diff --git a/src/main/java/com/didispace/swagger/SwaggerProperties.java b/src/main/java/com/didispace/swagger/SwaggerProperties.java index cafc22d..75314ff 100644 --- a/src/main/java/com/didispace/swagger/SwaggerProperties.java +++ b/src/main/java/com/didispace/swagger/SwaggerProperties.java @@ -3,6 +3,7 @@ import lombok.Data; import lombok.NoArgsConstructor; import org.springframework.boot.context.properties.ConfigurationProperties; +import springfox.documentation.schema.ModelRef; import java.util.ArrayList; import java.util.LinkedHashMap; @@ -18,6 +19,9 @@ @ConfigurationProperties("swagger") public class SwaggerProperties { + /**是否开启swagger**/ + private Boolean enabled; + /**标题**/ private String title = ""; /**描述**/ @@ -47,6 +51,30 @@ public class SwaggerProperties { /**host信息**/ private String host = ""; + /**全局参数配置**/ + private List globalOperationParameters; + + + @Data + @NoArgsConstructor + public static class GlobalOperationParameter{ + /**参数名**/ + private String name; + + /**描述信息**/ + private String description; + + /**指定参数类型**/ + private String modelRef; + + /**参数放在哪个地方:header,query,path,body.form**/ + private String parameterType; + + /**参数是否必须传**/ + private String required; + + } + @Data @NoArgsConstructor public static class DocketInfo { @@ -74,6 +102,8 @@ public static class DocketInfo { /**在basePath基础上需要排除的url规则**/ private List excludePath = new ArrayList<>(); + private List globalOperationParameters; + } @Data