mirror of
https://gitee.com/durcframework/SOP.git
synced 2025-08-11 12:56:28 +08:00
init
This commit is contained in:
@@ -45,18 +45,20 @@
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<!--<dependency>-->
|
||||
<!--<groupId>org.springframework.cloud</groupId>-->
|
||||
<!--<artifactId>spring-cloud-starter-netflix-zuul</artifactId>-->
|
||||
<!--</dependency>-->
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-gateway</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-webflux</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
|
@@ -1,8 +1,9 @@
|
||||
package com.gitee.sop.gatewaycommon.bean;
|
||||
|
||||
import com.gitee.sop.gatewaycommon.param.ApiParamBuilder;
|
||||
import com.gitee.sop.gatewaycommon.gateway.param.GatewayParamBuilder;
|
||||
import com.gitee.sop.gatewaycommon.gateway.result.GatewayResult;
|
||||
import com.gitee.sop.gatewaycommon.param.ParamBuilder;
|
||||
import com.gitee.sop.gatewaycommon.result.ApiResultExecutor;
|
||||
import com.gitee.sop.gatewaycommon.gateway.result.GatewayResultExecutor;
|
||||
import com.gitee.sop.gatewaycommon.result.ResultExecutor;
|
||||
import com.gitee.sop.gatewaycommon.secret.AppSecretManager;
|
||||
import com.gitee.sop.gatewaycommon.secret.CacheAppSecretManager;
|
||||
@@ -14,7 +15,12 @@ import com.gitee.sop.gatewaycommon.validate.ApiValidator;
|
||||
import com.gitee.sop.gatewaycommon.validate.Encrypter;
|
||||
import com.gitee.sop.gatewaycommon.validate.Signer;
|
||||
import com.gitee.sop.gatewaycommon.validate.Validator;
|
||||
import com.gitee.sop.gatewaycommon.zuul.configuration.BaseZuulController;
|
||||
import com.gitee.sop.gatewaycommon.zuul.param.ZuulParamBuilder;
|
||||
import com.gitee.sop.gatewaycommon.zuul.result.ZuulResultExecutor;
|
||||
import com.netflix.zuul.context.RequestContext;
|
||||
import lombok.Data;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
@@ -32,9 +38,14 @@ public class ApiConfig {
|
||||
}
|
||||
|
||||
/**
|
||||
* 合并结果处理
|
||||
* gateway合并结果处理
|
||||
*/
|
||||
private ResultExecutor resultExecutor = new ApiResultExecutor();
|
||||
private ResultExecutor<ServerWebExchange, GatewayResult> gatewayResultExecutor = new GatewayResultExecutor();
|
||||
|
||||
/**
|
||||
* zuul合并结果处理
|
||||
*/
|
||||
private ResultExecutor<RequestContext, String> zuulResultExecutor = new ZuulResultExecutor();
|
||||
|
||||
/**
|
||||
* app秘钥管理
|
||||
@@ -54,7 +65,12 @@ public class ApiConfig {
|
||||
/**
|
||||
* 参数解析,gateway
|
||||
*/
|
||||
private ParamBuilder paramBuilder = new ApiParamBuilder();
|
||||
private ParamBuilder<ServerWebExchange> gatewayParamBuilder = new GatewayParamBuilder();
|
||||
|
||||
/**
|
||||
* 参数解析,zuul
|
||||
*/
|
||||
private ParamBuilder<RequestContext> zuulParamBuilder = new ZuulParamBuilder();
|
||||
|
||||
/**
|
||||
* 验证
|
||||
@@ -66,6 +82,8 @@ public class ApiConfig {
|
||||
*/
|
||||
private SessionManager sessionManager = new ApiSessionManager();
|
||||
|
||||
private BaseZuulController baseZuulController = new BaseZuulController();
|
||||
|
||||
/**
|
||||
* 错误模块
|
||||
*/
|
||||
|
@@ -2,8 +2,6 @@ package com.gitee.sop.gatewaycommon.bean;
|
||||
|
||||
import com.gitee.sop.gatewaycommon.session.SessionManager;
|
||||
|
||||
//import com.netflix.zuul.context.RequestContext;
|
||||
|
||||
/**
|
||||
* 应用上下文,方便获取信息
|
||||
*
|
||||
@@ -11,10 +9,6 @@ import com.gitee.sop.gatewaycommon.session.SessionManager;
|
||||
*/
|
||||
public class ApiContext {
|
||||
|
||||
private ApiContext() {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取session管理器
|
||||
*
|
||||
|
@@ -0,0 +1,24 @@
|
||||
package com.gitee.sop.gatewaycommon.bean;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
/**
|
||||
* @author tanghc
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
public class BaseRouteDefinition {
|
||||
/**
|
||||
* 路由的Id
|
||||
*/
|
||||
private String id;
|
||||
/**
|
||||
* 路由规则转发的目标uri
|
||||
*/
|
||||
private String uri;
|
||||
/**
|
||||
* 路由执行的顺序
|
||||
*/
|
||||
private int order = 0;
|
||||
}
|
@@ -0,0 +1,12 @@
|
||||
package com.gitee.sop.gatewaycommon.bean;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
public class BaseServiceRouteInfo<T extends BaseRouteDefinition> {
|
||||
private String appName;
|
||||
private String md5;
|
||||
private List<T> routeDefinitionList;
|
||||
}
|
@@ -8,29 +8,11 @@ import java.nio.charset.StandardCharsets;
|
||||
*/
|
||||
public class SopConstants {
|
||||
|
||||
public static final String RANDOM_KEY_NAME = "ssl_randomKey";
|
||||
public static final Charset CHARSET_UTF8 = StandardCharsets.UTF_8;
|
||||
public static final String UTF8 = "UTF-8";
|
||||
public static final String FORMAT_JSON = "json";
|
||||
public static final String FORMAT_XML = "xml";
|
||||
public static final String AUTHORIZATION = "Authorization";
|
||||
public static final String PREFIX_BEARER = "Bearer ";
|
||||
public static final String YMDHMS = "yyyy-MM-dd HH:mm:ss";
|
||||
|
||||
public static final String DEFAULT_SIGN_METHOD = "md5";
|
||||
|
||||
public static final String CONTENT_TYPE_NAME = "Content-Type";
|
||||
public static final String CONTENT_TYPE_JSON = "application/json;charset=UTF-8";
|
||||
|
||||
public static final String LINE = "\n";
|
||||
|
||||
public static final String EMPTY_JSON = "{}";
|
||||
|
||||
public static final String SORT_DESC = "DESC";
|
||||
|
||||
public static final String REST_PARAM_NAME = "_REST_PARAM_NAME_";
|
||||
public static final String REST_PARAM_VERSION = "_REST_PARAM_VERSION_";
|
||||
|
||||
/**
|
||||
* 在拦截器中调用获取参数:
|
||||
* String cachedBody = (String)exchange.getAttribute(SopConstants.CACHE_REQUEST_BODY_OBJECT_KEY);
|
||||
@@ -47,4 +29,10 @@ public class SopConstants {
|
||||
|
||||
public static final String X_BIZ_ERROR_CODE = "x-biz-error-code";
|
||||
|
||||
public static final int BIZ_ERROR_STATUS = 4000;
|
||||
|
||||
/**
|
||||
* zookeeper存放接口信息的根目录
|
||||
*/
|
||||
public static final String SOP_SERVICE_API_PATH = "/sop-service-api";
|
||||
}
|
||||
|
@@ -0,0 +1,9 @@
|
||||
package com.gitee.sop.gatewaycommon.gateway;
|
||||
|
||||
import com.gitee.sop.gatewaycommon.bean.ApiContext;
|
||||
|
||||
/**
|
||||
* @author tanghc
|
||||
*/
|
||||
public class GatewayContext extends ApiContext {
|
||||
}
|
@@ -1,4 +1,4 @@
|
||||
package com.gitee.sop.gatewaycommon.configuration;
|
||||
package com.gitee.sop.gatewaycommon.gateway.configuration;
|
||||
|
||||
import com.gitee.sop.gatewaycommon.bean.ApiContext;
|
||||
import com.gitee.sop.gatewaycommon.validate.alipay.AlipaySigner;
|
@@ -1,15 +1,15 @@
|
||||
package com.gitee.sop.gatewaycommon.configuration;
|
||||
package com.gitee.sop.gatewaycommon.gateway.configuration;
|
||||
|
||||
import com.gitee.sop.gatewaycommon.bean.ApiContext;
|
||||
import com.gitee.sop.gatewaycommon.handler.GatewayExceptionHandler;
|
||||
import com.gitee.sop.gatewaycommon.filter.GatewayModifyResponseGatewayFilter;
|
||||
import com.gitee.sop.gatewaycommon.filter.LoadBalancerClientExtFilter;
|
||||
import com.gitee.sop.gatewaycommon.filter.ValidateFilter;
|
||||
import com.gitee.sop.gatewaycommon.manager.GatewayZookeeperApiMetaManager;
|
||||
import com.gitee.sop.gatewaycommon.gateway.filter.GatewayModifyResponseGatewayFilter;
|
||||
import com.gitee.sop.gatewaycommon.gateway.filter.LoadBalancerClientExtFilter;
|
||||
import com.gitee.sop.gatewaycommon.gateway.filter.ValidateFilter;
|
||||
import com.gitee.sop.gatewaycommon.gateway.handler.GatewayExceptionHandler;
|
||||
import com.gitee.sop.gatewaycommon.gateway.route.GatewayRouteRepository;
|
||||
import com.gitee.sop.gatewaycommon.gateway.route.NameVersionRoutePredicateFactory;
|
||||
import com.gitee.sop.gatewaycommon.gateway.route.ReadBodyRoutePredicateFactory;
|
||||
import com.gitee.sop.gatewaycommon.gateway.route.GatewayZookeeperRouteManager;
|
||||
import com.gitee.sop.gatewaycommon.message.ErrorFactory;
|
||||
import com.gitee.sop.gatewaycommon.route.DynamicRouteServiceManager;
|
||||
import com.gitee.sop.gatewaycommon.route.NameVersionRoutePredicateFactory;
|
||||
import com.gitee.sop.gatewaycommon.route.ReadBodyRoutePredicateFactory;
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.web.reactive.error.ErrorWebExceptionHandler;
|
||||
@@ -25,14 +25,6 @@ import javax.annotation.PostConstruct;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
//import com.gitee.sop.gatewaycommon.filter.ErrorFilter;
|
||||
//import com.gitee.sop.gatewaycommon.filter.PostResultFilter;
|
||||
//import com.gitee.sop.gatewaycommon.filter.PreValidateFilter;
|
||||
//import com.gitee.sop.gatewaycommon.manager.DefaultApiMetaContext;
|
||||
//import com.gitee.sop.gatewaycommon.manager.SopRouteLocator;
|
||||
//import org.springframework.cloud.netflix.zuul.filters.ProxyRequestHelper;
|
||||
//import org.springframework.cloud.netflix.zuul.filters.ZuulProperties;
|
||||
//import org.springframework.cloud.netflix.zuul.filters.pre.PreDecorationFilter;
|
||||
|
||||
/**
|
||||
* @author tanghc
|
||||
@@ -43,7 +35,7 @@ public class BaseGatewayConfiguration {
|
||||
protected Environment environment;
|
||||
|
||||
@Autowired
|
||||
protected GatewayZookeeperApiMetaManager gatewayZookeeperApiMetaManager;
|
||||
protected GatewayZookeeperRouteManager gatewayZookeeperApiMetaManager;
|
||||
|
||||
/**
|
||||
* 自定义异常处理[@@]注册Bean时依赖的Bean,会从容器中直接获取,所以直接注入即可
|
||||
@@ -65,11 +57,19 @@ public class BaseGatewayConfiguration {
|
||||
return jsonExceptionHandler;
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理返回结果
|
||||
* @return
|
||||
*/
|
||||
@Bean
|
||||
GatewayModifyResponseGatewayFilter gatewayModifyResponseGatewayFilter() {
|
||||
return new GatewayModifyResponseGatewayFilter();
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取post请求参数
|
||||
* @return
|
||||
*/
|
||||
@Bean
|
||||
ReadBodyRoutePredicateFactory readBodyRoutePredicateFactory() {
|
||||
return new ReadBodyRoutePredicateFactory();
|
||||
@@ -91,13 +91,13 @@ public class BaseGatewayConfiguration {
|
||||
}
|
||||
|
||||
@Bean
|
||||
GatewayZookeeperApiMetaManager zookeeperApiMetaManager(Environment environment, DynamicRouteServiceManager dynamicRouteServiceManager) {
|
||||
return new GatewayZookeeperApiMetaManager(environment, dynamicRouteServiceManager);
|
||||
GatewayZookeeperRouteManager gatewayZookeeperRouteManager(Environment environment, GatewayRouteRepository gatewayRouteManager) {
|
||||
return new GatewayZookeeperRouteManager(environment, gatewayRouteManager);
|
||||
}
|
||||
|
||||
@Bean
|
||||
DynamicRouteServiceManager dynamicRouteServiceManager() {
|
||||
return new DynamicRouteServiceManager();
|
||||
GatewayRouteRepository gatewayRouteRepository() {
|
||||
return new GatewayRouteRepository();
|
||||
}
|
||||
|
||||
|
@@ -1,4 +1,4 @@
|
||||
package com.gitee.sop.gatewaycommon.configuration;
|
||||
package com.gitee.sop.gatewaycommon.gateway.configuration;
|
||||
|
||||
import com.gitee.sop.gatewaycommon.bean.ApiContext;
|
||||
import com.gitee.sop.gatewaycommon.param.ParamNames;
|
@@ -1,13 +1,11 @@
|
||||
package com.gitee.sop.gatewaycommon.filter;
|
||||
package com.gitee.sop.gatewaycommon.gateway.filter;
|
||||
|
||||
import com.gitee.sop.gatewaycommon.bean.ApiContext;
|
||||
import com.gitee.sop.gatewaycommon.result.ResultExecutor;
|
||||
import org.reactivestreams.Publisher;
|
||||
import org.springframework.cloud.gateway.filter.GatewayFilter;
|
||||
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
|
||||
import org.springframework.cloud.gateway.filter.GlobalFilter;
|
||||
import org.springframework.cloud.gateway.filter.NettyWriteResponseFilter;
|
||||
import org.springframework.cloud.gateway.filter.factory.rewrite.ModifyResponseBodyGatewayFilterFactory;
|
||||
import org.springframework.cloud.gateway.support.BodyInserterContext;
|
||||
import org.springframework.cloud.gateway.support.CachedBodyOutputMessage;
|
||||
import org.springframework.cloud.gateway.support.DefaultClientResponse;
|
||||
@@ -55,7 +53,7 @@ public class GatewayModifyResponseGatewayFilter implements GlobalFilter, Ordered
|
||||
Mono modifiedBody = clientResponse.bodyToMono(inClass)
|
||||
.flatMap(originalBody -> {
|
||||
// 合并微服务传递过来的结果,变成最终结果
|
||||
ResultExecutor resultExecutor = ApiContext.getApiConfig().getResultExecutor();
|
||||
ResultExecutor resultExecutor = ApiContext.getApiConfig().getGatewayResultExecutor();
|
||||
String ret = resultExecutor.mergeResult(exchange, String.valueOf(originalBody));
|
||||
return Mono.just(ret);
|
||||
});
|
@@ -1,4 +1,4 @@
|
||||
package com.gitee.sop.gatewaycommon.filter;
|
||||
package com.gitee.sop.gatewaycommon.gateway.filter;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
|
@@ -1,4 +1,4 @@
|
||||
package com.gitee.sop.gatewaycommon.filter;
|
||||
package com.gitee.sop.gatewaycommon.gateway.filter;
|
||||
|
||||
import com.gitee.sop.gatewaycommon.bean.ApiConfig;
|
||||
import com.gitee.sop.gatewaycommon.bean.ApiContext;
|
||||
@@ -23,7 +23,7 @@ public class ValidateFilter implements GlobalFilter, Ordered {
|
||||
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
|
||||
ApiConfig apiConfig = ApiContext.getApiConfig();
|
||||
// 解析参数
|
||||
ApiParam param = apiConfig.getParamBuilder().build(exchange);
|
||||
ApiParam param = apiConfig.getGatewayParamBuilder().build(exchange);
|
||||
exchange.getAttributes().put(SopConstants.CACHE_API_PARAM, param);
|
||||
// 验证操作,这里有负责验证签名参数
|
||||
Validator validator = apiConfig.getValidator();
|
@@ -1,7 +1,7 @@
|
||||
package com.gitee.sop.gatewaycommon.handler;
|
||||
package com.gitee.sop.gatewaycommon.gateway.handler;
|
||||
|
||||
import com.gitee.sop.gatewaycommon.bean.ApiContext;
|
||||
import com.gitee.sop.gatewaycommon.result.GatewayResult;
|
||||
import com.gitee.sop.gatewaycommon.gateway.result.GatewayResult;
|
||||
import com.gitee.sop.gatewaycommon.result.ResultExecutor;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@@ -23,9 +23,8 @@ import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @classDesc: 统一异常处理, 参考{@link org.springframework.web.server.AbstractErrorWebExceptionHandler}修改
|
||||
* @author: chenggang
|
||||
* @createTime: 2018/10/30
|
||||
* 统一异常处理
|
||||
* @author thc
|
||||
*/
|
||||
public class GatewayExceptionHandler implements ErrorWebExceptionHandler {
|
||||
|
||||
@@ -33,7 +32,7 @@ public class GatewayExceptionHandler implements ErrorWebExceptionHandler {
|
||||
|
||||
@Override
|
||||
public Mono<Void> handle(ServerWebExchange exchange, Throwable ex) {
|
||||
ResultExecutor resultExecutor = ApiContext.getApiConfig().getResultExecutor();
|
||||
ResultExecutor<ServerWebExchange, GatewayResult> resultExecutor = ApiContext.getApiConfig().getGatewayResultExecutor();
|
||||
GatewayResult errorResult = resultExecutor.buildErrorResult(exchange, ex);
|
||||
|
||||
/**
|
||||
@@ -106,7 +105,6 @@ public class GatewayExceptionHandler implements ErrorWebExceptionHandler {
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 参考DefaultErrorWebExceptionHandler
|
||||
*
|
@@ -1,6 +1,8 @@
|
||||
package com.gitee.sop.gatewaycommon.param;
|
||||
package com.gitee.sop.gatewaycommon.gateway.param;
|
||||
|
||||
import com.gitee.sop.gatewaycommon.bean.SopConstants;
|
||||
import com.gitee.sop.gatewaycommon.param.ApiParam;
|
||||
import com.gitee.sop.gatewaycommon.param.ParamBuilder;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
|
||||
import java.util.Map;
|
||||
@@ -8,7 +10,7 @@ import java.util.Map;
|
||||
/**
|
||||
* @author tanghc
|
||||
*/
|
||||
public class ApiParamBuilder implements ParamBuilder {
|
||||
public class GatewayParamBuilder implements ParamBuilder<ServerWebExchange> {
|
||||
@Override
|
||||
public ApiParam build(ServerWebExchange exchange) {
|
||||
Map<String, String> params = exchange.getAttribute(SopConstants.CACHE_REQUEST_BODY_FOR_MAP);
|
@@ -1,4 +1,4 @@
|
||||
package com.gitee.sop.gatewaycommon.result;
|
||||
package com.gitee.sop.gatewaycommon.gateway.result;
|
||||
|
||||
import lombok.Data;
|
||||
import org.springframework.http.HttpStatus;
|
@@ -0,0 +1,70 @@
|
||||
package com.gitee.sop.gatewaycommon.gateway.result;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.gitee.sop.gatewaycommon.bean.SopConstants;
|
||||
import com.gitee.sop.gatewaycommon.exception.ApiException;
|
||||
import com.gitee.sop.gatewaycommon.message.Error;
|
||||
import com.gitee.sop.gatewaycommon.message.ErrorEnum;
|
||||
import com.gitee.sop.gatewaycommon.result.BaseExecutorAdapter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.cloud.gateway.support.NotFoundException;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.web.server.ResponseStatusException;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
|
||||
/**
|
||||
* @author tanghc
|
||||
*/
|
||||
@Slf4j
|
||||
public class GatewayResultExecutor extends BaseExecutorAdapter<ServerWebExchange, GatewayResult> {
|
||||
|
||||
@Override
|
||||
public int getBizHeaderCode(ServerWebExchange exchange) {
|
||||
int responseStatus = HttpStatus.OK.value();
|
||||
List<String> errorCodeList = exchange.getResponse().getHeaders().get(SopConstants.X_BIZ_ERROR_CODE);
|
||||
if (!CollectionUtils.isEmpty(errorCodeList)) {
|
||||
String errorCode = errorCodeList.get(0);
|
||||
responseStatus = Integer.valueOf(errorCode);
|
||||
}
|
||||
return responseStatus;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, ?> getApiParam(ServerWebExchange exchange) {
|
||||
return exchange.getAttribute(SopConstants.CACHE_REQUEST_BODY_FOR_MAP);
|
||||
}
|
||||
|
||||
@Override
|
||||
public GatewayResult buildErrorResult(ServerWebExchange exchange, Throwable ex) {
|
||||
Error error = null;
|
||||
if (ex instanceof ApiException) {
|
||||
ApiException apiException = (ApiException) ex;
|
||||
error = apiException.getError();
|
||||
} else if (ex instanceof NotFoundException) {
|
||||
error = ErrorEnum.ISV_INVALID_METHOD.getErrorMeta().getError();
|
||||
} else if (ex instanceof ResponseStatusException) {
|
||||
ResponseStatusException responseStatusException = (ResponseStatusException) ex;
|
||||
HttpStatus status = responseStatusException.getStatus();
|
||||
if (status == HttpStatus.NOT_FOUND) {
|
||||
error = ErrorEnum.ISV_INVALID_METHOD.getErrorMeta().getError();
|
||||
}
|
||||
}
|
||||
|
||||
if (error == null) {
|
||||
error = ErrorEnum.AOP_UNKNOW_ERROR.getErrorMeta().getError();
|
||||
}
|
||||
|
||||
JSONObject jsonObject = (JSONObject) JSON.toJSON(error);
|
||||
String body = this.merge(exchange, jsonObject);
|
||||
|
||||
return new GatewayResult(HttpStatus.OK, MediaType.APPLICATION_JSON_UTF8, body);
|
||||
}
|
||||
|
||||
}
|
@@ -1,4 +1,4 @@
|
||||
package com.gitee.sop.gatewaycommon.route;
|
||||
package com.gitee.sop.gatewaycommon.gateway.route;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
@@ -1,4 +1,4 @@
|
||||
package com.gitee.sop.gatewaycommon.route;
|
||||
package com.gitee.sop.gatewaycommon.gateway.route;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@@ -10,8 +10,8 @@ import java.util.Map;
|
||||
*/
|
||||
@Data
|
||||
public class GatewayFilterDefinition {
|
||||
//Filter Name */
|
||||
/** Filter Name */
|
||||
private String name;
|
||||
//对应的路由规则 */
|
||||
/** 对应的路由规则 */
|
||||
private Map<String, String> args = new LinkedHashMap<>();
|
||||
}
|
@@ -1,4 +1,4 @@
|
||||
package com.gitee.sop.gatewaycommon.route;
|
||||
package com.gitee.sop.gatewaycommon.gateway.route;
|
||||
|
||||
import lombok.Data;
|
||||
|
@@ -1,6 +1,9 @@
|
||||
package com.gitee.sop.gatewaycommon.route;
|
||||
package com.gitee.sop.gatewaycommon.gateway.route;
|
||||
|
||||
import com.gitee.sop.gatewaycommon.bean.BaseRouteDefinition;
|
||||
import lombok.Data;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
@@ -8,16 +11,11 @@ import java.util.List;
|
||||
/**
|
||||
* @author tanghc
|
||||
*/
|
||||
@Data
|
||||
public class GatewayRouteDefinition {
|
||||
/** 路由的Id */
|
||||
private String id;
|
||||
@Getter
|
||||
@Setter
|
||||
public class GatewayRouteDefinition extends BaseRouteDefinition {
|
||||
/** 路由断言集合配置 */
|
||||
private List<GatewayPredicateDefinition> predicates = new ArrayList<>();
|
||||
/** 路由过滤器集合配置 */
|
||||
private List<GatewayFilterDefinition> filters = new ArrayList<>();
|
||||
/** 路由规则转发的目标uri */
|
||||
private String uri;
|
||||
/** 路由执行的顺序 */
|
||||
private int order = 0;
|
||||
}
|
@@ -1,5 +1,6 @@
|
||||
package com.gitee.sop.gatewaycommon.route;
|
||||
package com.gitee.sop.gatewaycommon.gateway.route;
|
||||
|
||||
import com.gitee.sop.gatewaycommon.manager.RouteRepository;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.cloud.gateway.event.RefreshRoutesEvent;
|
||||
@@ -16,7 +17,7 @@ import reactor.core.publisher.Mono;
|
||||
* @author thc
|
||||
*/
|
||||
@Slf4j
|
||||
public class DynamicRouteServiceManager implements ApplicationEventPublisherAware {
|
||||
public class GatewayRouteRepository implements ApplicationEventPublisherAware, RouteRepository<RouteDefinition> {
|
||||
|
||||
@Autowired
|
||||
private RouteDefinitionRepository routeDefinitionRepository;
|
||||
@@ -24,6 +25,7 @@ public class DynamicRouteServiceManager implements ApplicationEventPublisherAwar
|
||||
private ApplicationEventPublisher publisher;
|
||||
|
||||
/** 根据ID获取路由 */
|
||||
@Override
|
||||
public RouteDefinition get(String id) {
|
||||
return routeDefinitionRepository.getRouteDefinitions()
|
||||
.filter(routeDefinition -> {
|
||||
@@ -32,6 +34,7 @@ public class DynamicRouteServiceManager implements ApplicationEventPublisherAwar
|
||||
}
|
||||
|
||||
/** 增加路由 */
|
||||
@Override
|
||||
public String add(RouteDefinition definition) {
|
||||
routeDefinitionRepository.save(Mono.just(definition)).subscribe();
|
||||
this.publisher.publishEvent(new RefreshRoutesEvent(this));
|
||||
@@ -39,7 +42,9 @@ public class DynamicRouteServiceManager implements ApplicationEventPublisherAwar
|
||||
}
|
||||
|
||||
/** 更新路由 */
|
||||
@Override
|
||||
public String update(RouteDefinition definition) {
|
||||
log.info("更新route,id:{}", definition.getId());
|
||||
try {
|
||||
this.routeDefinitionRepository.delete(Mono.just(definition.getId()));
|
||||
} catch (Exception e) {
|
||||
@@ -55,8 +60,9 @@ public class DynamicRouteServiceManager implements ApplicationEventPublisherAwar
|
||||
}
|
||||
|
||||
/** 删除路由 */
|
||||
public Mono<ResponseEntity<Object>> delete(String id) {
|
||||
return this.routeDefinitionRepository.delete(Mono.just(id))
|
||||
@Override
|
||||
public void delete(String id) {
|
||||
this.routeDefinitionRepository.delete(Mono.just(id))
|
||||
.then(Mono.defer(() -> Mono.just(ResponseEntity.ok().build())))
|
||||
.onErrorResume(t -> t instanceof NotFoundException, t -> Mono.just(ResponseEntity.notFound().build()));
|
||||
}
|
@@ -0,0 +1,12 @@
|
||||
package com.gitee.sop.gatewaycommon.gateway.route;
|
||||
|
||||
import com.gitee.sop.gatewaycommon.bean.BaseServiceRouteInfo;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author thc
|
||||
*/
|
||||
public class GatewayServiceRouteInfo extends BaseServiceRouteInfo<GatewayRouteDefinition> {
|
||||
}
|
@@ -0,0 +1,65 @@
|
||||
package com.gitee.sop.gatewaycommon.gateway.route;
|
||||
|
||||
import com.gitee.sop.gatewaycommon.manager.BaseRouteManager;
|
||||
import com.gitee.sop.gatewaycommon.manager.RouteRepository;
|
||||
import lombok.Getter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.cloud.gateway.filter.FilterDefinition;
|
||||
import org.springframework.cloud.gateway.handler.predicate.PredicateDefinition;
|
||||
import org.springframework.cloud.gateway.route.RouteDefinition;
|
||||
import org.springframework.core.env.Environment;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 从zookeeper监听route信息
|
||||
*
|
||||
* @author tanghc
|
||||
*/
|
||||
@Getter
|
||||
@Slf4j
|
||||
public class GatewayZookeeperRouteManager extends BaseRouteManager<GatewayRouteDefinition, GatewayServiceRouteInfo, RouteDefinition> {
|
||||
|
||||
public GatewayZookeeperRouteManager(Environment environment, RouteRepository<RouteDefinition> routeRepository) {
|
||||
super(environment, routeRepository);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Class<GatewayServiceRouteInfo> getServiceRouteInfoClass() {
|
||||
return GatewayServiceRouteInfo.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Class<GatewayRouteDefinition> getRouteDefinitionClass() {
|
||||
return GatewayRouteDefinition.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected RouteDefinition buildRouteDefinition(GatewayServiceRouteInfo serviceRouteInfo,GatewayRouteDefinition gatewayRouteDefinition) {
|
||||
RouteDefinition routeDefinition = new RouteDefinition();
|
||||
routeDefinition.setId(gatewayRouteDefinition.getId());
|
||||
routeDefinition.setUri(URI.create(gatewayRouteDefinition.getUri()));
|
||||
routeDefinition.setOrder(gatewayRouteDefinition.getOrder());
|
||||
List<FilterDefinition> filterDefinitionList = new ArrayList<>(gatewayRouteDefinition.getFilters().size());
|
||||
List<PredicateDefinition> predicateDefinitionList = new ArrayList<>(gatewayRouteDefinition.getPredicates().size());
|
||||
for (GatewayFilterDefinition filter : gatewayRouteDefinition.getFilters()) {
|
||||
FilterDefinition filterDefinition = new FilterDefinition();
|
||||
BeanUtils.copyProperties(filter, filterDefinition);
|
||||
filterDefinitionList.add(filterDefinition);
|
||||
}
|
||||
|
||||
for (GatewayPredicateDefinition predicate : gatewayRouteDefinition.getPredicates()) {
|
||||
PredicateDefinition predicateDefinition = new PredicateDefinition();
|
||||
BeanUtils.copyProperties(predicate, predicateDefinition);
|
||||
predicateDefinitionList.add(predicateDefinition);
|
||||
}
|
||||
|
||||
routeDefinition.setFilters(filterDefinitionList);
|
||||
routeDefinition.setPredicates(predicateDefinitionList);
|
||||
return routeDefinition;
|
||||
}
|
||||
|
||||
}
|
@@ -1,4 +1,4 @@
|
||||
package com.gitee.sop.gatewaycommon.route;
|
||||
package com.gitee.sop.gatewaycommon.gateway.route;
|
||||
|
||||
import com.gitee.sop.gatewaycommon.bean.SopConstants;
|
||||
import com.gitee.sop.gatewaycommon.param.ParamNames;
|
@@ -1,4 +1,4 @@
|
||||
package com.gitee.sop.gatewaycommon.route;
|
||||
package com.gitee.sop.gatewaycommon.gateway.route;
|
||||
|
||||
import org.springframework.cloud.gateway.handler.predicate.ReadBodyPredicateFactory;
|
||||
|
@@ -1,20 +0,0 @@
|
||||
package com.gitee.sop.gatewaycommon.manager;
|
||||
|
||||
import org.springframework.data.redis.core.StringRedisTemplate;
|
||||
|
||||
/**
|
||||
* @author tanghc
|
||||
*/
|
||||
public class ApiMetaConfig {
|
||||
|
||||
private StringRedisTemplate redisTemplate;
|
||||
|
||||
public ApiMetaConfig(StringRedisTemplate redisTemplate) {
|
||||
this.redisTemplate = redisTemplate;
|
||||
}
|
||||
|
||||
public void loadApiMetas() {
|
||||
|
||||
}
|
||||
|
||||
}
|
@@ -1,22 +0,0 @@
|
||||
package com.gitee.sop.gatewaycommon.manager;
|
||||
|
||||
/**
|
||||
* 管理各服务接口信息
|
||||
* @author tanghc
|
||||
*/
|
||||
public interface ApiMetaManager {
|
||||
|
||||
String API_STORE_KEY = "com.gitee.sop.api";
|
||||
|
||||
/**
|
||||
* 刷新素有的微服务接口信息
|
||||
*/
|
||||
void refresh();
|
||||
|
||||
/**
|
||||
* 某个服务接口更改时触发
|
||||
* @param serviceApiInfoJson 接口信息
|
||||
*/
|
||||
void onChange(String serviceApiInfoJson);
|
||||
|
||||
}
|
@@ -0,0 +1,135 @@
|
||||
package com.gitee.sop.gatewaycommon.manager;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.gitee.sop.gatewaycommon.bean.BaseRouteDefinition;
|
||||
import com.gitee.sop.gatewaycommon.bean.BaseServiceRouteInfo;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.curator.framework.CuratorFramework;
|
||||
import org.apache.curator.framework.CuratorFrameworkFactory;
|
||||
import org.apache.curator.framework.recipes.cache.ChildData;
|
||||
import org.apache.curator.framework.recipes.cache.PathChildrenCache;
|
||||
import org.apache.curator.framework.recipes.cache.PathChildrenCacheEvent;
|
||||
import org.apache.curator.framework.recipes.cache.PathChildrenCacheListener;
|
||||
import org.apache.curator.retry.ExponentialBackoffRetry;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static com.gitee.sop.gatewaycommon.bean.SopConstants.SOP_SERVICE_API_PATH;
|
||||
|
||||
/**
|
||||
* @author tanghc
|
||||
*/
|
||||
@Slf4j
|
||||
public abstract class BaseRouteManager<E extends BaseRouteDefinition,R extends BaseServiceRouteInfo<E>, T> implements RouteManager {
|
||||
|
||||
protected String sopServiceApiPath = SOP_SERVICE_API_PATH;
|
||||
|
||||
protected Environment environment;
|
||||
|
||||
protected RouteRepository<T> routeRepository;
|
||||
|
||||
protected abstract Class<R> getServiceRouteInfoClass();
|
||||
|
||||
protected abstract Class<E> getRouteDefinitionClass();
|
||||
|
||||
protected abstract T buildRouteDefinition(R serviceRouteInfo, E routeDefinition);
|
||||
|
||||
public BaseRouteManager(Environment environment, RouteRepository<T> routeRepository) {
|
||||
this.environment = environment;
|
||||
this.routeRepository = routeRepository;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void refresh() {
|
||||
log.info("刷新本地接口信息");
|
||||
try {
|
||||
String zookeeperServerAddr = environment.getProperty("spring.cloud.zookeeper.connect-string");
|
||||
if (StringUtils.isEmpty(zookeeperServerAddr)) {
|
||||
throw new RuntimeException("未指定spring.cloud.zookeeper.connect-string参数");
|
||||
}
|
||||
CuratorFramework client = CuratorFrameworkFactory.builder()
|
||||
.connectString(zookeeperServerAddr)
|
||||
.retryPolicy(new ExponentialBackoffRetry(1000, 3))
|
||||
.build();
|
||||
|
||||
client.start();
|
||||
|
||||
client.create()
|
||||
// 如果节点存在则Curator将会使用给出的数据设置这个节点的值
|
||||
.orSetData()
|
||||
// 如果指定节点的父节点不存在,则Curator将会自动级联创建父节点
|
||||
.creatingParentContainersIfNeeded()
|
||||
.forPath(sopServiceApiPath, "".getBytes());
|
||||
|
||||
this.watchChildren(client, sopServiceApiPath);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
protected void watchChildren(CuratorFramework client, String sopServiceApiPath) throws Exception {
|
||||
// 为子节点添加watcher
|
||||
// PathChildrenCache: 监听数据节点的增删改,可以设置触发的事件
|
||||
final PathChildrenCache childrenCache = new PathChildrenCache(client, sopServiceApiPath, true);
|
||||
|
||||
/**
|
||||
* StartMode: 初始化方式
|
||||
* POST_INITIALIZED_EVENT:异步初始化,初始化之后会触发事件
|
||||
* NORMAL:异步初始化
|
||||
* BUILD_INITIAL_CACHE:同步初始化
|
||||
*/
|
||||
childrenCache.start(PathChildrenCache.StartMode.BUILD_INITIAL_CACHE);
|
||||
|
||||
// 列出子节点数据列表,需要使用BUILD_INITIAL_CACHE同步初始化模式才能获得,异步是获取不到的
|
||||
List<ChildData> childDataList = childrenCache.getCurrentData();
|
||||
log.info("微服务API详细数据列表:");
|
||||
for (ChildData childData : childDataList) {
|
||||
String nodeData = new String(childData.getData());
|
||||
log.info("\t* 子节点路径:" + childData.getPath() + ",该节点的数据为:" + nodeData);
|
||||
R serviceRouteInfo = JSON.parseObject(nodeData, getServiceRouteInfoClass());
|
||||
for (E routeDefinitionItem : serviceRouteInfo.getRouteDefinitionList()) {
|
||||
T routeDefinition = buildRouteDefinition(serviceRouteInfo, routeDefinitionItem);
|
||||
routeRepository.add(routeDefinition);
|
||||
}
|
||||
}
|
||||
// 添加事件监听器
|
||||
childrenCache.getListenable().addListener(new PathChildrenCacheListener() {
|
||||
@Override
|
||||
public void childEvent(CuratorFramework curatorFramework, PathChildrenCacheEvent event) throws Exception {
|
||||
PathChildrenCacheEvent.Type type = event.getType();
|
||||
// 通过判断event type的方式来实现不同事件的触发
|
||||
if (PathChildrenCacheEvent.Type.CHILD_ADDED.equals(type)) {
|
||||
String nodeData = new String(event.getData().getData());
|
||||
R serviceRouteInfo = JSON.parseObject(nodeData, getServiceRouteInfoClass());
|
||||
// 添加子节点时触发
|
||||
log.info("子节点:{}添加,数据为:{}", event.getData().getPath(), nodeData);
|
||||
for (E routeDefinitionItem : serviceRouteInfo.getRouteDefinitionList()) {
|
||||
T routeDefinition = buildRouteDefinition(serviceRouteInfo, routeDefinitionItem);
|
||||
routeRepository.add(routeDefinition);
|
||||
}
|
||||
} else if (PathChildrenCacheEvent.Type.CHILD_UPDATED.equals(type)) {
|
||||
String nodeData = new String(event.getData().getData());
|
||||
R serviceRouteInfo = JSON.parseObject(nodeData, getServiceRouteInfoClass());
|
||||
// 修改子节点数据时触发
|
||||
log.info("子节点:{}修改,数据为:{}", event.getData().getPath(), nodeData);
|
||||
for (E routeDefinitionItem : serviceRouteInfo.getRouteDefinitionList()) {
|
||||
T routeDefinition = buildRouteDefinition(serviceRouteInfo, routeDefinitionItem);
|
||||
routeRepository.update(routeDefinition);
|
||||
}
|
||||
} else if (PathChildrenCacheEvent.Type.CHILD_REMOVED.equals(type)) {
|
||||
String nodeData = new String(event.getData().getData());
|
||||
R serviceRouteInfo = JSON.parseObject(nodeData, getServiceRouteInfoClass());
|
||||
// 删除子节点时触发
|
||||
log.info("子节点:{}删除,数据为:{}", event.getData().getPath(), nodeData);
|
||||
for (E routeDefinitionItem : serviceRouteInfo.getRouteDefinitionList()) {
|
||||
routeRepository.delete(routeDefinitionItem.getId());
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
}
|
@@ -1,168 +0,0 @@
|
||||
package com.gitee.sop.gatewaycommon.manager;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.gitee.sop.gatewaycommon.route.DynamicRouteServiceManager;
|
||||
import com.gitee.sop.gatewaycommon.route.GatewayFilterDefinition;
|
||||
import com.gitee.sop.gatewaycommon.route.GatewayPredicateDefinition;
|
||||
import com.gitee.sop.gatewaycommon.route.GatewayRouteDefinition;
|
||||
import com.gitee.sop.gatewaycommon.route.ServiceRouteInfo;
|
||||
import lombok.Getter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.curator.framework.CuratorFramework;
|
||||
import org.apache.curator.framework.CuratorFrameworkFactory;
|
||||
import org.apache.curator.framework.recipes.cache.ChildData;
|
||||
import org.apache.curator.framework.recipes.cache.PathChildrenCache;
|
||||
import org.apache.curator.framework.recipes.cache.PathChildrenCacheEvent;
|
||||
import org.apache.curator.framework.recipes.cache.PathChildrenCacheListener;
|
||||
import org.apache.curator.retry.ExponentialBackoffRetry;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.cloud.gateway.filter.FilterDefinition;
|
||||
import org.springframework.cloud.gateway.handler.predicate.PredicateDefinition;
|
||||
import org.springframework.cloud.gateway.route.RouteDefinition;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 从zookeeper监听route信息
|
||||
*
|
||||
* @author tanghc
|
||||
*/
|
||||
@Getter
|
||||
@Slf4j
|
||||
public class GatewayZookeeperApiMetaManager implements ApiMetaManager {
|
||||
|
||||
private String sopServiceApiPath = "/sop-service-api";
|
||||
|
||||
private Environment environment;
|
||||
|
||||
private DynamicRouteServiceManager dynamicRouteServiceManager;
|
||||
|
||||
|
||||
public GatewayZookeeperApiMetaManager(Environment environment, DynamicRouteServiceManager dynamicRouteServiceManager) {
|
||||
this.environment = environment;
|
||||
this.dynamicRouteServiceManager = dynamicRouteServiceManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void refresh() {
|
||||
log.info("刷新本地接口信息");
|
||||
try {
|
||||
String zookeeperServerAddr = environment.getProperty("spring.cloud.zookeeper.connect-string");
|
||||
if (StringUtils.isEmpty(zookeeperServerAddr)) {
|
||||
throw new RuntimeException("未指定spring.cloud.zookeeper.connect-string参数");
|
||||
}
|
||||
CuratorFramework client = CuratorFrameworkFactory.builder()
|
||||
.connectString(zookeeperServerAddr)
|
||||
.retryPolicy(new ExponentialBackoffRetry(1000, 3))
|
||||
.build();
|
||||
|
||||
client.start();
|
||||
|
||||
client.create()
|
||||
// 如果节点存在则Curator将会使用给出的数据设置这个节点的值
|
||||
.orSetData()
|
||||
// 如果指定节点的父节点不存在,则Curator将会自动级联创建父节点
|
||||
.creatingParentContainersIfNeeded()
|
||||
.forPath(sopServiceApiPath, "".getBytes());
|
||||
|
||||
this.watchChildren(client, sopServiceApiPath);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
protected void watchChildren(CuratorFramework client, String sopServiceApiPath) throws Exception {
|
||||
// 为子节点添加watcher
|
||||
// PathChildrenCache: 监听数据节点的增删改,可以设置触发的事件
|
||||
final PathChildrenCache childrenCache = new PathChildrenCache(client, sopServiceApiPath, true);
|
||||
|
||||
/**
|
||||
* StartMode: 初始化方式
|
||||
* POST_INITIALIZED_EVENT:异步初始化,初始化之后会触发事件
|
||||
* NORMAL:异步初始化
|
||||
* BUILD_INITIAL_CACHE:同步初始化
|
||||
*/
|
||||
childrenCache.start(PathChildrenCache.StartMode.BUILD_INITIAL_CACHE);
|
||||
|
||||
// 列出子节点数据列表,需要使用BUILD_INITIAL_CACHE同步初始化模式才能获得,异步是获取不到的
|
||||
List<ChildData> childDataList = childrenCache.getCurrentData();
|
||||
log.info("微服务API详细数据列表:");
|
||||
for (ChildData childData : childDataList) {
|
||||
String nodeData = new String(childData.getData());
|
||||
log.info("\t* 子节点路径:" + childData.getPath() + ",该节点的数据为:" + nodeData);
|
||||
ServiceRouteInfo serviceRouteInfo = JSON.parseObject(nodeData, ServiceRouteInfo.class);
|
||||
for (GatewayRouteDefinition gatewayRouteDefinition : serviceRouteInfo.getRouteDefinitionList()) {
|
||||
RouteDefinition routeDefinition = buildRouteDefinition(gatewayRouteDefinition);
|
||||
dynamicRouteServiceManager.add(routeDefinition);
|
||||
}
|
||||
}
|
||||
// 添加事件监听器
|
||||
childrenCache.getListenable().addListener(new PathChildrenCacheListener() {
|
||||
@Override
|
||||
public void childEvent(CuratorFramework curatorFramework, PathChildrenCacheEvent event) throws Exception {
|
||||
PathChildrenCacheEvent.Type type = event.getType();
|
||||
// 通过判断event type的方式来实现不同事件的触发
|
||||
if (PathChildrenCacheEvent.Type.CHILD_ADDED.equals(type)) {
|
||||
String nodeData = new String(event.getData().getData());
|
||||
ServiceRouteInfo serviceRouteInfo = JSON.parseObject(nodeData, ServiceRouteInfo.class);
|
||||
// 添加子节点时触发
|
||||
log.info("子节点:{}添加成功,数据为:{}", event.getData().getPath(), nodeData);
|
||||
for (GatewayRouteDefinition gatewayRouteDefinition : serviceRouteInfo.getRouteDefinitionList()) {
|
||||
RouteDefinition routeDefinition = buildRouteDefinition(gatewayRouteDefinition);
|
||||
dynamicRouteServiceManager.add(routeDefinition);
|
||||
}
|
||||
} else if (PathChildrenCacheEvent.Type.CHILD_UPDATED.equals(type)) {
|
||||
String nodeData = new String(event.getData().getData());
|
||||
ServiceRouteInfo serviceRouteInfo = JSON.parseObject(nodeData, ServiceRouteInfo.class);
|
||||
// 修改子节点数据时触发
|
||||
log.info("子节点:{}修改成功,数据为:{}", event.getData().getPath(), nodeData);
|
||||
for (GatewayRouteDefinition gatewayRouteDefinition : serviceRouteInfo.getRouteDefinitionList()) {
|
||||
RouteDefinition routeDefinition = buildRouteDefinition(gatewayRouteDefinition);
|
||||
dynamicRouteServiceManager.update(routeDefinition);
|
||||
}
|
||||
} else if (PathChildrenCacheEvent.Type.CHILD_REMOVED.equals(type)) {
|
||||
String nodeData = new String(event.getData().getData());
|
||||
ServiceRouteInfo serviceRouteInfo = JSON.parseObject(nodeData, ServiceRouteInfo.class);
|
||||
// 删除子节点时触发
|
||||
log.info("子节点:{}删除成功,数据为:{}", event.getData().getPath(), nodeData);
|
||||
for (GatewayRouteDefinition gatewayRouteDefinition : serviceRouteInfo.getRouteDefinitionList()) {
|
||||
dynamicRouteServiceManager.delete(gatewayRouteDefinition.getId());
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
protected RouteDefinition buildRouteDefinition(GatewayRouteDefinition gatewayRouteDefinition) {
|
||||
RouteDefinition routeDefinition = new RouteDefinition();
|
||||
routeDefinition.setId(gatewayRouteDefinition.getId());
|
||||
routeDefinition.setUri(URI.create(gatewayRouteDefinition.getUri()));
|
||||
routeDefinition.setOrder(gatewayRouteDefinition.getOrder());
|
||||
List<FilterDefinition> filterDefinitionList = new ArrayList<>(gatewayRouteDefinition.getFilters().size());
|
||||
List<PredicateDefinition> predicateDefinitionList = new ArrayList<>(gatewayRouteDefinition.getPredicates().size());
|
||||
for (GatewayFilterDefinition filter : gatewayRouteDefinition.getFilters()) {
|
||||
FilterDefinition filterDefinition = new FilterDefinition();
|
||||
BeanUtils.copyProperties(filter, filterDefinition);
|
||||
filterDefinitionList.add(filterDefinition);
|
||||
}
|
||||
|
||||
for (GatewayPredicateDefinition predicate : gatewayRouteDefinition.getPredicates()) {
|
||||
PredicateDefinition predicateDefinition = new PredicateDefinition();
|
||||
BeanUtils.copyProperties(predicate, predicateDefinition);
|
||||
predicateDefinitionList.add(predicateDefinition);
|
||||
}
|
||||
|
||||
routeDefinition.setFilters(filterDefinitionList);
|
||||
routeDefinition.setPredicates(predicateDefinitionList);
|
||||
return routeDefinition;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onChange(String serviceApiInfoJson) {
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,16 @@
|
||||
package com.gitee.sop.gatewaycommon.manager;
|
||||
|
||||
/**
|
||||
* 管理各服务路由信息
|
||||
* @author tanghc
|
||||
*/
|
||||
public interface RouteManager {
|
||||
|
||||
String API_STORE_KEY = "com.gitee.sop.api";
|
||||
|
||||
/**
|
||||
* 刷新素有的微服务接口信息
|
||||
*/
|
||||
void refresh();
|
||||
|
||||
}
|
@@ -0,0 +1,14 @@
|
||||
package com.gitee.sop.gatewaycommon.manager;
|
||||
|
||||
/**
|
||||
* @author tanghc
|
||||
*/
|
||||
public interface RouteRepository<T> {
|
||||
T get(String id);
|
||||
|
||||
String add(T route);
|
||||
|
||||
String update(T route);
|
||||
|
||||
void delete(String id);
|
||||
}
|
@@ -1,16 +1,14 @@
|
||||
package com.gitee.sop.gatewaycommon.param;
|
||||
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
|
||||
/**
|
||||
* @author tanghc
|
||||
*/
|
||||
public interface ParamBuilder {
|
||||
public interface ParamBuilder<T> {
|
||||
/**
|
||||
* 从request提取参数
|
||||
* @param exchange
|
||||
* @param request
|
||||
* @return 返回ApiParam
|
||||
* @throws Exception
|
||||
*/
|
||||
ApiParam build(ServerWebExchange exchange);
|
||||
ApiParam build(T request);
|
||||
}
|
||||
|
@@ -1,146 +0,0 @@
|
||||
package com.gitee.sop.gatewaycommon.result;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.gitee.sop.gatewaycommon.bean.SopConstants;
|
||||
import com.gitee.sop.gatewaycommon.exception.ApiException;
|
||||
import com.gitee.sop.gatewaycommon.message.Error;
|
||||
import com.gitee.sop.gatewaycommon.message.ErrorEnum;
|
||||
import com.gitee.sop.gatewaycommon.message.ErrorMeta;
|
||||
import com.gitee.sop.gatewaycommon.param.ParamNames;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.cloud.gateway.support.NotFoundException;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.web.server.ResponseStatusException;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
|
||||
/**
|
||||
* @author tanghc
|
||||
*/
|
||||
@Slf4j
|
||||
public class ApiResultExecutor implements ResultExecutor {
|
||||
|
||||
private static final ErrorMeta SUCCESS_META = ErrorEnum.SUCCESS.getErrorMeta();
|
||||
private static final ErrorMeta ISP_UNKNOW_ERROR_META = ErrorEnum.ISP_UNKNOW_ERROR.getErrorMeta();
|
||||
private static final ErrorMeta ISP_BIZ_ERROR = ErrorEnum.BIZ_ERROR.getErrorMeta();
|
||||
|
||||
|
||||
public static final int BIZ_ERROR_STATUS = 4000;
|
||||
private static final char DOT = '.';
|
||||
private static final char UNDERLINE = '_';
|
||||
public static final String GATEWAY_CODE_NAME = "code";
|
||||
public static final String GATEWAY_MSG_NAME = "msg";
|
||||
public static final String DATA_SUFFIX = "_response";
|
||||
|
||||
@Override
|
||||
public String mergeResult(ServerWebExchange exchange, String responseData) {
|
||||
int responseStatus = HttpStatus.OK.value();
|
||||
List<String> errorCodeList = exchange.getResponse().getHeaders().get(SopConstants.X_BIZ_ERROR_CODE);
|
||||
if (!CollectionUtils.isEmpty(errorCodeList)) {
|
||||
String errorCode = errorCodeList.get(0);
|
||||
responseStatus = Integer.valueOf(errorCode);
|
||||
}
|
||||
if (responseStatus == HttpStatus.OK.value() || responseStatus == BIZ_ERROR_STATUS) {
|
||||
return mergeSuccess(exchange, responseStatus, responseData);
|
||||
} else {
|
||||
// 微服务端有可能返回500错误
|
||||
// {"path":"/book/getBook3","error":"Internal Server Error","message":"id不能为空","timestamp":"2019-02-13T07:41:00.495+0000","status":500}
|
||||
return mergeError(exchange, responseData);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public GatewayResult buildErrorResult(ServerWebExchange exchange, Throwable ex) {
|
||||
Error error = null;
|
||||
if (ex instanceof ApiException) {
|
||||
ApiException apiException = (ApiException) ex;
|
||||
error = apiException.getError();
|
||||
} else if (ex instanceof NotFoundException) {
|
||||
error = ErrorEnum.ISV_INVALID_METHOD.getErrorMeta().getError();
|
||||
} else if (ex instanceof ResponseStatusException) {
|
||||
ResponseStatusException responseStatusException = (ResponseStatusException) ex;
|
||||
HttpStatus status = responseStatusException.getStatus();
|
||||
if (status == HttpStatus.NOT_FOUND) {
|
||||
error = ErrorEnum.ISV_INVALID_METHOD.getErrorMeta().getError();
|
||||
}
|
||||
}
|
||||
|
||||
if (error == null) {
|
||||
error = ErrorEnum.AOP_UNKNOW_ERROR.getErrorMeta().getError();
|
||||
}
|
||||
|
||||
JSONObject jsonObject = (JSONObject) JSON.toJSON(error);
|
||||
String body = this.merge(exchange, jsonObject);
|
||||
|
||||
return new GatewayResult(HttpStatus.OK, MediaType.APPLICATION_JSON_UTF8, body);
|
||||
}
|
||||
|
||||
/*
|
||||
成功示例
|
||||
{
|
||||
"alipay_trade_fastpay_refund_query_response": {
|
||||
"code": "10000",
|
||||
"msg": "Success",
|
||||
"trade_no": "2014112611001004680073956707",
|
||||
"out_trade_no": "20150320010101001",
|
||||
"out_request_no": "20150320010101001",
|
||||
"refund_reason": "用户退款请求",
|
||||
"total_amount": 100.2,
|
||||
"refund_amount": 12.33
|
||||
},
|
||||
"sign": "ERITJKEIJKJHKKKKKKKHJEREEEEEEEEEEE"
|
||||
}
|
||||
*/
|
||||
public String mergeSuccess(ServerWebExchange exchange, int responseStatus, String serviceResult) {
|
||||
JSONObject jsonObjectService;
|
||||
// 如果是业务出错
|
||||
if (responseStatus == BIZ_ERROR_STATUS) {
|
||||
jsonObjectService = JSON.parseObject(serviceResult);
|
||||
jsonObjectService.put(GATEWAY_CODE_NAME, ISP_BIZ_ERROR.getCode());
|
||||
jsonObjectService.put(GATEWAY_MSG_NAME, ISP_BIZ_ERROR.getError().getMsg());
|
||||
} else {
|
||||
// 200正常返回
|
||||
jsonObjectService = JSON.parseObject(serviceResult);
|
||||
jsonObjectService.put(GATEWAY_CODE_NAME, SUCCESS_META.getCode());
|
||||
jsonObjectService.put(GATEWAY_MSG_NAME, SUCCESS_META.getError().getMsg());
|
||||
}
|
||||
return this.merge(exchange, jsonObjectService);
|
||||
}
|
||||
|
||||
/*
|
||||
异常示例
|
||||
{
|
||||
"alipay_trade_fastpay_refund_query_response": {
|
||||
"code": "20000",
|
||||
"msg": "Service Currently Unavailable",
|
||||
"sub_code": "isp.unknow-error",
|
||||
"sub_msg": "系统繁忙"
|
||||
},
|
||||
"sign": "ERITJKEIJKJHKKKKKKKHJEREEEEEEEEEEE"
|
||||
}
|
||||
*/
|
||||
public String mergeError(ServerWebExchange exchange, String serviceResult) {
|
||||
JSONObject jsonObjectService = new JSONObject();
|
||||
jsonObjectService.put(GATEWAY_CODE_NAME, ISP_UNKNOW_ERROR_META.getCode());
|
||||
jsonObjectService.put(GATEWAY_MSG_NAME, ISP_UNKNOW_ERROR_META.getError().getMsg());
|
||||
return this.merge(exchange, jsonObjectService);
|
||||
}
|
||||
|
||||
private String merge(ServerWebExchange exchange, JSONObject jsonObjectService) {
|
||||
JSONObject ret = new JSONObject();
|
||||
// 点换成下划线
|
||||
Map<String, String> params = exchange.getAttribute(SopConstants.CACHE_REQUEST_BODY_FOR_MAP);
|
||||
String apiName = params.getOrDefault(ParamNames.API_NAME, "error").replace(DOT, UNDERLINE);
|
||||
ret.put(apiName + DATA_SUFFIX, jsonObjectService);
|
||||
ret.put(ParamNames.SIGN_NAME, params.getOrDefault(ParamNames.SIGN_NAME, ""));
|
||||
return ret.toJSONString();
|
||||
}
|
||||
|
||||
|
||||
}
|
@@ -0,0 +1,82 @@
|
||||
package com.gitee.sop.gatewaycommon.result;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.gitee.sop.gatewaycommon.bean.SopConstants;
|
||||
import com.gitee.sop.gatewaycommon.message.ErrorEnum;
|
||||
import com.gitee.sop.gatewaycommon.message.ErrorMeta;
|
||||
import com.gitee.sop.gatewaycommon.param.ParamNames;
|
||||
import org.springframework.http.HttpStatus;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author tanghc
|
||||
*/
|
||||
public abstract class BaseExecutorAdapter<T, R> implements ResultExecutor<T, R> {
|
||||
private static final ErrorMeta SUCCESS_META = ErrorEnum.SUCCESS.getErrorMeta();
|
||||
private static final ErrorMeta ISP_UNKNOW_ERROR_META = ErrorEnum.ISP_UNKNOW_ERROR.getErrorMeta();
|
||||
private static final ErrorMeta ISP_BIZ_ERROR = ErrorEnum.BIZ_ERROR.getErrorMeta();
|
||||
|
||||
private static final char DOT = '.';
|
||||
private static final char UNDERLINE = '_';
|
||||
public static final String GATEWAY_CODE_NAME = "code";
|
||||
public static final String GATEWAY_MSG_NAME = "msg";
|
||||
public static final String DATA_SUFFIX = "_response";
|
||||
|
||||
/**
|
||||
* 获取业务方约定的返回码
|
||||
* @param t
|
||||
* @return
|
||||
*/
|
||||
public abstract int getBizHeaderCode(T t);
|
||||
|
||||
/**
|
||||
* 返回Api参数
|
||||
* @param t
|
||||
* @return
|
||||
*/
|
||||
public abstract Map<String, ?> getApiParam(T t);
|
||||
|
||||
@Override
|
||||
public String mergeResult(T request, String serviceResult) {
|
||||
int responseStatus = this.getBizHeaderCode(request);
|
||||
JSONObject jsonObjectService;
|
||||
if (responseStatus == HttpStatus.OK.value()) {
|
||||
// 200正常返回
|
||||
jsonObjectService = JSON.parseObject(serviceResult);
|
||||
jsonObjectService.put(GATEWAY_CODE_NAME, SUCCESS_META.getCode());
|
||||
jsonObjectService.put(GATEWAY_MSG_NAME, SUCCESS_META.getError().getMsg());
|
||||
} else if (responseStatus == SopConstants.BIZ_ERROR_STATUS) {
|
||||
// 如果是业务出错
|
||||
jsonObjectService = JSON.parseObject(serviceResult);
|
||||
jsonObjectService.put(GATEWAY_CODE_NAME, ISP_BIZ_ERROR.getCode());
|
||||
jsonObjectService.put(GATEWAY_MSG_NAME, ISP_BIZ_ERROR.getError().getMsg());
|
||||
} else {
|
||||
// 微服务端有可能返回500错误
|
||||
// {"path":"/book/getBook3","error":"Internal Server Error","message":"id不能为空","timestamp":"2019-02-13T07:41:00.495+0000","status":500}
|
||||
jsonObjectService = new JSONObject();
|
||||
jsonObjectService.put(GATEWAY_CODE_NAME, ISP_UNKNOW_ERROR_META.getCode());
|
||||
jsonObjectService.put(GATEWAY_MSG_NAME, ISP_UNKNOW_ERROR_META.getError().getMsg());
|
||||
}
|
||||
return this.merge(request, jsonObjectService);
|
||||
}
|
||||
|
||||
public String merge(T exchange, JSONObject jsonObjectService) {
|
||||
JSONObject ret = new JSONObject();
|
||||
// 点换成下划线
|
||||
Map<String, ?> params = this.getApiParam(exchange);
|
||||
Object name = params.get(ParamNames.API_NAME);
|
||||
if (name == null) {
|
||||
name = "error";
|
||||
}
|
||||
Object sign = params.get(ParamNames.SIGN_NAME);
|
||||
if (sign == null) {
|
||||
sign = "";
|
||||
}
|
||||
String method = String.valueOf(name).replace(DOT, UNDERLINE);
|
||||
ret.put(method + DATA_SUFFIX, jsonObjectService);
|
||||
ret.put(ParamNames.SIGN_NAME, sign);
|
||||
return ret.toJSONString();
|
||||
}
|
||||
}
|
@@ -1,25 +1,51 @@
|
||||
package com.gitee.sop.gatewaycommon.result;
|
||||
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
import com.gitee.sop.gatewaycommon.gateway.result.GatewayResult;
|
||||
|
||||
/**
|
||||
* 对返回结果进行处理
|
||||
* 成功示例
|
||||
* {
|
||||
* "alipay_trade_fastpay_refund_query_response": {
|
||||
* "code": "10000",
|
||||
* "msg": "Success",
|
||||
* "trade_no": "2014112611001004680073956707",
|
||||
* "out_trade_no": "20150320010101001",
|
||||
* "out_request_no": "20150320010101001",
|
||||
* "refund_reason": "用户退款请求",
|
||||
* "total_amount": 100.2,
|
||||
* "refund_amount": 12.33
|
||||
* },
|
||||
* "sign": "ERITJKEIJKJHKKKKKKKHJEREEEEEEEEEEE"
|
||||
* }
|
||||
* <p>
|
||||
* 异常示例
|
||||
* {
|
||||
* "alipay_trade_fastpay_refund_query_response": {
|
||||
* "code": "20000",
|
||||
* "msg": "Service Currently Unavailable",
|
||||
* "sub_code": "isp.unknow-error",
|
||||
* "sub_msg": "系统繁忙"
|
||||
* },
|
||||
* "sign": "ERITJKEIJKJHKKKKKKKHJEREEEEEEEEEEE"
|
||||
* }
|
||||
*
|
||||
* @author tanghc
|
||||
*/
|
||||
public interface ResultExecutor {
|
||||
public interface ResultExecutor<T, R> {
|
||||
/**
|
||||
* 合并结果
|
||||
* @param exchange
|
||||
* @param responseData
|
||||
* @param request
|
||||
* @param serviceResult
|
||||
* @return
|
||||
*/
|
||||
String mergeResult(ServerWebExchange exchange, String responseData);
|
||||
String mergeResult(T request, String serviceResult);
|
||||
|
||||
/**
|
||||
* 合并错误结果
|
||||
* @param exchange
|
||||
* @param request
|
||||
* @param ex
|
||||
* @return
|
||||
*/
|
||||
GatewayResult buildErrorResult(ServerWebExchange exchange, Throwable ex);
|
||||
R buildErrorResult(T request, Throwable ex);
|
||||
}
|
||||
|
@@ -1,12 +0,0 @@
|
||||
package com.gitee.sop.gatewaycommon.route;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
public class ServiceRouteInfo {
|
||||
private String appName;
|
||||
private String md5;
|
||||
private List<GatewayRouteDefinition> routeDefinitionList;
|
||||
}
|
@@ -1,12 +1,15 @@
|
||||
package com.gitee.sop.gatewaycommon.util;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URLDecoder;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
|
||||
/**
|
||||
@@ -43,4 +46,38 @@ public class RequestUtil {
|
||||
return params;
|
||||
}
|
||||
|
||||
/**
|
||||
* request中的参数转换成map
|
||||
*
|
||||
* @param request request对象
|
||||
* @return 返回参数键值对
|
||||
*/
|
||||
public static Map<String, Object> convertRequestParamsToMap(HttpServletRequest request) {
|
||||
Map<String, String[]> paramMap = request.getParameterMap();
|
||||
if(paramMap == null || paramMap.isEmpty()) {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
Map<String, Object> retMap = new HashMap<String, Object>(paramMap.size());
|
||||
|
||||
Set<Map.Entry<String, String[]>> entrySet = paramMap.entrySet();
|
||||
|
||||
for (Map.Entry<String, String[]> entry : entrySet) {
|
||||
String name = entry.getKey();
|
||||
String[] values = entry.getValue();
|
||||
if (values.length == 1) {
|
||||
retMap.put(name, values[0]);
|
||||
} else if (values.length > 1) {
|
||||
retMap.put(name, values);
|
||||
} else {
|
||||
retMap.put(name, "");
|
||||
}
|
||||
}
|
||||
|
||||
return retMap;
|
||||
}
|
||||
|
||||
public static String getText(HttpServletRequest request) throws Exception {
|
||||
return IOUtils.toString(request.getInputStream(), UTF8);
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -0,0 +1,167 @@
|
||||
package com.gitee.sop.gatewaycommon.zuul;
|
||||
|
||||
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.param.UploadContext;
|
||||
import com.netflix.zuul.context.RequestContext;
|
||||
import com.gitee.sop.gatewaycommon.param.ApiParam;
|
||||
import com.gitee.sop.gatewaycommon.session.SessionManager;
|
||||
|
||||
import javax.servlet.ServletContext;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.servlet.http.HttpSession;
|
||||
import java.util.Locale;
|
||||
|
||||
|
||||
/**
|
||||
* @author tanghc
|
||||
*/
|
||||
public class ZuulContext extends ApiContext {
|
||||
|
||||
private static final String ATTR_PARAM = "zuul.common.api.param";
|
||||
private static final String ATTR_UPLOAD_CONTEXT = "zuul.common.api.upload_context";
|
||||
|
||||
private static void setAttr(String name, Object val) {
|
||||
HttpServletRequest request = getRequest();
|
||||
if (request != null) {
|
||||
request.setAttribute(name, val);
|
||||
}
|
||||
}
|
||||
|
||||
private static Object getAttr(String name) {
|
||||
HttpServletRequest request = getRequest();
|
||||
if (request == null) {
|
||||
return null;
|
||||
}
|
||||
return request.getAttribute(name);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 获取HttpServletRequest
|
||||
*
|
||||
* @return HttpServletRequest
|
||||
*/
|
||||
public static HttpServletRequest getRequest() {
|
||||
return RequestContext.getCurrentContext().getRequest();
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回默认的HttpServletRequest.getSession();
|
||||
*
|
||||
* @return 没有返回null
|
||||
*/
|
||||
public static HttpSession getSession() {
|
||||
HttpServletRequest req = getRequest();
|
||||
if (req == null) {
|
||||
return null;
|
||||
} else {
|
||||
return req.getSession();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 同getSessionId()
|
||||
* @return 返回accessToken,没有返回null
|
||||
*/
|
||||
public static String getAccessToken() {
|
||||
return getSessionId();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 返回自定义的session,被SessionManager管理
|
||||
*
|
||||
* @return 如果sessionId为null,则返回null
|
||||
*/
|
||||
public static HttpSession getManagedSession() {
|
||||
String sessionId = getSessionId();
|
||||
if (sessionId != null) {
|
||||
return getSessionManager().getSession(sessionId);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取登陆的token
|
||||
*
|
||||
* @return 没有返回null
|
||||
*/
|
||||
public static String getSessionId() {
|
||||
ApiParam apiParam = getApiParam();
|
||||
if (apiParam == null) {
|
||||
return null;
|
||||
}
|
||||
return apiParam.fetchAccessToken();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取本地化,从HttpServletRequest中获取,没有则返回Locale.SIMPLIFIED_CHINESE
|
||||
*
|
||||
* @return Locale
|
||||
*/
|
||||
public static Locale getLocale() {
|
||||
HttpServletRequest req = getRequest();
|
||||
if (req == null) {
|
||||
return Locale.SIMPLIFIED_CHINESE;
|
||||
}
|
||||
return req.getLocale();
|
||||
}
|
||||
|
||||
public static void setApiParam(ApiParam apiParam) {
|
||||
setAttr(ATTR_PARAM, apiParam);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取系统参数
|
||||
*
|
||||
* @return 返回ApiParam
|
||||
*/
|
||||
public static ApiParam getApiParam() {
|
||||
return (ApiParam) getAttr(ATTR_PARAM);
|
||||
}
|
||||
|
||||
public static ApiConfig getApiConfig() {
|
||||
return ApiConfig.getInstance();
|
||||
}
|
||||
|
||||
public static void setApiConfig(ApiConfig apiConfig) {
|
||||
ApiConfig.setInstance(apiConfig);
|
||||
}
|
||||
|
||||
|
||||
public static ServletContext getServletContext() {
|
||||
ServletContext ctx = null;
|
||||
HttpSession session = getSession();
|
||||
if (session != null) {
|
||||
ctx = session.getServletContext();
|
||||
}
|
||||
return ctx;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取上传文件,如果客户端有文件上传,从这里取。
|
||||
* @return 如果没有文件上传,返回null
|
||||
*/
|
||||
public static UploadContext getUploadContext() {
|
||||
return (UploadContext) getAttr(ATTR_UPLOAD_CONTEXT);
|
||||
}
|
||||
|
||||
public static void setUploadContext(UploadContext uploadCtx) {
|
||||
setAttr(ATTR_UPLOAD_CONTEXT, uploadCtx);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取response
|
||||
* @return 返回response
|
||||
*/
|
||||
public static HttpServletResponse getResponse() {
|
||||
return RequestContext.getCurrentContext().getResponse();
|
||||
}
|
||||
}
|
@@ -0,0 +1,16 @@
|
||||
package com.gitee.sop.gatewaycommon.zuul.configuration;
|
||||
|
||||
import com.gitee.sop.gatewaycommon.bean.ApiContext;
|
||||
import com.gitee.sop.gatewaycommon.validate.alipay.AlipaySigner;
|
||||
|
||||
/**
|
||||
* 具备支付宝开放平台能力配置 https://docs.open.alipay.com/api
|
||||
*
|
||||
* @author tanghc
|
||||
*/
|
||||
public class AlipayZuulConfiguration extends BaseZuulConfiguration {
|
||||
|
||||
static {
|
||||
ApiContext.getApiConfig().setSigner(new AlipaySigner());
|
||||
}
|
||||
}
|
@@ -0,0 +1,92 @@
|
||||
package com.gitee.sop.gatewaycommon.zuul.configuration;
|
||||
|
||||
import com.gitee.sop.gatewaycommon.bean.ApiContext;
|
||||
import com.gitee.sop.gatewaycommon.manager.RouteManager;
|
||||
import com.gitee.sop.gatewaycommon.message.ErrorFactory;
|
||||
import com.gitee.sop.gatewaycommon.zuul.filter.ErrorFilter;
|
||||
import com.gitee.sop.gatewaycommon.zuul.filter.PostResultFilter;
|
||||
import com.gitee.sop.gatewaycommon.zuul.filter.PreValidateFilter;
|
||||
import com.gitee.sop.gatewaycommon.zuul.route.SopRouteLocator;
|
||||
import com.gitee.sop.gatewaycommon.zuul.route.ZuulRouteRepository;
|
||||
import com.gitee.sop.gatewaycommon.zuul.route.ZuulZookeeperRouteManager;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.web.ServerProperties;
|
||||
import org.springframework.cloud.netflix.zuul.filters.ProxyRequestHelper;
|
||||
import org.springframework.cloud.netflix.zuul.filters.ZuulProperties;
|
||||
import org.springframework.cloud.netflix.zuul.filters.pre.PreDecorationFilter;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.core.env.Environment;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
|
||||
/**
|
||||
* @author tanghc
|
||||
*/
|
||||
public class BaseZuulConfiguration {
|
||||
|
||||
@Autowired
|
||||
protected ZuulProperties zuulProperties;
|
||||
|
||||
@Autowired
|
||||
protected ServerProperties server;
|
||||
|
||||
@Autowired
|
||||
protected Environment environment;
|
||||
|
||||
@Autowired
|
||||
protected RouteManager apiMetaManager;
|
||||
|
||||
@Bean
|
||||
ZuulZookeeperRouteManager zuulZookeeperRouteManager(Environment environment, ZuulRouteRepository zuulRouteRepository) {
|
||||
return new ZuulZookeeperRouteManager(environment, zuulRouteRepository);
|
||||
}
|
||||
|
||||
@Bean
|
||||
ZuulRouteRepository zuulRouteRepository() {
|
||||
return new ZuulRouteRepository();
|
||||
}
|
||||
|
||||
@Bean
|
||||
PreValidateFilter preValidateFilter() {
|
||||
return new PreValidateFilter();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public PreDecorationFilter preDecorationFilter(ZuulRouteRepository zuulRouteRepository, ProxyRequestHelper proxyRequestHelper) {
|
||||
SopRouteLocator routeLocator = new SopRouteLocator(zuulRouteRepository);
|
||||
return new PreDecorationFilter(routeLocator,
|
||||
this.server.getServlet().getContextPath(),
|
||||
this.zuulProperties,
|
||||
proxyRequestHelper);
|
||||
}
|
||||
|
||||
@Bean
|
||||
ErrorFilter errorFilter() {
|
||||
return new ErrorFilter();
|
||||
}
|
||||
|
||||
@Bean
|
||||
PostResultFilter postResultFilter() {
|
||||
return new PostResultFilter();
|
||||
}
|
||||
|
||||
@Bean
|
||||
BaseZuulController baseZuulController() {
|
||||
return ApiContext.getApiConfig().getBaseZuulController();
|
||||
}
|
||||
|
||||
@PostConstruct
|
||||
public void after() {
|
||||
doAfter();
|
||||
}
|
||||
|
||||
protected void doAfter() {
|
||||
initMessage();
|
||||
apiMetaManager.refresh();
|
||||
}
|
||||
|
||||
protected void initMessage() {
|
||||
ErrorFactory.initMessageSource(ApiContext.getApiConfig().getI18nModules());
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,43 @@
|
||||
package com.gitee.sop.gatewaycommon.zuul.configuration;
|
||||
|
||||
import com.gitee.sop.gatewaycommon.bean.ApiContext;
|
||||
import com.gitee.sop.gatewaycommon.result.ResultExecutor;
|
||||
import com.netflix.zuul.context.RequestContext;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.boot.web.servlet.error.ErrorController;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
|
||||
/**
|
||||
* 处理网关自身异常
|
||||
*
|
||||
* @author tanghc
|
||||
*/
|
||||
@Controller
|
||||
@Slf4j
|
||||
public class BaseZuulController implements ErrorController {
|
||||
|
||||
public static final String ERROR_PATH = "/error";
|
||||
|
||||
/**
|
||||
* 错误最终会到这里来
|
||||
*/
|
||||
@RequestMapping(ERROR_PATH)
|
||||
@ResponseBody
|
||||
public Object error() {
|
||||
RequestContext ctx = RequestContext.getCurrentContext();
|
||||
Throwable throwable = ctx.getThrowable();
|
||||
return this.buildResult(throwable);
|
||||
}
|
||||
|
||||
protected Object buildResult(Throwable throwable) {
|
||||
ResultExecutor<RequestContext, String> resultExecutor = ApiContext.getApiConfig().getZuulResultExecutor();
|
||||
return resultExecutor.buildErrorResult(RequestContext.getCurrentContext(), throwable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getErrorPath() {
|
||||
return ERROR_PATH;
|
||||
}
|
||||
}
|
@@ -0,0 +1,23 @@
|
||||
package com.gitee.sop.gatewaycommon.zuul.configuration;
|
||||
|
||||
import com.gitee.sop.gatewaycommon.bean.ApiContext;
|
||||
import com.gitee.sop.gatewaycommon.param.ParamNames;
|
||||
import com.gitee.sop.gatewaycommon.validate.taobao.TaobaoSigner;
|
||||
|
||||
/**
|
||||
* 具备淘宝开放平台能力配置
|
||||
* 淘宝开放平台:http://open.taobao.com/doc.htm
|
||||
* @author tanghc
|
||||
*/
|
||||
public class TaobaoZuulConfiguration extends BaseZuulConfiguration {
|
||||
|
||||
static {
|
||||
ParamNames.APP_KEY_NAME = "app_key";
|
||||
ParamNames.SIGN_TYPE_NAME = "sign_method";
|
||||
ParamNames.VERSION_NAME = "v";
|
||||
ParamNames.APP_AUTH_TOKEN_NAME = "session";
|
||||
|
||||
ApiContext.getApiConfig().setSigner(new TaobaoSigner());
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,117 @@
|
||||
package com.gitee.sop.gatewaycommon.zuul.filter;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.gitee.sop.gatewaycommon.result.ApiResult;
|
||||
import com.netflix.zuul.ZuulFilter;
|
||||
import com.netflix.zuul.context.RequestContext;
|
||||
import com.netflix.zuul.exception.ZuulException;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* @author tanghc
|
||||
*/
|
||||
public abstract class BaseZuulFilter extends ZuulFilter {
|
||||
|
||||
protected Logger log = LoggerFactory.getLogger(getClass());
|
||||
|
||||
private Integer filterOrder;
|
||||
|
||||
/**
|
||||
* 获取过滤器类型
|
||||
* @return 返回FilterType
|
||||
* @see ZuulFilter#filterType() filterType()
|
||||
*/
|
||||
protected abstract FilterType getFilterType();
|
||||
|
||||
/**
|
||||
* 获取过滤器顺序
|
||||
* @return
|
||||
* @see ZuulFilter#filterOrder() filterOrder()
|
||||
*/
|
||||
protected abstract int getFilterOrder();
|
||||
|
||||
/**
|
||||
* 执行run
|
||||
* @param requestContext
|
||||
* @return
|
||||
* @throws ZuulException
|
||||
*/
|
||||
protected abstract Object doRun(RequestContext requestContext) throws ZuulException;
|
||||
|
||||
/**
|
||||
* 设置过滤器顺序
|
||||
*
|
||||
* @param filterOrder 顺序,值越小优先执行
|
||||
* @return 返回自身对象
|
||||
*/
|
||||
public BaseZuulFilter order(int filterOrder) {
|
||||
this.filterOrder = filterOrder;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int filterOrder() {
|
||||
return filterOrder != null ? filterOrder : this.getFilterOrder();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String filterType() {
|
||||
return this.getFilterType().getType();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldFilter() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object run() throws ZuulException {
|
||||
return this.doRun(RequestContext.getCurrentContext());
|
||||
}
|
||||
|
||||
/**
|
||||
* 过滤该请求,不往下级服务去转发请求,到此结束。并填充responseBody
|
||||
*
|
||||
* @param requestContext
|
||||
* @param result
|
||||
*/
|
||||
public static void stopRouteAndReturn(RequestContext requestContext, Object result) {
|
||||
requestContext.setSendZuulResponse(false);
|
||||
requestContext.setResponseBody(JSON.toJSONString(result));
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
System.out.println(JSON.toJSONString(new ApiResult()));
|
||||
}
|
||||
|
||||
/**
|
||||
* to classify a filter by type. Standard types in Zuul are "pre" for pre-routing filtering,
|
||||
* "route" for routing to an origin, "post" for post-routing filters, "error" for error handling.
|
||||
* We also support a "static" type for static responses see StaticResponseFilter.
|
||||
* Any filterType made be created or added and doRun by calling FilterProcessor.runFilters(type)
|
||||
*/
|
||||
public enum FilterType {
|
||||
/** zuul过滤器pre类型 */
|
||||
PRE("pre"),
|
||||
/** zuul过滤器route类型 */
|
||||
ROUTE("route"),
|
||||
/** zuul过滤器post类型 */
|
||||
POST("post"),
|
||||
/** zuul过滤器error类型 */
|
||||
ERROR("error"),
|
||||
/** zuul过滤器static类型 */
|
||||
STATIC("static"),
|
||||
;
|
||||
|
||||
FilterType(String type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
private String type;
|
||||
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,60 @@
|
||||
package com.gitee.sop.gatewaycommon.zuul.filter;
|
||||
|
||||
import com.netflix.zuul.FilterProcessor;
|
||||
import com.netflix.zuul.ZuulFilter;
|
||||
import com.netflix.zuul.context.RequestContext;
|
||||
import com.netflix.zuul.exception.ZuulException;
|
||||
import org.springframework.cloud.netflix.zuul.filters.post.SendErrorFilter;
|
||||
import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants;
|
||||
|
||||
/**
|
||||
* 处理来自post过滤器引起的异常
|
||||
* @author tanghc
|
||||
*/
|
||||
public class ErrorFilter extends SendErrorFilter {
|
||||
|
||||
public static final String FAILED_FILTER = "failed.filter";
|
||||
|
||||
private int filterOrder = 10;
|
||||
|
||||
public ErrorFilter() {
|
||||
initFilterProcessor();
|
||||
}
|
||||
|
||||
public void initFilterProcessor() {
|
||||
FilterProcessor instance = FilterProcessor.getInstance();
|
||||
if (!(instance instanceof MyFilterProcessor)) {
|
||||
FilterProcessor.setProcessor(new MyFilterProcessor());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int filterOrder() {
|
||||
return filterOrder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldFilter() {
|
||||
// 判断:仅处理来自post过滤器引起的异常
|
||||
RequestContext ctx = RequestContext.getCurrentContext();
|
||||
ZuulFilter failedFilter = (ZuulFilter) ctx.get(FAILED_FILTER);
|
||||
return failedFilter != null && failedFilter.filterType().equals(FilterConstants.POST_TYPE);
|
||||
}
|
||||
|
||||
public static class MyFilterProcessor extends FilterProcessor {
|
||||
@Override
|
||||
public Object processZuulFilter(ZuulFilter filter) throws ZuulException {
|
||||
try {
|
||||
return super.processZuulFilter(filter);
|
||||
} catch (ZuulException e) {
|
||||
RequestContext ctx = RequestContext.getCurrentContext();
|
||||
ctx.set(FAILED_FILTER, filter);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void setFilterOrder(int filterOrder) {
|
||||
this.filterOrder = filterOrder;
|
||||
}
|
||||
}
|
@@ -0,0 +1,48 @@
|
||||
package com.gitee.sop.gatewaycommon.zuul.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.result.ResultExecutor;
|
||||
import com.netflix.zuul.context.RequestContext;
|
||||
import com.netflix.zuul.exception.ZuulException;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants;
|
||||
|
||||
import java.io.InputStream;
|
||||
|
||||
/**
|
||||
* 合并微服务结果,统一返回格式
|
||||
*
|
||||
* @author tanghc
|
||||
*/
|
||||
public class PostResultFilter extends BaseZuulFilter {
|
||||
|
||||
@Override
|
||||
protected FilterType getFilterType() {
|
||||
return FilterType.POST;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getFilterOrder() {
|
||||
return FilterConstants.SEND_RESPONSE_FILTER_ORDER - 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object doRun(RequestContext requestContext) throws ZuulException {
|
||||
InputStream responseDataStream = requestContext.getResponseDataStream();
|
||||
ApiConfig apiConfig = ApiContext.getApiConfig();
|
||||
ResultExecutor<RequestContext, String> resultExecutor = apiConfig.getZuulResultExecutor();
|
||||
String serviceResult;
|
||||
try {
|
||||
serviceResult = IOUtils.toString(responseDataStream, SopConstants.CHARSET_UTF8);
|
||||
} catch (Exception e) {
|
||||
log.error("业务方无数据返回", e);
|
||||
serviceResult = SopConstants.EMPTY_JSON;
|
||||
}
|
||||
String finalResult = resultExecutor.mergeResult(requestContext, serviceResult);
|
||||
requestContext.setResponseBody(finalResult);
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,41 @@
|
||||
package com.gitee.sop.gatewaycommon.zuul.filter;
|
||||
|
||||
import com.gitee.sop.gatewaycommon.message.ErrorEnum;
|
||||
import com.netflix.zuul.context.RequestContext;
|
||||
import com.netflix.zuul.exception.ZuulException;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
/**
|
||||
* @author tanghc
|
||||
*/
|
||||
public class PreTokenFilter extends BaseZuulFilter {
|
||||
|
||||
@Override
|
||||
protected FilterType getFilterType() {
|
||||
return FilterType.PRE;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getFilterOrder() {
|
||||
return FilterConstants.PRE_DECORATION_FILTER_ORDER + 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object doRun(RequestContext ctx) throws ZuulException {
|
||||
Object serviceId = ctx.get(FilterConstants.SERVICE_ID_KEY);
|
||||
log.info("serviceId:{}", serviceId);
|
||||
HttpServletRequest request = ctx.getRequest();
|
||||
|
||||
log.info("send {} request to {}", request.getMethod(), request.getRequestURL().toString());
|
||||
|
||||
String accessToken = request.getParameter("access_token");
|
||||
if (StringUtils.isBlank(accessToken)) {
|
||||
throw ErrorEnum.AOP_INVALID_APP_AUTH_TOKEN.getErrorMeta().getException();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,48 @@
|
||||
package com.gitee.sop.gatewaycommon.zuul.filter;
|
||||
|
||||
import com.gitee.sop.gatewaycommon.bean.ApiContext;
|
||||
import com.gitee.sop.gatewaycommon.bean.ApiConfig;
|
||||
import com.gitee.sop.gatewaycommon.exception.ApiException;
|
||||
import com.gitee.sop.gatewaycommon.param.ApiParam;
|
||||
import com.gitee.sop.gatewaycommon.validate.Validator;
|
||||
import com.gitee.sop.gatewaycommon.zuul.ZuulContext;
|
||||
import com.netflix.zuul.context.RequestContext;
|
||||
import com.netflix.zuul.exception.ZuulException;
|
||||
import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
/**
|
||||
* 前置校验
|
||||
* @author tanghc
|
||||
*/
|
||||
public class PreValidateFilter extends BaseZuulFilter {
|
||||
@Override
|
||||
protected FilterType getFilterType() {
|
||||
return FilterType.PRE;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getFilterOrder() {
|
||||
// 在org.springframework.cloud.netflix.zuul.filters.pre.PreDecorationFilter前面
|
||||
return FilterConstants.PRE_DECORATION_FILTER_ORDER - 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object doRun(RequestContext requestContext) throws ZuulException {
|
||||
ApiConfig apiConfig = ApiContext.getApiConfig();
|
||||
// 解析参数
|
||||
ApiParam param = apiConfig.getZuulParamBuilder().build(requestContext);
|
||||
ZuulContext.setApiParam(param);
|
||||
// 验证操作,这里有负责验证签名参数
|
||||
Validator validator = apiConfig.getValidator();
|
||||
try {
|
||||
validator.validate(param);
|
||||
} catch (ApiException e) {
|
||||
log.error("验证失败,params:{}", param.toJSONString(), e);
|
||||
throw e;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,111 @@
|
||||
package com.gitee.sop.gatewaycommon.zuul.param;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.gitee.sop.gatewaycommon.message.ErrorEnum;
|
||||
import com.gitee.sop.gatewaycommon.param.ApiParam;
|
||||
import com.gitee.sop.gatewaycommon.param.ApiUploadContext;
|
||||
import com.gitee.sop.gatewaycommon.param.ParamBuilder;
|
||||
import com.gitee.sop.gatewaycommon.util.RequestUtil;
|
||||
import com.gitee.sop.gatewaycommon.zuul.ZuulContext;
|
||||
import com.netflix.zuul.context.RequestContext;
|
||||
import com.netflix.zuul.http.HttpServletRequestWrapper;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
import org.springframework.web.multipart.MultipartHttpServletRequest;
|
||||
import org.springframework.web.multipart.commons.CommonsMultipartResolver;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* 参数解析默认实现
|
||||
*
|
||||
* @author tanghc
|
||||
*/
|
||||
public class ZuulParamBuilder implements ParamBuilder<RequestContext> {
|
||||
|
||||
private static final String CONTENT_TYPE_MULTIPART = MediaType.MULTIPART_FORM_DATA_VALUE;
|
||||
private static final String CONTENT_TYPE_JSON = MediaType.APPLICATION_JSON_VALUE;
|
||||
private static final String CONTENT_TYPE_TEXT = MediaType.TEXT_PLAIN_VALUE;
|
||||
private static final String GET = "get";
|
||||
|
||||
@Override
|
||||
public ApiParam build(RequestContext ctx) {
|
||||
try {
|
||||
HttpServletRequest request = ctx.getRequest();
|
||||
Map<String, Object> params = this.getJson(request);
|
||||
return new ApiParam(params);
|
||||
} catch (Exception e) {
|
||||
throw ErrorEnum.ISV_INVALID_PARAMETER.getErrorMeta().getException();
|
||||
}
|
||||
}
|
||||
|
||||
public Map<String, Object> getJson(HttpServletRequest request) throws Exception {
|
||||
// zuul会做一层包装
|
||||
if (request instanceof HttpServletRequestWrapper) {
|
||||
HttpServletRequestWrapper req = (HttpServletRequestWrapper) request;
|
||||
request = req.getRequest();
|
||||
}
|
||||
Map<String, Object> params = null;
|
||||
|
||||
if (GET.equalsIgnoreCase(request.getMethod())) {
|
||||
params = RequestUtil.convertRequestParamsToMap(request);
|
||||
} else {
|
||||
String contectType = request.getContentType();
|
||||
|
||||
if (contectType == null) {
|
||||
contectType = "";
|
||||
}
|
||||
|
||||
contectType = contectType.toLowerCase();
|
||||
|
||||
// json或者纯文本形式
|
||||
if (contectType.contains(CONTENT_TYPE_JSON) || contectType.contains(CONTENT_TYPE_TEXT)) {
|
||||
String txt = RequestUtil.getText(request);
|
||||
params = JSON.parseObject(txt);
|
||||
} else if (contectType.contains(CONTENT_TYPE_MULTIPART)) {
|
||||
// 上传文件形式
|
||||
params = this.parseUploadRequest(request);
|
||||
} else {
|
||||
params = RequestUtil.convertRequestParamsToMap(request);
|
||||
}
|
||||
}
|
||||
|
||||
return params;
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析文件上传请求
|
||||
*
|
||||
* @param request
|
||||
* @return 返回json字符串
|
||||
*/
|
||||
protected Map<String, Object> parseUploadRequest(HttpServletRequest request) {
|
||||
CommonsMultipartResolver multipartResolver = new CommonsMultipartResolver(
|
||||
request.getSession().getServletContext());
|
||||
// 检查form中是否有enctype="multipart/form-data"
|
||||
if (multipartResolver.isMultipart(request)) {
|
||||
MultipartHttpServletRequest multiRequest = (MultipartHttpServletRequest) request;
|
||||
Map<String, MultipartFile> fileMap = multiRequest.getFileMap();
|
||||
Map<String, MultipartFile> finalMap = new HashMap<>(fileMap.size());
|
||||
|
||||
Set<String> keys = fileMap.keySet();
|
||||
for (String name : keys) {
|
||||
MultipartFile file = fileMap.get(name);
|
||||
if (file.getSize() > 0) {
|
||||
finalMap.put(name, file);
|
||||
}
|
||||
}
|
||||
if (finalMap.size() > 0) {
|
||||
// 保存上传文件
|
||||
ZuulContext.setUploadContext(new ApiUploadContext(finalMap));
|
||||
}
|
||||
}
|
||||
|
||||
return RequestUtil.convertRequestParamsToMap(request);
|
||||
}
|
||||
|
||||
|
||||
}
|
@@ -0,0 +1,58 @@
|
||||
package com.gitee.sop.gatewaycommon.zuul.result;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.gitee.sop.gatewaycommon.bean.SopConstants;
|
||||
import com.gitee.sop.gatewaycommon.exception.ApiException;
|
||||
import com.gitee.sop.gatewaycommon.message.Error;
|
||||
import com.gitee.sop.gatewaycommon.message.ErrorEnum;
|
||||
import com.gitee.sop.gatewaycommon.result.BaseExecutorAdapter;
|
||||
import com.netflix.zuul.context.RequestContext;
|
||||
import com.netflix.zuul.exception.ZuulException;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.http.HttpStatus;
|
||||
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author tanghc
|
||||
*/
|
||||
@Slf4j
|
||||
public class ZuulResultExecutor extends BaseExecutorAdapter<RequestContext, String> {
|
||||
|
||||
@Override
|
||||
public int getBizHeaderCode(RequestContext requestContext) {
|
||||
HttpServletResponse response = requestContext.getResponse();
|
||||
int code = HttpStatus.OK.value();
|
||||
String bizErrorCode = response.getHeader(SopConstants.X_BIZ_ERROR_CODE);
|
||||
if (bizErrorCode != null) {
|
||||
code = Integer.valueOf(bizErrorCode);
|
||||
}
|
||||
return code;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, ?> getApiParam(RequestContext requestContext) {
|
||||
return (Map<String, ?>) requestContext.get(SopConstants.CACHE_API_PARAM);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String buildErrorResult(RequestContext request, Throwable throwable) {
|
||||
Error error = null;
|
||||
if (throwable instanceof ZuulException) {
|
||||
ZuulException ex = (ZuulException) throwable;
|
||||
Throwable cause = ex.getCause();
|
||||
if (cause instanceof ApiException) {
|
||||
ApiException apiException = (ApiException) cause;
|
||||
error = apiException.getError();
|
||||
}
|
||||
}
|
||||
if (error == null) {
|
||||
error = ErrorEnum.AOP_UNKNOW_ERROR.getErrorMeta().getError();
|
||||
}
|
||||
JSONObject jsonObject = (JSONObject) JSON.toJSON(error);
|
||||
return this.merge(request, jsonObject);
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,46 @@
|
||||
package com.gitee.sop.gatewaycommon.zuul.route;
|
||||
|
||||
import com.gitee.sop.gatewaycommon.manager.RouteRepository;
|
||||
import com.gitee.sop.gatewaycommon.param.ApiParam;
|
||||
import com.gitee.sop.gatewaycommon.zuul.ZuulContext;
|
||||
import org.springframework.cloud.netflix.zuul.filters.Route;
|
||||
import org.springframework.cloud.netflix.zuul.filters.RouteLocator;
|
||||
import org.springframework.core.Ordered;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author tanghc
|
||||
*/
|
||||
public class SopRouteLocator implements RouteLocator, Ordered {
|
||||
|
||||
private RouteRepository<Route> routeRepository;
|
||||
|
||||
public SopRouteLocator(RouteRepository<Route> routeRepository) {
|
||||
this.routeRepository = routeRepository;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<String> getIgnoredPaths() {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Route> getRoutes() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Route getMatchingRoute(String path) {
|
||||
ApiParam param = ZuulContext.getApiParam();
|
||||
String nameVersion = param.fetchNameVersion();
|
||||
return routeRepository.get(nameVersion);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOrder() {
|
||||
return 0;
|
||||
}
|
||||
}
|
@@ -0,0 +1,14 @@
|
||||
package com.gitee.sop.gatewaycommon.zuul.route;
|
||||
|
||||
import com.gitee.sop.gatewaycommon.bean.BaseRouteDefinition;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
/**
|
||||
* @author tanghc
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
public class ZuulRouteDefinition extends BaseRouteDefinition {
|
||||
private String path;
|
||||
}
|
@@ -0,0 +1,41 @@
|
||||
package com.gitee.sop.gatewaycommon.zuul.route;
|
||||
|
||||
import com.gitee.sop.gatewaycommon.manager.RouteRepository;
|
||||
import com.gitee.sop.gatewaycommon.message.ErrorEnum;
|
||||
import org.springframework.cloud.netflix.zuul.filters.Route;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author tanghc
|
||||
*/
|
||||
public class ZuulRouteRepository implements RouteRepository<Route> {
|
||||
/** key:nameVersion */
|
||||
private Map<String, Route> nameVersionServiceIdMap = new HashMap<>(128);
|
||||
|
||||
@Override
|
||||
public Route get(String id) {
|
||||
Route route = nameVersionServiceIdMap.get(id);
|
||||
if (route == null) {
|
||||
throw ErrorEnum.ISV_INVALID_METHOD.getErrorMeta().getException();
|
||||
}
|
||||
return route;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String add(Route route) {
|
||||
return this.update(route);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String update(Route route) {
|
||||
nameVersionServiceIdMap.put(route.getId(), route);
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete(String id) {
|
||||
nameVersionServiceIdMap.remove(id);
|
||||
}
|
||||
}
|
@@ -0,0 +1,12 @@
|
||||
package com.gitee.sop.gatewaycommon.zuul.route;
|
||||
|
||||
import com.gitee.sop.gatewaycommon.bean.BaseServiceRouteInfo;
|
||||
import com.gitee.sop.gatewaycommon.gateway.route.GatewayRouteDefinition;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* @author thc
|
||||
*/
|
||||
@Data
|
||||
public class ZuulServiceRouteInfo extends BaseServiceRouteInfo<ZuulRouteDefinition> {
|
||||
}
|
@@ -0,0 +1,33 @@
|
||||
package com.gitee.sop.gatewaycommon.zuul.route;
|
||||
|
||||
import com.gitee.sop.gatewaycommon.manager.BaseRouteManager;
|
||||
import com.gitee.sop.gatewaycommon.manager.RouteRepository;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.cloud.netflix.zuul.filters.Route;
|
||||
import org.springframework.core.env.Environment;
|
||||
|
||||
/**
|
||||
* @author tanghc
|
||||
*/
|
||||
@Slf4j
|
||||
public class ZuulZookeeperRouteManager extends BaseRouteManager<ZuulRouteDefinition, ZuulServiceRouteInfo, Route> {
|
||||
|
||||
public ZuulZookeeperRouteManager(Environment environment, RouteRepository<Route> routeRepository) {
|
||||
super(environment, routeRepository);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Class<ZuulServiceRouteInfo> getServiceRouteInfoClass() {
|
||||
return ZuulServiceRouteInfo.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Class<ZuulRouteDefinition> getRouteDefinitionClass() {
|
||||
return ZuulRouteDefinition.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Route buildRouteDefinition(ZuulServiceRouteInfo serviceRouteInfo, ZuulRouteDefinition routeDefinition) {
|
||||
return new Route(routeDefinition.getId(), routeDefinition.getPath(), serviceRouteInfo.getAppName(), null, false, null);
|
||||
}
|
||||
}
|
@@ -26,10 +26,29 @@
|
||||
<version>1.0.0-SNAPSHOT</version>
|
||||
</dependency>
|
||||
|
||||
<!--<dependency>-->
|
||||
<!--<groupId>org.springframework.cloud</groupId>-->
|
||||
<!--<artifactId>spring-cloud-starter-netflix-zuul</artifactId>-->
|
||||
<!--</dependency>-->
|
||||
<!-- ↓↓↓ 使用spring cloud zuul ↓↓↓
|
||||
如果要使用gateway需要注释掉
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
|
||||
</dependency>
|
||||
-->
|
||||
<!-- ↑↑↑ 使用spring cloud zuul ↑↑↑ -->
|
||||
|
||||
<!-- ↓↓↓ 使用spring cloud gateway ↓↓↓
|
||||
如果要使用zuul需要注释掉
|
||||
-->
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-gateway</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-webflux</artifactId>
|
||||
</dependency>
|
||||
<!-- ↑↑↑ 使用spring cloud gateway ↑↑↑ -->
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
|
||||
|
@@ -1,8 +1,7 @@
|
||||
package com.gitee.sop.gateway.config;
|
||||
|
||||
import com.gitee.sop.gatewaycommon.bean.ApiContext;
|
||||
import com.gitee.sop.gatewaycommon.configuration.AlipayGatewayConfiguration;
|
||||
import com.gitee.sop.gatewaycommon.configuration.TaobaoGatewayConfiguration;
|
||||
import com.gitee.sop.gatewaycommon.gateway.configuration.AlipayGatewayConfiguration;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import java.util.HashMap;
|
||||
@@ -12,25 +11,25 @@ import java.util.Map;
|
||||
* 开通支付宝开放平台能力
|
||||
* @author tanghc
|
||||
*/
|
||||
//@Configuration
|
||||
//public class GatewayConfig extends AlipayGatewayConfiguration {
|
||||
//
|
||||
// {
|
||||
// Map<String, String> appSecretStore = new HashMap();
|
||||
// appSecretStore.put("alipay_test", "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAlyb9aUBaljQP/vjmBFe1mF8HsWSvyfC2NTlpT/V9E+sBxTr8TSkbzJCeeeOEm4LCaVXL0Qz63MZoT24v7AIXTuMdj4jyiM/WJ4tjrWAgnmohNOegfntTto16C3l234vXz4ryWZMR/7W+MXy5B92wPGQEJ0LKFwNEoLspDEWZ7RdE53VH7w6y6sIZUfK+YkXWSwehfKPKlx+lDw3zRJ3/yvMF+U+BAdW/MfECe1GuBnCFKnlMRh3UKczWyXWkL6ItOpYHHJi/jx85op5BWDje2pY9QowzfN94+0DB3T7UvZeweu3zlP6diwAJDzLaFQX8ULfWhY+wfKxIRgs9NoiSAQIDAQAB");
|
||||
// ApiContext.getApiConfig().addAppSecret(appSecretStore);
|
||||
// }
|
||||
//}
|
||||
@Configuration
|
||||
public class GatewayConfig extends AlipayGatewayConfiguration {
|
||||
|
||||
{
|
||||
Map<String, String> appSecretStore = new HashMap();
|
||||
appSecretStore.put("alipay_test", "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAlyb9aUBaljQP/vjmBFe1mF8HsWSvyfC2NTlpT/V9E+sBxTr8TSkbzJCeeeOEm4LCaVXL0Qz63MZoT24v7AIXTuMdj4jyiM/WJ4tjrWAgnmohNOegfntTto16C3l234vXz4ryWZMR/7W+MXy5B92wPGQEJ0LKFwNEoLspDEWZ7RdE53VH7w6y6sIZUfK+YkXWSwehfKPKlx+lDw3zRJ3/yvMF+U+BAdW/MfECe1GuBnCFKnlMRh3UKczWyXWkL6ItOpYHHJi/jx85op5BWDje2pY9QowzfN94+0DB3T7UvZeweu3zlP6diwAJDzLaFQX8ULfWhY+wfKxIRgs9NoiSAQIDAQAB");
|
||||
ApiContext.getApiConfig().addAppSecret(appSecretStore);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 开通淘宝开放平能力
|
||||
*/
|
||||
@Configuration
|
||||
public class GatewayConfig extends TaobaoGatewayConfiguration {
|
||||
|
||||
{
|
||||
Map<String, String> appSecretStore = new HashMap();
|
||||
appSecretStore.put("taobao_test", "G9w0BAQEFAAOCAQ8AMIIBCgKCA");
|
||||
ApiContext.getApiConfig().addAppSecret(appSecretStore);
|
||||
}
|
||||
}
|
||||
//@Configuration
|
||||
//public class GatewayConfig extends TaobaoGatewayConfiguration {
|
||||
//
|
||||
// {
|
||||
// Map<String, String> appSecretStore = new HashMap();
|
||||
// appSecretStore.put("taobao_test", "G9w0BAQEFAAOCAQ8AMIIBCgKCA");
|
||||
// ApiContext.getApiConfig().addAppSecret(appSecretStore);
|
||||
// }
|
||||
//}
|
||||
|
@@ -8,16 +8,16 @@ import org.springframework.context.annotation.Configuration;
|
||||
* 使用支付宝开放平台功能
|
||||
* @author tanghc
|
||||
*/
|
||||
//@Configuration
|
||||
//public class OpenServiceConfig extends AlipayServiceConfiguration {
|
||||
//
|
||||
//}
|
||||
@Configuration
|
||||
public class OpenServiceConfig extends AlipayServiceConfiguration {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用淘宝开放平台功能
|
||||
* @author tanghc
|
||||
*/
|
||||
@Configuration
|
||||
public class OpenServiceConfig extends TaobaoServiceConfiguration {
|
||||
|
||||
}
|
||||
//@Configuration
|
||||
//public class OpenServiceConfig extends TaobaoServiceConfiguration {
|
||||
//
|
||||
//}
|
||||
|
Reference in New Issue
Block a user