diff --git a/build.gradle b/build.gradle index fa6b608..dd9f780 100644 --- a/build.gradle +++ b/build.gradle @@ -23,6 +23,7 @@ dependencies { annotationProcessor 'org.springframework.boot:spring-boot-configuration-processor' implementation 'org.springframework.boot:spring-boot-starter-web' implementation 'org.springframework.boot:spring-boot-starter' + implementation 'org.springframework:spring-webflux:5.0.1.RELEASE' testImplementation 'org.springframework.boot:spring-boot-starter-test' testImplementation 'org.springframework.boot:spring-boot-starter-validation' } diff --git a/src/main/java/com/zkdlu/apiresponsespringbootstarter/autoconfig/EnableReactiveResponse.java b/src/main/java/com/zkdlu/apiresponsespringbootstarter/autoconfig/EnableReactiveResponse.java new file mode 100644 index 0000000..38420c3 --- /dev/null +++ b/src/main/java/com/zkdlu/apiresponsespringbootstarter/autoconfig/EnableReactiveResponse.java @@ -0,0 +1,17 @@ +package com.zkdlu.apiresponsespringbootstarter.autoconfig; + +import com.zkdlu.apiresponsespringbootstarter.core.advice.ExceptionAdvice; +import com.zkdlu.apiresponsespringbootstarter.core.config.ReactiveConfig; +import com.zkdlu.apiresponsespringbootstarter.core.service.ResponseService; +import org.springframework.context.annotation.Import; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +@Import({ResponseService.class, ExceptionAdvice.class, ReactiveConfig.class }) +public @interface EnableReactiveResponse { +} diff --git a/src/main/java/com/zkdlu/apiresponsespringbootstarter/core/advice/ReactiveResponseAdvice.java b/src/main/java/com/zkdlu/apiresponsespringbootstarter/core/advice/ReactiveResponseAdvice.java new file mode 100644 index 0000000..1bf2dda --- /dev/null +++ b/src/main/java/com/zkdlu/apiresponsespringbootstarter/core/advice/ReactiveResponseAdvice.java @@ -0,0 +1,81 @@ +package com.zkdlu.apiresponsespringbootstarter.core.advice; + +import com.zkdlu.apiresponsespringbootstarter.autoconfig.ResponseProperties; +import com.zkdlu.apiresponsespringbootstarter.core.model.CommonResult; +import com.zkdlu.apiresponsespringbootstarter.core.service.ResponseService; +import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication; +import org.springframework.core.MethodParameter; +import org.springframework.http.codec.HttpMessageWriter; +import org.springframework.http.server.PathContainer; +import org.springframework.web.reactive.HandlerResult; +import org.springframework.web.reactive.accept.RequestedContentTypeResolver; +import org.springframework.web.reactive.result.method.annotation.ResponseBodyResultHandler; +import org.springframework.web.server.ServerWebExchange; +import org.springframework.web.util.pattern.PathPattern; +import org.springframework.web.util.pattern.PathPatternParser; +import reactor.core.publisher.Mono; + +import java.util.Arrays; +import java.util.List; + +@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.REACTIVE) +public class ReactiveResponseAdvice extends ResponseBodyResultHandler { + private final List whitelist = Arrays.asList( + new PathPatternParser().parse("/v*/api-docs"), + new PathPatternParser().parse("/swagger-resources/**"), + new PathPatternParser().parse("/swagger-ui.html"), + new PathPatternParser().parse("/webjars/**"), + new PathPatternParser().parse("/swagger/**")); + private static MethodParameter param; + + private final ResponseService responseService; + private final ResponseProperties responseProperties; + + static { + try { + param = new MethodParameter(ReactiveResponseAdvice.class + .getDeclaredMethod("methodForParams"), -1); + } catch (NoSuchMethodException e) { + e.printStackTrace(); + } + } + + public ReactiveResponseAdvice(List> writers, RequestedContentTypeResolver resolver, ResponseService responseService, ResponseProperties responseProperties) { + super(writers, resolver); + this.responseService = responseService; + this.responseProperties = responseProperties; + } + + @Override + public boolean supports(HandlerResult result) { + return true; + } + + private static Object methodForParams() { + return null; + } + + @Override + public Mono handleResult(ServerWebExchange exchange, HandlerResult result) { + if (whitelist.stream().anyMatch(pathPattern -> pathPattern.matches(PathContainer.parsePath(exchange.getRequest().getURI().getPath()))) + || result.getReturnValue() instanceof CommonResult) { + return writeBody(result.getReturnValue(), param, exchange); + } + + Object obj = result.getReturnValue(); + + if (result.getReturnValue() instanceof Mono) { + obj = ((Mono) result.getReturnValue()).block(); + if (obj instanceof CommonResult) { + return writeBody(obj, param, exchange); + } + } + + CommonResult body = responseService.getResult(obj); + body.setSuccess(true); + body.setCode(responseProperties.getSuccess().getCode()); + body.setMsg(responseProperties.getSuccess().getMsg()); + + return writeBody(body, param, exchange); + } +} \ No newline at end of file diff --git a/src/main/java/com/zkdlu/apiresponsespringbootstarter/core/config/ReactiveConfig.java b/src/main/java/com/zkdlu/apiresponsespringbootstarter/core/config/ReactiveConfig.java new file mode 100644 index 0000000..90e87a5 --- /dev/null +++ b/src/main/java/com/zkdlu/apiresponsespringbootstarter/core/config/ReactiveConfig.java @@ -0,0 +1,62 @@ +package com.zkdlu.apiresponsespringbootstarter.core.config; + +import com.zkdlu.apiresponsespringbootstarter.autoconfig.ResponseProperties; +import com.zkdlu.apiresponsespringbootstarter.core.advice.ReactiveResponseAdvice; +import com.zkdlu.apiresponsespringbootstarter.core.service.ResponseService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.AutoConfigureAfter; +import org.springframework.boot.autoconfigure.AutoConfigureOrder; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication; +import org.springframework.boot.autoconfigure.http.codec.CodecsAutoConfiguration; +import org.springframework.boot.autoconfigure.validation.ValidationAutoConfiguration; +import org.springframework.boot.autoconfigure.web.reactive.ReactiveWebServerFactoryAutoConfiguration; +import org.springframework.boot.autoconfigure.web.reactive.WebFluxProperties; +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 org.springframework.core.Ordered; +import org.springframework.http.codec.ServerCodecConfigurer; +import org.springframework.web.reactive.accept.RequestedContentTypeResolver; +import org.springframework.web.reactive.config.DelegatingWebFluxConfiguration; +import org.springframework.web.reactive.config.WebFluxConfigurationSupport; +import org.springframework.web.reactive.config.WebFluxConfigurer; +import org.springframework.web.reactive.result.method.annotation.ResponseBodyResultHandler; + +@Configuration(proxyBeanMethods = false) +@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.REACTIVE) +@ConditionalOnClass(WebFluxConfigurer.class) +@ConditionalOnMissingBean({ WebFluxConfigurationSupport.class }) +@AutoConfigureAfter({ ReactiveWebServerFactoryAutoConfiguration.class, CodecsAutoConfiguration.class, + ValidationAutoConfiguration.class }) +@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10) +public class ReactiveConfig { + @Configuration(proxyBeanMethods = false) + @EnableConfigurationProperties({ WebFluxProperties.class }) + @Import({ EnableWebFluxConfiguration.class }) + public static class WebFluxConfig implements WebFluxConfigurer { + @Override + public void configureHttpMessageCodecs(ServerCodecConfigurer configurer) { + } + } + + @Configuration(proxyBeanMethods = false) + public static class EnableWebFluxConfiguration extends DelegatingWebFluxConfiguration { + } + + @Autowired + private ResponseService responseService; + + @Autowired + private ResponseProperties responseProperties; + + @Bean + public ResponseBodyResultHandler responseBodyResultHandler( + ServerCodecConfigurer serverCodecConfigurer, + RequestedContentTypeResolver contentTypeResolver) { + return new ReactiveResponseAdvice(serverCodecConfigurer.getWriters(), + contentTypeResolver, responseService, responseProperties); + } +} diff --git a/src/main/java/com/zkdlu/apiresponsespringbootstarter/core/config/WebConfig.java b/src/main/java/com/zkdlu/apiresponsespringbootstarter/core/config/WebConfig.java index 4314a66..eb2ef31 100644 --- a/src/main/java/com/zkdlu/apiresponsespringbootstarter/core/config/WebConfig.java +++ b/src/main/java/com/zkdlu/apiresponsespringbootstarter/core/config/WebConfig.java @@ -1,5 +1,6 @@ package com.zkdlu.apiresponsespringbootstarter.core.config; +import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.http.converter.HttpMessageConverter; @@ -11,6 +12,7 @@ import java.util.List; @EnableWebMvc +@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET) @Configuration public class WebConfig implements WebMvcConfigurer { @Override diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index e69de29..afa0c3c 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -0,0 +1,2 @@ +spring.main.allow-bean-definition-overriding=true +spring.main.web-application-type=REACTIVE \ No newline at end of file