Spring Cloud Getway 动态配置路由及自定义修改请求内容、请求url、相应内容
程序员文章站
2023-01-13 19:03:05
一 、最近研究平台代理看到Spring Cloud Getway ,动态添加删除根据网上直接复制具体如下:spring-boot和cloud版本如下(Finchley.SR1版本能代理到具体的path,之后高版本Getway会自动把path给删除掉,也就是高版本只能代理到port)2.0.6.RELEASE Finchley.SR....
一 、最近研究平台代理看到Spring Cloud Getway ,动态添加删除根据网上直接复制具体如下:
spring-boot和cloud版本如下(Finchley.SR1版本能代理到具体的path,之后高版本Getway会自动把path给删除掉,也就是高版本只能代理到port)
<spring-boot.version>2.0.6.RELEASE</spring-boot.version>
<spring-cloud.version>Finchley.SR1</spring-cloud.version>
主要依赖:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
配置文件(配置了两个固定的路由),其中要说的是filters中增加PreserveHostHeader能够发送网关主机头,像webservice这种soap可以不暴露服务原始地址
server:
port: 8085
spring:
application:
name: gateway-dynamic-route
datasource:
driver-class-name: oracle.jdbc.OracleDriver
url:
username:
password:
hikari:
minimum-idle: 5
idle-timeout: 600000
maximum-pool-size: 10
auto-commit: true
pool-name: MyHikariCP
max-lifetime: 1800000
connection-timeout: 30000
connection-test-query: SELECT 1 FROM DUAL
cloud:
gateway:
# discovery:
# locator:
# enabled: true
routes:
- id: user-service # 请求转发
uri: http://192.168.2.163:8999 # 代理restful地址只能写到端口号
predicates:
- Path=/HttpService/** # 匹配路由前缀规则
filters:
- PreserveHostHeader # 发送网关原始主机头:
- StripPrefix=1
- id: user11-service # 请求转发
uri: http://192.168.2.163:8999/webservice/messagePackService?wsdl # 代理webservice要全路径 且Path路劲后面不能有**做模糊匹配
predicates:
- Path=/HttpWebService # 匹配路由前缀规则
filters:
- PreserveHostHeader # 发送网关原始主机头:
# - StripPrefix=1 # 替换第一部分
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
mapper-locations: classpath:mybatis/*.xml
具体动态路由class类
动态路由数据入口
import com.neil.dynamic.gateway.definition.GatewayFilterDefinition;
import com.neil.dynamic.gateway.definition.GatewayPredicateDefinition;
import com.neil.dynamic.gateway.definition.GatewayRouteDefinition;
import com.neil.dynamic.gateway.service.DynamicRouteService;
import org.springframework.beans.factory.annotation.Autowired;
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.cloud.gateway.route.RouteDefinitionLocator;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.util.UriComponentsBuilder;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import java.net.URI;
import java.util.ArrayList;
import java.util.List;
/**
* 获取网关的路由信息
* @ProjectName:
* @Package: com.neil.dynamic.gateway.controller
* @ClassName: DynamicRouteController
* @Description: java类作用描述
* @Author: Neil.Zhou
* @CreateDate: 2019/10/14 16:19
* @UpdateUser: Neil.Zhou
* @UpdateDate: 2019/10/14 16:19
* @UpdateRemark: The modified content
* @Version: 1.0
* <p>Copyright: Copyright (c) 2019/10/14</p>
*
*/
@RestController
@RequestMapping("/route")
public class DynamicRouteController {
@Autowired
private RouteDefinitionLocator routeDefinitionLocator;
@Autowired
private DynamicRouteService dynamicRouteService;
/**
* 获取网关所有的路由信息
* @method
* @author 作者姓名
* @version
* @param
* @return
* @exception
* @date 2019/10/14 16:20
*/
@GetMapping("/routes")
public Flux<RouteDefinition> getRouteDefinitions(){
return routeDefinitionLocator.getRouteDefinitions();
}
//增加路由
@PostMapping("/add")
public String add(@RequestBody GatewayRouteDefinition gwdefinition) {
String flag = "fail";
try {
RouteDefinition definition = assembleRouteDefinition(gwdefinition);
flag = this.dynamicRouteService.add(definition);
} catch (Exception e) {
e.printStackTrace();
}
return flag;
}
//更新路由
@PostMapping("/update")
public String update(@RequestBody GatewayRouteDefinition gwdefinition) {
RouteDefinition definition = assembleRouteDefinition(gwdefinition);
return this.dynamicRouteService.update(definition);
}
//删除路由
@DeleteMapping("/routes/{id}")
public Mono<ResponseEntity<Object>> delete(@PathVariable String id) {
try {
return this.dynamicRouteService.delete(id);
}catch (Exception e){
e.printStackTrace();
}
return null;
}
//把前端传递的参数转换成路由对象
private RouteDefinition assembleRouteDefinition(GatewayRouteDefinition gwdefinition) {
RouteDefinition definition = new RouteDefinition();
definition.setId(gwdefinition.getId());
definition.setOrder(gwdefinition.getOrder());
//设置断言
List<PredicateDefinition> pdList=new ArrayList<PredicateDefinition>();
List<GatewayPredicateDefinition> gatewayPredicateDefinitionList=gwdefinition.getPredicates();
for (GatewayPredicateDefinition gpDefinition: gatewayPredicateDefinitionList) {
PredicateDefinition predicate = new PredicateDefinition();
predicate.setArgs(gpDefinition.getArgs());
predicate.setName(gpDefinition.getName());
pdList.add(predicate);
}
definition.setPredicates(pdList);
//设置过滤器
List<FilterDefinition> filters = new ArrayList();
List<GatewayFilterDefinition> gatewayFilters = gwdefinition.getFilters();
for(GatewayFilterDefinition filterDefinition : gatewayFilters){
FilterDefinition filter = new FilterDefinition();
filter.setName(filterDefinition.getName());
filter.setArgs(filterDefinition.getArgs());
filters.add(filter);
}
definition.setFilters(filters);
URI uri = null;
if(gwdefinition.getUri().startsWith("http")){
uri = UriComponentsBuilder.fromHttpUrl(gwdefinition.getUri()).build().toUri();
}else{
uri = URI.create(gwdefinition.getUri());
}
definition.setUri(uri);
return definition;
}
}
路由、过滤器模型
import lombok.Data;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* 过滤器模型
* @ProjectName:
* @Package: com.neil.dynamic.gateway.definition
* @ClassName: GatewayFilterDefinition
* @Description: 过滤器模型
* @Author: Neil.Zhou
* @CreateDate: 2019/10/14 17:00
* @UpdateUser: Neil.Zhou
* @UpdateDate: 2019/10/14 17:00
* @UpdateRemark: The modified content
* @Version: 1.0
* <p>Copyright: Copyright (c) 2019/10/14</p>
*
*/
@Data
public class GatewayFilterDefinition {
//Filter Name
private String name;
//对应的路由规则
private Map<String, String> args = new LinkedHashMap<>();
}
import lombok.Data;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* 路由断言模型
* @ProjectName:
* @Package: com.neil.dynamic.gateway.definition
* @ClassName: GatewayPredicateDefinition
* @Description: 路由断言模型
* @Author: Neil.Zhou
* @CreateDate: 2019/10/14 17:00
* @UpdateUser: Neil.Zhou
* @UpdateDate: 2019/10/14 17:00
* @UpdateRemark: The modified content
* @Version: 1.0
* <p>Copyright: Copyright (c) 2019/10/14</p>
*
*/
@Data
public class GatewayPredicateDefinition {
//断言对应的Name
private String name;
//配置的断言规则
private Map<String, String> args = new LinkedHashMap<>();
}
import lombok.Data;
import java.util.ArrayList;
import java.util.List;
/**
* 路由模型对象
* @ProjectName:
* @Package: com.neil.dynamic.gateway.definition
* @ClassName: GatewayRouteDefinition
* @Description: 路由模型对象
* @Author: Neil.Zhou
* @CreateDate: 2019/10/14 16:23
* @UpdateUser: Neil.Zhou
* @UpdateDate: 2019/10/14 16:23
* @UpdateRemark: The modified content
* @Version: 1.0
* <p>Copyright: Copyright (c) 2019/10/14</p>
*
*/
@Data
public class GatewayRouteDefinition {
/*** 路由的Id ***/
private String id;
/*** 路由断言集合配置 ***/
private List<GatewayPredicateDefinition> predicates = new ArrayList<>();
/*** 路由过滤器集合配置 ***/
private List<GatewayFilterDefinition> filters = new ArrayList<>();
/*** 路由规则转发的目标uri ***/
private String uri;
/*** 路由执行的顺序 ***/
private int order = 0;
}
具体实现类
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.event.RefreshRoutesEvent;
import org.springframework.cloud.gateway.route.RouteDefinition;
import org.springframework.cloud.gateway.route.RouteDefinitionWriter;
import org.springframework.cloud.gateway.support.NotFoundException;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import reactor.core.publisher.Mono;
/**
* 动态路由维护
* @ProjectName:
* @Package: com.neil.dynamic.gateway.controller
* @ClassName: DynamicRouteService
* @Description: 动态路由维护
* @Author: Neil.Zhou
* @CreateDate: 2019/10/14 16:58
* @UpdateUser: Neil.Zhou
* @UpdateDate: 2019/10/14 16:58
* @UpdateRemark: The modified content
* @Version: 1.0
* <p>Copyright: Copyright (c) 2019/10/14</p>
*
*/
@Service
public class DynamicRouteService implements ApplicationEventPublisherAware {
@Autowired
private RouteDefinitionWriter routeDefinitionWriter;
private ApplicationEventPublisher publisher;
@Override
public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
this.publisher = applicationEventPublisher;
}
/**
* 增加路由
* @method
* @author Neil.Zhou
* @version
* @return
* @exception
* @date 2019/10/14 16:59
*/
public String add(RouteDefinition definition) {
routeDefinitionWriter.save(Mono.just(definition)).subscribe();
this.publisher.publishEvent(new RefreshRoutesEvent(this));
return "添加成功!";
}
/**
* 更新路由
* @method
* @author Neil.Zhou
* @version
* @return
* @exception
* @date 2019/10/14 16:59
*/
public String update(RouteDefinition definition) {
try {
delete(definition.getId());
} catch (Exception e) {
return "update fail,not find route routeId: "+definition.getId();
}
try {
routeDefinitionWriter.save(Mono.just(definition)).subscribe();
this.publisher.publishEvent(new RefreshRoutesEvent(this));
return "路由修改成功!";
} catch (Exception e) {
return "更新失败!";
}
}
/**
* 删除路由
* @method
* @author Neil.Zhou
* @version
* @return
* @exception
* @date 2019/10/14 16:59
*/
public Mono<ResponseEntity<Object>> delete(String id) {
return this.routeDefinitionWriter.delete(Mono.just(id)).then(Mono.defer(() -> {
return Mono.just(ResponseEntity.ok().build());
})).onErrorResume((t) -> {
return t instanceof NotFoundException;
}, (t) -> {
return Mono.just(ResponseEntity.notFound().build());
});
}
}
另外postman测试增加路由截图
具体json
{
"id": "user66-service",
"predicates": [{
"name": "Path",
"args": {
"_genkey_0": "/HttpService/WS005"
}
}],
"filters": [{
"name": "PreserveHostHeader",
"args": {}
},
{
"name":"StripPrefix",
"args":{
"_genkey_0":"2"
}
}],
"uri": "http://192.168.2.163:8999/webservice/messagePackService?wsdl",
"order": 1
}
二 、自定义拦截请求及返回等信息
增加个GlobalGilter 全局过滤器接口
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.neil.dynamic.gateway.filter.model.BodyHandlerFunction;
import com.neil.dynamic.gateway.filter.model.BodyHandlerServerHttpResponseDecorator;
import io.netty.buffer.ByteBufAllocator;
import org.apache.commons.lang3.StringUtils;
import org.reactivestreams.Publisher;
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.route.Route;
import org.springframework.cloud.gateway.support.BodyInserterContext;
import org.springframework.cloud.gateway.support.CachedBodyOutputMessage;
import org.springframework.cloud.gateway.support.DefaultServerRequest;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.*;
import org.springframework.http.HttpMethod;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpRequestDecorator;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.http.server.reactive.ServerHttpResponseDecorator;
import org.springframework.stereotype.Component;
import org.springframework.web.reactive.function.BodyInserter;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.util.UriComponentsBuilder;
import org.synchronoss.cloud.nio.multipart.util.IOUtils;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import org.springframework.http.HttpHeaders;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.CharBuffer;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicReference;
import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR;
@Component("requestGlobalFilter")
public class RequestGlobalFilter implements GlobalFilter,Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
// TODO 拦截主要操作内容
}
//执行顺序
@Override
public int getOrder() {
//WRITE_RESPONSE_FILTER 之前执行
return NettyWriteResponseFilter.WRITE_RESPONSE_FILTER_ORDER - 1;
}
}
修改请求和返回信息都需要使用gatway提供的修改器ServerHttpRequestDecorator 和ServerHttpResponseDecorator
拦截请求url并修改重写GlobalFilter的filter方法
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
// exchange.transformUrl()
URI sourceUri = request.getURI();
System.out.println(" sourceUri : " + sourceUri.toString());
String sourcePath = sourceUri.getPath();
String sourceQuery = sourceUri.getQuery();
System.out.println("sourcePath"+sourcePath+";sourceQuery"+sourceQuery);
ServerHttpRequestDecorator serverHttpRequestDecorator = requestDecorator(exchange);
return chain.filter(exchange.mutate().request(serverHttpRequestDecorator).build());
// return chain.filter(exchange);
}
private ServerHttpRequestDecorator requestDecorator(ServerWebExchange exchange) {
ServerHttpRequestDecorator serverHttpRequestDecorator = new ServerHttpRequestDecorator(exchange.getRequest()) {
@Override
public URI getURI() {
URI url = super.getURI();
// URI base = UriComponentsBuilder.fromUri(url).replacePath("/HttpService/webservice/messagePackService").replaceQuery("?wsdl").build(true).toUri();
URI base = null;
try {
base = new URI("http://localhost:8085/HttpService/webservice/messagePackService?wsdl");
} catch (URISyntaxException e) {
e.printStackTrace();
}
String path = url.getPath();
System.out.println("cscscs1:"+url.toString());
System.out.println("cscscs:"+path);
// return url;
return base;
}
};
return serverHttpRequestDecorator;
}
//执行顺序
@Override
public int getOrder() {
//WRITE_RESPONSE_FILTER 之前执行
return NettyWriteResponseFilter.WRITE_RESPONSE_FILTER_ORDER - 1;
}
拦截请求信息及返回信息重写GlobalFilter的filter方法(注意在拿出请求和返回的body要重新设置头长度否则收不到消息)
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
// 获取body
ServerHttpRequest request = exchange.getRequest();
// // 只拦截POST 请求
// if (request.getMethod() != HttpMethod.POST) {
// return chain.filter(exchange);
// }
ServerHttpRequestDecorator serverHttpRequestDecorator = requestDecorator(exchange);
//构建响应拦截处理器
BodyHandlerFunction bodyHandler = (resp, body) -> Flux.from(body)
.map(dataBuffer -> {
//响应信息转换为字符串
String reqBody = null;
try {
//dataBuffer 转换为String
reqBody = IOUtils
.inputStreamAsString(dataBuffer.asInputStream(), "ISO-8859-1")
.replaceAll("sayHello", "sayHello1");
System.out.println(reqBody);
} catch (IOException e) {
e.printStackTrace();
}
return reqBody;
})
.flatMap(orgBody -> {
//根据原有的响应信息构建新响应信息并写入到resp中
//此处可以根据业务进行组装新的响应信息,
// 例如:登录后创建会话
//- 拿到登录请求的响应信息,判断登录是否成功
//- 登录成功调用创建会话接口,根据创建会话接口返回组装登录的真实响应
String rbody = orgBody + "";
HttpHeaders headers = resp.getHeaders();
if(headers.get("Access-Control-Allow-Origin")!=null && headers.get("Access-Control-Allow-Origin").size()>1){
String value=headers.get("Access-Control-Allow-Origin").get(0);
headers.remove("Access-Control-Allow-Origin");
headers.setAccessControlAllowOrigin(value);
}
if(headers.get("Access-Control-Allow-Credentials")!=null && headers.get("Access-Control-Allow-Credentials").size()>1){
String value=headers.get("Access-Control-Allow-Credentials").get(0);
headers.remove("Access-Control-Allow-Credentials");
headers.setAccessControlAllowCredentials(value=="true");
}
return resp.writeWith(Flux.just(rbody)
.map(bx -> resp.bufferFactory()
.wrap(bx.getBytes())));
}).then();
//构建响应包装类
// BodyHandlerServerHttpResponseDecorator responseDecorator = new BodyHandlerServerHttpResponseDecorator(bodyHandler, exchange.getResponse());
ServerHttpResponseDecorator serverHttpResponseDecorator = responseDecorator(exchange) ;
return chain.filter(exchange.mutate().request(serverHttpRequestDecorator).response(serverHttpResponseDecorator).build());
//
// String requesBody = resolveBodyFromRequest(req);
// System.out.println("requesBody:"+requesBody);
// return chain.filter(exchange);
}
private ServerHttpResponseDecorator responseDecorator(ServerWebExchange exchange ) {
ServerHttpResponse originalResponse = exchange.getResponse();
DataBufferFactory bufferFactory = originalResponse.bufferFactory();
ServerHttpResponseDecorator decoratedResponse = new ServerHttpResponseDecorator(originalResponse) {
@Override
public Mono<Void> writeWith(Publisher<? extends DataBuffer> body) {
if (body instanceof Flux) {
Flux<? extends DataBuffer> fluxBody = (Flux<? extends DataBuffer>) body;
return super.writeWith(fluxBody.buffer().map(dataBuffer -> {
DataBufferFactory dataBufferFactory = new DefaultDataBufferFactory();
DataBuffer join = dataBufferFactory.join(dataBuffer);
byte[] content = new byte[join.readableByteCount()];
join.read(content);
//释放掉内存
DataBufferUtils.release(join);
String s = new String(content, StandardCharsets.UTF_8);
//TODO,s就是response的值,
// cacheService.saveCacheList(path,s,"10");
System.out.println("Response1:"+s);
s = s.replace("http://localhost:8085/webservice/messagePackService", "http://localhost:8085/HttpWebService");
System.out.println("Response:"+s);
byte[] uppedContent = new String(s.getBytes(), StandardCharsets.UTF_8).getBytes();
this.getDelegate().getHeaders().setContentLength(uppedContent.length);//如果不重新设置长度则收不到消息。
return bufferFactory.wrap(uppedContent);
}));
}
// if body is not a flux. never got there.
return super.writeWith(body);
}
};
return decoratedResponse;
}
private ServerHttpRequestDecorator requestDecorator(ServerWebExchange exchange) {
ServerHttpRequestDecorator serverHttpRequestDecorator = new ServerHttpRequestDecorator(exchange.getRequest()) {
@Override
public Flux<DataBuffer> getBody() {
Flux<DataBuffer> body = super.getBody();
return body.map(dataBuffer -> {
byte[] content = new byte[dataBuffer.readableByteCount()];
dataBuffer.read(content);
//释放掉内存
DataBufferUtils.release(dataBuffer);
//request body的json格式数据
String bodyString = new String(content, Charset.forName("UTF-8"));
System.out.println("requesBody"+bodyString);
//转成字节
byte[] bytes = bodyString.getBytes();
NettyDataBufferFactory nettyDataBufferFactory = new NettyDataBufferFactory(ByteBufAllocator.DEFAULT);
DataBuffer buffer = nettyDataBufferFactory.allocateBuffer(bytes.length);
buffer.write(bytes);
return buffer;
});
}
//复写getHeaders方法
@Override
public HttpHeaders getHeaders() {
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.putAll(super.getHeaders());
//由于修改了请求体的body,导致content-length长度不确定,因此需要删除原先的content-length
httpHeaders.remove(HttpHeaders.CONTENT_LENGTH);
httpHeaders.set(HttpHeaders.TRANSFER_ENCODING, "chunked");
return httpHeaders;
}
};
return serverHttpRequestDecorator;
}
还有修改Route
//
// @Override
// public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
// // 修改route 目标内容
// ServerHttpRequest req = exchange.getRequest();
//
// URI newUri = UriComponentsBuilder.fromHttpUrl("http://192.168.2.163:8999/webservice/messagePackService?wsdl").build(true).toUri();
// ServerHttpRequest request = req.mutate().uri(newUri).build();
// Route route = exchange.getAttribute(GATEWAY_ROUTE_ATTR);
//
// Route newRoute = Route.async().asyncPredicate(route.getPredicate())
// .filters(route.getFilters()).id(route.getId()).order(route.getOrder()).uri(newUri).build();
// exchange.getAttributes().put(GATEWAY_ROUTE_ATTR,newRoute);
//
// return chain.filter(exchange.mutate().request(req).build());
// }
本文地址:https://blog.csdn.net/weixin_40912627/article/details/107381180
上一篇: PHP内置服务器的使用步骤
下一篇: Java-Thread-生产者消费模式