From 289e93b16d8eb7675f092182ac82fa2af4d4631b Mon Sep 17 00:00:00 2001 From: tanghc Date: Tue, 9 Jul 2019 13:38:29 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96SpringCloudGateway?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../sop/gatewaycommon/bean/SopConstants.java | 6 +- .../gatewaycommon/gateway/GatewayContext.java | 69 +++++++ .../gateway/filter/LimitFilter.java | 4 +- .../gateway/filter/ValidateFilter.java | 4 +- .../gateway/param/GatewayParamBuilder.java | 8 +- .../gateway/route/GatewayRouteRepository.java | 44 +---- .../route/GatewayZookeeperRouteManager.java | 23 ++- .../NameVersionRoutePredicateFactory.java | 30 ++- .../route/ReadBodyRoutePredicateFactory.java | 177 +++++++++++++++++- .../manager/DefaultRouteConfigManager.java | 3 +- .../result/BaseExecutorAdapter.java | 6 +- .../ServiceZookeeperApiMetaManager.java | 37 +--- 12 files changed, 295 insertions(+), 116 deletions(-) diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/SopConstants.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/SopConstants.java index c0ba6457..7ed69b01 100644 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/SopConstants.java +++ b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/bean/SopConstants.java @@ -9,7 +9,7 @@ import java.nio.charset.StandardCharsets; public class SopConstants { private SopConstants() {} - + public static final Charset CHARSET_UTF8 = StandardCharsets.UTF_8; public static final String UTF8 = "UTF-8"; public static final String FORMAT_JSON = "json"; @@ -51,5 +51,7 @@ public class SopConstants { public static final String SOP_MSG_CHANNEL_PATH = "/com.gitee.sop.channel"; - + public static final String UNKNOWN_SERVICE= "_sop_unknown_service_"; + public static final String UNKNOWN_METHOD = "_sop_unknown_method_"; + public static final String UNKNOWN_VERSION = "_sop_unknown_version_"; } diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/GatewayContext.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/GatewayContext.java index c1fe98ec..8b7e2d2c 100644 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/GatewayContext.java +++ b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/GatewayContext.java @@ -1,9 +1,78 @@ package com.gitee.sop.gatewaycommon.gateway; import com.gitee.sop.gatewaycommon.bean.ApiContext; +import com.gitee.sop.gatewaycommon.bean.SopConstants; +import com.gitee.sop.gatewaycommon.param.ApiParam; +import com.gitee.sop.gatewaycommon.util.RequestUtil; +import org.springframework.http.HttpMethod; +import org.springframework.util.MultiValueMap; +import org.springframework.web.server.ServerWebExchange; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static com.gitee.sop.gatewaycommon.bean.SopConstants.CACHE_REQUEST_BODY_FOR_MAP; +import static com.gitee.sop.gatewaycommon.bean.SopConstants.CACHE_REQUEST_BODY_OBJECT_KEY; /** * @author tanghc */ public class GatewayContext extends ApiContext { + + /** + * 获取请求参数 + * @param exchange + * @return + */ + public static ApiParam getApiParam(ServerWebExchange exchange) { + return exchange.getAttribute(SopConstants.CACHE_API_PARAM); + } + + /** + * 设置请求参数 + * @param exchange + * @param apiParam + */ + public static void setApiParam(ServerWebExchange exchange, ApiParam apiParam) { + exchange.getAttributes().put(SopConstants.CACHE_API_PARAM, apiParam); + } + + /** + * 获取Spring Cloud Gateway请求的原始参数。前提是要使用ReadBodyRoutePredicateFactory + * @param exchange + * @return 没有参数返回null + * @see com.gitee.sop.gatewaycommon.gateway.route.ReadBodyRoutePredicateFactory + */ + public static Map getRequestParams(ServerWebExchange exchange) { + Map params = exchange.getAttribute(CACHE_REQUEST_BODY_FOR_MAP); + if (params != null) { + return params; + } + if (exchange.getRequest().getMethod() == HttpMethod.GET) { + MultiValueMap queryParams = exchange.getRequest().getQueryParams(); + params = buildParams(queryParams); + } else { + String cachedBody = exchange.getAttribute(CACHE_REQUEST_BODY_OBJECT_KEY); + if (cachedBody != null) { + params = RequestUtil.parseQueryToMap(cachedBody); + } + } + if (params != null) { + exchange.getAttributes().put(CACHE_REQUEST_BODY_FOR_MAP, params); + } + return params; + } + + private static Map buildParams(MultiValueMap queryParams) { + if (queryParams == null || queryParams.size() == 0) { + return null; + } + Map params = new HashMap<>(queryParams.size()); + for (Map.Entry> entry : queryParams.entrySet()) { + String val = entry.getValue().get(0); + params.put(entry.getKey(), val); + } + return params; + } } diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/filter/LimitFilter.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/filter/LimitFilter.java index 4cdee962..084560bb 100644 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/filter/LimitFilter.java +++ b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/filter/LimitFilter.java @@ -3,8 +3,8 @@ package com.gitee.sop.gatewaycommon.gateway.filter; import com.gitee.sop.gatewaycommon.bean.ApiConfig; import com.gitee.sop.gatewaycommon.bean.ConfigLimitDto; import com.gitee.sop.gatewaycommon.bean.RouteConfig; -import com.gitee.sop.gatewaycommon.bean.SopConstants; import com.gitee.sop.gatewaycommon.exception.ApiException; +import com.gitee.sop.gatewaycommon.gateway.GatewayContext; import com.gitee.sop.gatewaycommon.limit.LimitManager; import com.gitee.sop.gatewaycommon.limit.LimitType; import com.gitee.sop.gatewaycommon.manager.LimitConfigManager; @@ -35,7 +35,7 @@ public class LimitFilter implements GlobalFilter, Ordered { if (!apiConfig.isOpenLimit()) { return chain.filter(exchange); } - ApiParam apiParam = exchange.getAttribute(SopConstants.CACHE_API_PARAM); + ApiParam apiParam = GatewayContext.getApiParam(exchange); ConfigLimitDto configLimitDto = this.findConfigLimitDto(apiConfig, apiParam, exchange); if (configLimitDto == null) { return null; diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/filter/ValidateFilter.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/filter/ValidateFilter.java index 32dc3baa..36911028 100644 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/filter/ValidateFilter.java +++ b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/filter/ValidateFilter.java @@ -2,8 +2,8 @@ package com.gitee.sop.gatewaycommon.gateway.filter; import com.gitee.sop.gatewaycommon.bean.ApiConfig; import com.gitee.sop.gatewaycommon.bean.ApiContext; -import com.gitee.sop.gatewaycommon.bean.SopConstants; import com.gitee.sop.gatewaycommon.exception.ApiException; +import com.gitee.sop.gatewaycommon.gateway.GatewayContext; import com.gitee.sop.gatewaycommon.param.ApiParam; import com.gitee.sop.gatewaycommon.validate.Validator; import lombok.extern.slf4j.Slf4j; @@ -24,7 +24,7 @@ public class ValidateFilter implements GlobalFilter, Ordered { ApiConfig apiConfig = ApiContext.getApiConfig(); // 解析参数 ApiParam param = apiConfig.getGatewayParamBuilder().build(exchange); - exchange.getAttributes().put(SopConstants.CACHE_API_PARAM, param); + GatewayContext.setApiParam(exchange, param); // 验证操作,这里有负责验证签名参数 Validator validator = apiConfig.getValidator(); try { diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/param/GatewayParamBuilder.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/param/GatewayParamBuilder.java index 19b17100..e0f1ae96 100644 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/param/GatewayParamBuilder.java +++ b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/param/GatewayParamBuilder.java @@ -1,9 +1,10 @@ package com.gitee.sop.gatewaycommon.gateway.param; -import com.gitee.sop.gatewaycommon.bean.SopConstants; +import com.gitee.sop.gatewaycommon.gateway.GatewayContext; import com.gitee.sop.gatewaycommon.param.BaseParamBuilder; import org.springframework.web.server.ServerWebExchange; +import java.util.Collections; import java.util.Map; /** @@ -12,7 +13,8 @@ import java.util.Map; public class GatewayParamBuilder extends BaseParamBuilder { @Override - public Map buildRequestParams(ServerWebExchange request) { - return request.getAttribute(SopConstants.CACHE_REQUEST_BODY_FOR_MAP); + public Map buildRequestParams(ServerWebExchange exchange) { + Map params = GatewayContext.getRequestParams(exchange); + return params == null ? Collections.emptyMap() : params; } } diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/route/GatewayRouteRepository.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/route/GatewayRouteRepository.java index f9f14d68..f3beb109 100644 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/route/GatewayRouteRepository.java +++ b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/route/GatewayRouteRepository.java @@ -3,22 +3,12 @@ package com.gitee.sop.gatewaycommon.gateway.route; import com.gitee.sop.gatewaycommon.bean.TargetRoute; import com.gitee.sop.gatewaycommon.manager.RouteRepository; import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.BeansException; -import org.springframework.beans.factory.BeanFactory; -import org.springframework.beans.factory.BeanFactoryAware; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cloud.gateway.event.PredicateArgsEvent; import org.springframework.cloud.gateway.event.RefreshRoutesEvent; -import org.springframework.cloud.gateway.handler.predicate.PredicateDefinition; -import org.springframework.cloud.gateway.handler.predicate.RoutePredicateFactory; import org.springframework.cloud.gateway.route.RouteDefinition; import org.springframework.cloud.gateway.route.RouteDefinitionRepository; -import org.springframework.cloud.gateway.support.ConfigurationUtils; import org.springframework.context.ApplicationEventPublisher; import org.springframework.context.ApplicationEventPublisherAware; -import org.springframework.core.convert.ConversionService; -import org.springframework.expression.spel.standard.SpelExpressionParser; -import org.springframework.validation.Validator; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; @@ -39,27 +29,17 @@ import static java.util.Collections.synchronizedMap; @Slf4j public class GatewayRouteRepository implements ApplicationEventPublisherAware, RouteDefinitionRepository, - BeanFactoryAware, RouteRepository { private final Map routes = synchronizedMap(new LinkedHashMap<>()); - private final SpelExpressionParser parser = new SpelExpressionParser(); - - @Autowired - private ConversionService conversionService; - - @Autowired - private Validator validator; - private ApplicationEventPublisher publisher; - private BeanFactory beanFactory; - @Override public Flux getRouteDefinitions() { List list = routes.values().parallelStream() .map(TargetRoute::getTargetRouteDefinition) + .filter(routeDefinition -> !routeDefinition.getId().contains("_first.route_")) .collect(Collectors.toList()); return Flux.fromIterable(list); } @@ -97,7 +77,6 @@ public class GatewayRouteRepository implements ApplicationEventPublisherAware, public String add(GatewayTargetRoute targetRoute) { GatewayRouteDefinition baseRouteDefinition = targetRoute.getRouteDefinition(); routes.put(baseRouteDefinition.getId(), targetRoute); - this.initPredicateDefinition(targetRoute); this.publisher.publishEvent(new RefreshRoutesEvent(this)); return "success"; } @@ -108,23 +87,6 @@ public class GatewayRouteRepository implements ApplicationEventPublisherAware, routes.put(baseRouteDefinition.getId(), targetRoute); } - protected void initPredicateDefinition(GatewayTargetRoute targetRoute) { - GatewayRouteDefinition routeDefinition = targetRoute.getRouteDefinition(); - RouteDefinition targetRouteDefinition = targetRoute.getTargetRouteDefinition(); - for (PredicateDefinition predicate : targetRouteDefinition.getPredicates()) { - Map args = predicate.getArgs(); - if (!args.isEmpty()) { - RoutePredicateFactory factory = new NameVersionRoutePredicateFactory(); - Map properties = factory.shortcutType().normalize(args, factory, this.parser, this.beanFactory); - Object config = factory.newConfig(); - ConfigurationUtils.bind(config, properties, factory.shortcutFieldPrefix(), predicate.getName(), - validator, conversionService); - this.publisher.publishEvent(new PredicateArgsEvent(this, routeDefinition.getId(), properties)); - } - } - - } - /** * 删除路由 */ @@ -150,8 +112,4 @@ public class GatewayRouteRepository implements ApplicationEventPublisherAware, this.publisher = applicationEventPublisher; } - @Override - public void setBeanFactory(BeanFactory beanFactory) throws BeansException { - this.beanFactory = beanFactory; - } } \ No newline at end of file diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/route/GatewayZookeeperRouteManager.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/route/GatewayZookeeperRouteManager.java index e515df03..b77844e0 100644 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/route/GatewayZookeeperRouteManager.java +++ b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/route/GatewayZookeeperRouteManager.java @@ -12,6 +12,7 @@ import org.springframework.core.env.Environment; import java.net.URI; import java.util.ArrayList; +import java.util.LinkedList; import java.util.List; /** @@ -44,7 +45,7 @@ public class GatewayZookeeperRouteManager extends BaseRouteManager filterDefinitionList = new ArrayList<>(gatewayRouteDefinition.getFilters().size()); - List predicateDefinitionList = new ArrayList<>(gatewayRouteDefinition.getPredicates().size()); + LinkedList predicateDefinitionList = new LinkedList<>(); for (GatewayFilterDefinition filter : gatewayRouteDefinition.getFilters()) { FilterDefinition filterDefinition = new FilterDefinition(); BeanUtils.copyProperties(filter, filterDefinition); @@ -56,10 +57,28 @@ public class GatewayZookeeperRouteManager extends BaseRouteManager predicateDefinitionList, String name, String args) { + for (PredicateDefinition predicateDefinition : predicateDefinitionList) { + // 如果已经存在,直接返回 + if (predicateDefinition.getName().equals(name)) { + return; + } + } + predicateDefinitionList.addFirst(new PredicateDefinition(name + "=" + args)); + } + } diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/route/NameVersionRoutePredicateFactory.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/route/NameVersionRoutePredicateFactory.java index b13a53c6..10ffb40e 100644 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/route/NameVersionRoutePredicateFactory.java +++ b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/route/NameVersionRoutePredicateFactory.java @@ -1,10 +1,11 @@ package com.gitee.sop.gatewaycommon.gateway.route; import com.gitee.sop.gatewaycommon.bean.SopConstants; +import com.gitee.sop.gatewaycommon.gateway.GatewayContext; import com.gitee.sop.gatewaycommon.param.ParamNames; -import com.gitee.sop.gatewaycommon.util.RequestUtil; import lombok.extern.slf4j.Slf4j; import org.springframework.cloud.gateway.handler.predicate.AbstractRoutePredicateFactory; +import org.springframework.util.CollectionUtils; import org.springframework.validation.annotation.Validated; import org.springframework.web.server.ServerWebExchange; @@ -15,17 +16,15 @@ import java.util.Map; import java.util.function.Predicate; /** + * 此断言决定执行哪个路由 + * * @author tanghc */ @Slf4j public class NameVersionRoutePredicateFactory extends AbstractRoutePredicateFactory { - public static final String PARAM_KEY = "param"; - public static final String REGEXP_KEY = "regexp"; - - private static final String CACHE_REQUEST_BODY_OBJECT_KEY = SopConstants.CACHE_REQUEST_BODY_OBJECT_KEY; - public static final String CACHE_REQUEST_BODY_FOR_MAP = SopConstants.CACHE_REQUEST_BODY_FOR_MAP; - + private static final String PARAM_KEY = "param"; + private static final String REGEXP_KEY = "regexp"; public NameVersionRoutePredicateFactory() { super(Config.class); @@ -37,7 +36,7 @@ public class NameVersionRoutePredicateFactory extends AbstractRoutePredicateFact } /** - * config.param为nameVersion + * config.param为nameVersion,即路由id * * @param config * @return 返回断言 @@ -46,21 +45,14 @@ public class NameVersionRoutePredicateFactory extends AbstractRoutePredicateFact public Predicate apply(Config config) { return exchange -> { - String cachedBody = exchange.getAttribute(CACHE_REQUEST_BODY_OBJECT_KEY); - if (cachedBody == null) { + Map params = GatewayContext.getRequestParams(exchange); + if (CollectionUtils.isEmpty(params)) { return false; } - Map params = exchange.getAttribute(CACHE_REQUEST_BODY_FOR_MAP); - if (params == null) { - params = RequestUtil.parseQueryToMap(cachedBody); - exchange.getAttributes().put(CACHE_REQUEST_BODY_FOR_MAP, params); - } - String nameVersion = config.param; - String name = params.getOrDefault(ParamNames.API_NAME, String.valueOf(System.currentTimeMillis())); + String name = params.getOrDefault(ParamNames.API_NAME, SopConstants.UNKNOWN_METHOD); String version = params.getOrDefault(ParamNames.VERSION_NAME, ""); - boolean match = (name + version).equals(nameVersion); - return match; + return (name + version).equals(nameVersion); }; } diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/route/ReadBodyRoutePredicateFactory.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/route/ReadBodyRoutePredicateFactory.java index dc5b3259..8dfd7f05 100644 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/route/ReadBodyRoutePredicateFactory.java +++ b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/route/ReadBodyRoutePredicateFactory.java @@ -1,19 +1,145 @@ package com.gitee.sop.gatewaycommon.gateway.route; +import org.apache.commons.lang.StringUtils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.cloud.gateway.handler.AsyncPredicate; +import org.springframework.cloud.gateway.handler.predicate.AbstractRoutePredicateFactory; import org.springframework.cloud.gateway.handler.predicate.ReadBodyPredicateFactory; +import org.springframework.core.io.buffer.DataBuffer; +import org.springframework.core.io.buffer.DataBufferUtils; +import org.springframework.http.HttpMethod; +import org.springframework.http.codec.HttpMessageReader; +import org.springframework.http.server.reactive.ServerHttpRequest; +import org.springframework.http.server.reactive.ServerHttpRequestDecorator; +import org.springframework.util.MultiValueMap; +import org.springframework.web.reactive.function.server.HandlerStrategies; +import org.springframework.web.reactive.function.server.ServerRequest; +import org.springframework.web.server.ServerWebExchange; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; import java.util.Objects; +import java.util.function.Predicate; + +import static org.springframework.cloud.gateway.filter.AdaptCachedBodyGlobalFilter.CACHED_REQUEST_BODY_KEY; /** - * 获取form表单插件,使用方式: + * 获取请求参数插件,兼容get,post,使用方式: * @Bean * ReadBodyRoutePredicateFactory readBodyRoutePredicateFactory() { * return new ReadBodyRoutePredicateFactory(); * } * + * @see org.springframework.cloud.gateway.handler.predicate.ReadBodyPredicateFactory + * 详见:https://blog.51cto.com/thinklili/2329184 * @author tanghc */ -public class ReadBodyRoutePredicateFactory extends ReadBodyPredicateFactory { +public class ReadBodyRoutePredicateFactory extends AbstractRoutePredicateFactory { + + protected static final Log LOGGER = LogFactory.getLog(ReadBodyPredicateFactory.class); + + private static final String TEST_ATTRIBUTE = "read_body_predicate_test_attribute"; + private static final String CACHE_REQUEST_BODY_OBJECT_KEY = "cachedRequestBodyObject"; + private static final List> messageReaders = HandlerStrategies.withDefaults().messageReaders(); + + + public ReadBodyRoutePredicateFactory() { + super(ReadBodyRoutePredicateFactory.Config.class); + } + + @Override + public AsyncPredicate applyAsync(Config config) { + return exchange -> { + HttpMethod method = exchange.getRequest().getMethod(); + if (method == HttpMethod.POST) { + return this.applyForPost(exchange, config); + } else { + return this.applyForGet(exchange, config); + } + }; + } + + /** + * 获取post表单参数 + * @param exchange + * @param config + * @return + */ + protected Mono applyForPost(ServerWebExchange exchange, Config config) { + Class inClass = config.getInClass(); + + Object cachedBody = exchange.getAttribute(CACHE_REQUEST_BODY_OBJECT_KEY); + // We can only read the body from the request once, once that happens if we try to read the body again an + // exception will be thrown. The below if/else caches the body object as a request attribute in the ServerWebExchange + // so if this filter is run more than once (due to more than one route using it) we do not try to read the + // request body multiple times + if (cachedBody != null) { + try { + boolean test = config.getPredicate().test(cachedBody); + exchange.getAttributes().put(TEST_ATTRIBUTE, test); + return Mono.just(test); + } catch (ClassCastException e) { + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("Predicate test failed because class in predicate does not match the cached body object", + e); + } + } + return Mono.just(false); + } else { + //Join all the DataBuffers so we have a single DataBuffer for the body + return DataBufferUtils.join(exchange.getRequest().getBody()) + .flatMap(dataBuffer -> { + //Update the retain counts so we can read the body twice, once to parse into an object + //that we can test the predicate against and a second time when the HTTP client sends + //the request downstream + //Note: if we end up reading the body twice we will run into a problem, but as of right + //now there is no good use case for doing this + DataBufferUtils.retain(dataBuffer); + //Make a slice for each read so each read has its own read/write indexes + Flux cachedFlux = Flux.defer(() -> Flux.just(dataBuffer.slice(0, dataBuffer.readableByteCount()))); + + ServerHttpRequest mutatedRequest = new ServerHttpRequestDecorator(exchange.getRequest()) { + @Override + public Flux getBody() { + return cachedFlux; + } + }; + return ServerRequest.create(exchange.mutate().request(mutatedRequest).build(), messageReaders) + .bodyToMono(inClass) + .doOnNext(objectValue -> { + exchange.getAttributes().put(CACHE_REQUEST_BODY_OBJECT_KEY, objectValue); + exchange.getAttributes().put(CACHED_REQUEST_BODY_KEY, cachedFlux); + }) + .map(objectValue -> config.getPredicate().test(objectValue)); + }); + + } + } + + /** + * 获取GET请求参数 + * @param exchange + * @param config + * @return + */ + protected Mono applyForGet(ServerWebExchange exchange, Config config) { + // 处理get请求 + MultiValueMap queryParams = exchange.getRequest().getQueryParams(); + String objectValue = null; + if (queryParams != null && queryParams.size() > 0) { + List params = new ArrayList<>(queryParams.size()); + for (Map.Entry> entry : queryParams.entrySet()) { + params.add(entry.getKey() + "=" + entry.getValue().get(0)); + } + objectValue = StringUtils.join(params.toArray()); + exchange.getAttributes().put(CACHE_REQUEST_BODY_OBJECT_KEY, objectValue); + } + return Mono.just(config.getPredicate().test(objectValue)); + } @Override public Config newConfig() { @@ -22,4 +148,51 @@ public class ReadBodyRoutePredicateFactory extends ReadBodyPredicateFactory { config.setPredicate(Objects::nonNull); return config; } + + @Override + @SuppressWarnings("unchecked") + public Predicate apply(Config config) { + throw new UnsupportedOperationException( + "ReadBodyPredicateFactory is only async."); + } + + public static class Config { + private Class inClass; + private Predicate predicate; + private Map hints; + + public Class getInClass() { + return inClass; + } + + public Config setInClass(Class inClass) { + this.inClass = inClass; + return this; + } + + public Predicate getPredicate() { + return predicate; + } + + public Config setPredicate(Class inClass, Predicate predicate) { + setInClass(inClass); + this.predicate = predicate; + return this; + } + + public Config setPredicate(Predicate predicate) { + this.predicate = predicate; + return this; + } + + public Map getHints() { + return hints; + } + + public Config setHints(Map hints) { + this.hints = hints; + return this; + } + } + } diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/manager/DefaultRouteConfigManager.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/manager/DefaultRouteConfigManager.java index 5b56bb66..800a675f 100644 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/manager/DefaultRouteConfigManager.java +++ b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/manager/DefaultRouteConfigManager.java @@ -38,9 +38,8 @@ public class DefaultRouteConfigManager implements RouteConfigManager { routeConfig = newRouteConfig(); routeConfig.setRouteId(routeId); routeConfigMap.put(routeId, routeConfig); - } else { - MyBeanUtil.copyPropertiesIgnoreNull(res, routeConfig); } + MyBeanUtil.copyPropertiesIgnoreNull(res, routeConfig); routeConfig.initRateLimiter(); } diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/result/BaseExecutorAdapter.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/result/BaseExecutorAdapter.java index 34ca08cc..bc4b22f7 100644 --- a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/result/BaseExecutorAdapter.java +++ b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/result/BaseExecutorAdapter.java @@ -145,15 +145,15 @@ public abstract class BaseExecutorAdapter implements ResultExecutor protected ApiInfo getApiInfo(T request) { Map params = this.getApiParam(request); - String name = this.getParamValue(params, ParamNames.API_NAME, "method.unknown"); - String version = this.getParamValue(params, ParamNames.VERSION_NAME, "version.unknown"); + String name = this.getParamValue(params, ParamNames.API_NAME, SopConstants.UNKNOWN_METHOD); + String version = this.getParamValue(params, ParamNames.VERSION_NAME, SopConstants.UNKNOWN_VERSION); TargetRoute targetRoute = RouteRepositoryContext.getRouteRepository().get(name + version); String serviceId = Optional.ofNullable(targetRoute) .flatMap(route -> Optional.ofNullable(route.getServiceRouteInfo())) .map(BaseServiceRouteInfo::getServiceId) - .orElse("serviceId.unknown"); + .orElse(SopConstants.UNKNOWN_SERVICE); BaseRouteDefinition baseRouteDefinition = Optional.ofNullable(targetRoute) .map(route -> route.getRouteDefinition()) diff --git a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/manager/ServiceZookeeperApiMetaManager.java b/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/manager/ServiceZookeeperApiMetaManager.java index 7dc95210..2807e123 100644 --- a/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/manager/ServiceZookeeperApiMetaManager.java +++ b/sop-common/sop-service-common/src/main/java/com/gitee/sop/servercommon/manager/ServiceZookeeperApiMetaManager.java @@ -4,7 +4,6 @@ import com.alibaba.fastjson.JSON; import com.gitee.sop.servercommon.bean.ServiceApiInfo; import com.gitee.sop.servercommon.bean.ServiceConstants; import com.gitee.sop.servercommon.bean.ZookeeperTool; -import com.gitee.sop.servercommon.route.GatewayPredicateDefinition; import com.gitee.sop.servercommon.route.GatewayRouteDefinition; import com.gitee.sop.servercommon.route.ServiceRouteInfo; import lombok.Getter; @@ -16,7 +15,6 @@ import org.springframework.core.env.Environment; import org.springframework.util.StringUtils; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collections; import java.util.List; @@ -36,14 +34,6 @@ public class ServiceZookeeperApiMetaManager implements ApiMetaManager { public static final String SOP_SERVICE_ROUTE_PATH = ServiceConstants.SOP_SERVICE_ROUTE_PATH; public static final String PATH_START_CHAR = "/"; - /** - * NameVersion=alipay.story.get1.0 - * see com.gitee.sop.gatewaycommon.routeDefinition.NameVersionRoutePredicateFactory - */ - private static String QUERY_PREDICATE_DEFINITION_TPL = "NameVersion=%s"; - - private static ServiceApiInfo.ApiMeta FIRST_API_META = new ServiceApiInfo.ApiMeta("_first.route_", "/", "v_000"); - private final String routeRootPath = SOP_SERVICE_ROUTE_PATH; private Environment environment; @@ -99,8 +89,7 @@ public class ServiceZookeeperApiMetaManager implements ApiMetaManager { */ protected ServiceRouteInfo buildServiceGatewayInfo(ServiceApiInfo serviceApiInfo) { List apis = serviceApiInfo.getApis(); - List routeDefinitionList = new ArrayList<>(apis.size() + 1); - routeDefinitionList.add(this.buildReadBodyRouteDefinition(serviceApiInfo)); + List routeDefinitionList = new ArrayList<>(apis.size()); for (ServiceApiInfo.ApiMeta apiMeta : apis) { GatewayRouteDefinition gatewayRouteDefinition = this.buildGatewayRouteDefinition(serviceApiInfo, apiMeta); routeDefinitionList.add(gatewayRouteDefinition); @@ -118,32 +107,12 @@ public class ServiceZookeeperApiMetaManager implements ApiMetaManager { return serviceRouteInfo; } - /** - * 添加com.gitee.sop.gatewaycommon.routeDefinition.ReadBodyRoutePredicateFactory,解决form表单获取不到问题 - * - * @return 返回路由定义 - */ - protected GatewayRouteDefinition buildReadBodyRouteDefinition(ServiceApiInfo serviceApiInfo) { - GatewayRouteDefinition readBodyRouteDefinition = this.buildGatewayRouteDefinition(serviceApiInfo, FIRST_API_META); - readBodyRouteDefinition.setOrder(Integer.MIN_VALUE); - - GatewayPredicateDefinition gatewayPredicateDefinition = new GatewayPredicateDefinition(); - gatewayPredicateDefinition.setName("ReadBody"); - GatewayPredicateDefinition readerBodyPredicateDefinition = this.buildNameVersionPredicateDefinition(FIRST_API_META); - List predicates = Arrays.asList(gatewayPredicateDefinition, readerBodyPredicateDefinition); - readBodyRouteDefinition.setPredicates(predicates); - - return readBodyRouteDefinition; - } - protected GatewayRouteDefinition buildGatewayRouteDefinition(ServiceApiInfo serviceApiInfo, ServiceApiInfo.ApiMeta apiMeta) { GatewayRouteDefinition gatewayRouteDefinition = new GatewayRouteDefinition(); // 唯一id规则:接口名 + 版本号 BeanUtils.copyProperties(apiMeta, gatewayRouteDefinition); gatewayRouteDefinition.setId(apiMeta.fetchNameVersion()); gatewayRouteDefinition.setFilters(Collections.emptyList()); - List predicates = Arrays.asList(this.buildNameVersionPredicateDefinition(apiMeta)); - gatewayRouteDefinition.setPredicates(predicates); String uri = this.buildUri(serviceApiInfo, apiMeta); String path = this.buildServletPath(serviceApiInfo, apiMeta); gatewayRouteDefinition.setUri(uri); @@ -166,10 +135,6 @@ public class ServiceZookeeperApiMetaManager implements ApiMetaManager { return servletPath; } - protected GatewayPredicateDefinition buildNameVersionPredicateDefinition(ServiceApiInfo.ApiMeta apiMeta) { - return new GatewayPredicateDefinition(String.format(QUERY_PREDICATE_DEFINITION_TPL, apiMeta.fetchNameVersion())); - } - /** * 上传接口信息到zookeeper *