parent
11ea3c2ae9
commit
7d22c92122
@ -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; |
||||
} |
@ -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,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,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.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; |
||||
|
@ -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; |
||||
} |
@ -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); |
||||
} |
||||
} |
Loading…
Reference in new issue