spring cloud gateway中如何读取请求参数
作者:影落离风 发布时间:2021-08-19 16:19:02
标签:springcloud,gateway,请求,参数
spring cloud gateway读取请求参数
1. 我的版本:
spring-cloud:Hoxton.RELEASE
spring-boot:2.2.2.RELEASE
spring-cloud-starter-gateway
2. 请求日志
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferUtils;
import org.springframework.http.HttpMethod;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpRequestDecorator;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import java.nio.charset.StandardCharsets;
import java.util.Map;
import java.util.stream.Collectors;
/**
* @author MinWeikai
* @date 2019-12-20 18:09:39
*/
@Slf4j
@Component
public class LoggerFilter implements GlobalFilter {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
String method = request.getMethodValue();
if (HttpMethod.POST.matches(method)) {
return DataBufferUtils.join(exchange.getRequest().getBody())
.flatMap(dataBuffer -> {
byte[] bytes = new byte[dataBuffer.readableByteCount()];
dataBuffer.read(bytes);
String bodyString = new String(bytes, StandardCharsets.UTF_8);
logtrace(exchange, bodyString);
exchange.getAttributes().put("POST_BODY", bodyString);
DataBufferUtils.release(dataBuffer);
Flux<DataBuffer> cachedFlux = Flux.defer(() -> {
DataBuffer buffer = exchange.getResponse().bufferFactory()
.wrap(bytes);
return Mono.just(buffer);
});
ServerHttpRequest mutatedRequest = new ServerHttpRequestDecorator(
exchange.getRequest()) {
@Override
public Flux<DataBuffer> getBody() {
return cachedFlux;
}
};
return chain.filter(exchange.mutate().request(mutatedRequest)
.build());
});
} else if (HttpMethod.GET.matches(method)) {
Map m = request.getQueryParams();
logtrace(exchange, m.toString());
}
return chain.filter(exchange);
}
/**
* 日志信息
*
* @param exchange
* @param param 请求参数
*/
private void logtrace(ServerWebExchange exchange, String param) {
ServerHttpRequest serverHttpRequest = exchange.getRequest();
String path = serverHttpRequest.getURI().getPath();
String method = serverHttpRequest.getMethodValue();
String headers = serverHttpRequest.getHeaders().entrySet()
.stream()
.map(entry -> " " + entry.getKey() + ": [" + String.join(";", entry.getValue()) + "]")
.collect(Collectors.joining("\n"));
log.info("\n" + "---------------- ---------------- ---------------->>\n" +
"HttpMethod : {}\n" +
"Uri : {}\n" +
"Param : {}\n" +
"Headers : \n" +
"{}\n" +
"\"<<---------------- ---------------- ----------------"
, method, path, param, headers);
}
}
3. 测试输出,我这边测试没有问题,日志正常输出
gateway网关转发请求添加参数
在继承AbstractGatewayFilterFactory的过滤器中
GET请求添加参数
// 参考api文档中GatewapFilter中“添加请求参数 * ”:AddRequestParameterGatewayFilterFactory.java
//记录日志
//logger.info("全局参数处理: {} url:{} 参数:{}",method.toString(),serverHttpRequest.getURI().getRawPath(),newRequestQueryParams.toString());
// 获取原参数
URI uri = serverHttpRequest.getURI();
StringBuilder query = new StringBuilder();
String originalQuery = uri.getRawQuery();
if (org.springframework.util.StringUtils.hasText(originalQuery)) {
query.append(originalQuery);
if (originalQuery.charAt(originalQuery.length() - 1) != '&') {
query.append('&');
}
}
// 添加查询参数
query.append(ServiceConstants.COMMON_PARAMETER_ENTERPRISEID+"="+authenticationVO.getEnterpriseId()
+"&"+ServiceConstants.COMMON_PARAMETER_USERID+"="+authenticationVO.getUserId());
// 替换查询参数
URI newUri = UriComponentsBuilder.fromUri(uri)
.replaceQuery(query.toString())
.build(true)
.toUri();
ServerHttpRequest request = exchange.getRequest().mutate().uri(newUri).build();
return chain.filter(exchange.mutate().request(request).build());
POST请求添加参数
//从请求里获取Post请求体
String bodyStr = resolveBodyFromRequest(serverHttpRequest);
String userId = "123";
// 这种处理方式,必须保证post请求时,原始post表单必须有数据过来,不然会报错
if (StringUtils.isEmpty(bodyStr)) {
logger.error("请求异常:{} POST请求必须传递参数", serverHttpRequest.getURI().getRawPath());
ServerHttpResponse response = exchange.getResponse();
response.setStatusCode(HttpStatus.BAD_REQUEST);
return response.setComplete();
}
//application/x-www-form-urlencoded和application/json才添加参数
//其他上传文件之类的,不做参数处理,因为文件流添加参数,文件原格式就会出问题了
/* if (MediaType.APPLICATION_FORM_URLENCODED_VALUE.equalsIgnoreCase(contentType)) {
// 普通键值对,增加参数
bodyStr = String.format(bodyStr+"&%s=%s&%s=%s",ServiceConstants.COMMON_PARAMETER_ENTERPRISEID,authenticationVO.getEnterpriseId()
,ServiceConstants.COMMON_PARAMETER_USERID,authenticationVO.getUserId());
}*/
// 新增body参数
if (MediaType.APPLICATION_JSON_VALUE.equalsIgnoreCase(contentType)) {
JSONObject jsonObject = new JSONObject(bodyStr);
jsonObject.put("userId", userId);
bodyStr = jsonObject.toString();
}
//记录日志
logger.info("全局参数处理: {} url:{} 参数:{}", method.toString(), serverHttpRequest.getURI().getRawPath(), bodyStr);
//下面的将请求体再次封装写回到request里,传到下一级,否则,由于请求体已被消费,后续的服务将取不到值
URI uri = serverHttpRequest.getURI();
URI newUri = UriComponentsBuilder.fromUri(uri).build(true).toUri();
ServerHttpRequest request = exchange.getRequest().mutate().uri(newUri).build();
DataBuffer bodyDataBuffer = stringBuffer(bodyStr);
Flux<DataBuffer> bodyFlux = Flux.just(bodyDataBuffer);
// 定义新的消息头
HttpHeaders headers = new HttpHeaders();
headers.putAll(exchange.getRequest().getHeaders());
// 添加消息头
// headers.set(ServiceConstants.SHIRO_SESSION_PRINCIPALS,GsonUtils.toJson(authenticationVO));
// 由于修改了传递参数,需要重新设置CONTENT_LENGTH,长度是字节长度,不是字符串长度
int length = bodyStr.getBytes().length;
headers.remove(HttpHeaders.CONTENT_LENGTH);
headers.setContentLength(length);
// 设置CONTENT_TYPE
if (StringUtils.isEmpty(contentType)) {
headers.set(HttpHeaders.CONTENT_TYPE, contentType);
}
// 由于post的body只能订阅一次,由于上面代码中已经订阅过一次body。所以要再次封装请求到request才行,不然会报错请求已经订阅过
request = new ServerHttpRequestDecorator(request) {
@Override
public HttpHeaders getHeaders() {
long contentLength = headers.getContentLength();
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.putAll(super.getHeaders());
if (contentLength > 0) {
httpHeaders.setContentLength(contentLength);
} else {
// TODO: this causes a 'HTTP/1.1 411 Length Required' on httpbin.org
httpHeaders.set(HttpHeaders.TRANSFER_ENCODING, "chunked");
}
return httpHeaders;
}
@Override
public Flux<DataBuffer> getBody() {
return bodyFlux;
}
};
//封装request,传给下一级
request.mutate().header(HttpHeaders.CONTENT_LENGTH, Integer.toString(bodyStr.length()));
return chain.filter(exchange.mutate().request(request).build());
/**
* 从Flux<DataBuffer>中获取字符串的方法
* @return 请求体
*/
private String resolveBodyFromRequest(ServerHttpRequest serverHttpRequest) {
//获取请求体
Flux<DataBuffer> body = serverHttpRequest.getBody();
AtomicReference<String> bodyRef = new AtomicReference<>();
body.subscribe(buffer -> {
CharBuffer charBuffer = StandardCharsets.UTF_8.decode(buffer.asByteBuffer());
DataBufferUtils.release(buffer);
bodyRef.set(charBuffer.toString());
});
//获取request body
return bodyRef.get();
}
/**
* 字符串转DataBuffer
* @param value
* @return
*/
private DataBuffer stringBuffer(String value) {
byte[] bytes = value.getBytes(StandardCharsets.UTF_8);
NettyDataBufferFactory nettyDataBufferFactory = new NettyDataBufferFactory(ByteBufAllocator.DEFAULT);
DataBuffer buffer = nettyDataBufferFactory.allocateBuffer(bytes.length);
buffer.write(bytes);
return buffer;
}
来源:https://blog.csdn.net/weixin_41187876/article/details/103637035


猜你喜欢
- 目录为什么要用Geometry数据做图标?怎么获取Geometry数据?如何使用Geometry数据相信大家在阅读WPF相关GitHub开源
- 前言最近接手的项目里涉及到了 GIF 动图的播放与监听,在上一版本中对于 GIF 的处理是由 H5 来实现的,因为考虑到用户体验,因此现在的
- 异常分类可查的异常(checked exceptions):Exception下除了RuntimeException外的异常不可查的异常(u
- 开始以前,先认识一下WinForm控件数据绑定的两种形式,简单数据绑定和复杂数据绑定。1. 简单的数据绑定例1using (SqlConne
- 具体实现过程请看下面代码:简单的调用了一下系统的拍照功能代码如下所示://拍照的方法 private void openTakePhoto(
- 本文实例讲述了Java基于余弦方法实现的计算相似度算法。分享给大家供大家参考,具体如下:(1)余弦相似性通过测量两个向量之间的角的余弦值来度
- 注册BeanPostProcessorrefresh()调用registerBeanPostProcessors(beanFactory)方
- 先看看效果:用极少的代码实现了 动态详情 及 二级评论 的 数据获取与处理 和 UI显示与交互,并且高解耦、高复用、高灵活。动态列表界面Mo
- 为什么说是常见问题整合呢,因为小编我就是Genymotion模板器最悲剧的使用者,该见过的问题,我基本都见过了,在此总结出这血的教训,望大家
- 请求SpringBoot接受前台参数的六种方式,首先因为从前台发送的请求没有界面的话只能是从地址栏发送并且只能是Get请求,为了测试其他的请
- Android InputAndroid Input指的是输入事件,主要是触摸滑动,当然还包括类似蓝牙外设的输入。Input涉及到的主要模块
- 侧滑菜单在很多应用中都会见到,最近QQ5.0侧滑还玩了点花样~~对于侧滑菜单,一般大家都会自定义ViewGroup,然后隐藏菜单栏,当手指滑
- 责任链模式责任链模式的定义:使多个对象都有机会处理请求,从而避免请求的发送者和接受者之间的耦合关系, 将这个对象连成一条链,并沿着这条链传递
- springboot远程debug调试1.首先去编辑器打开项目2.打开Edit Configurations 选择remote选项
- Android 自定义gradle property在Android studio上运行项目,gradle的配置是必不可少的,但是随着项目的
- 引言在前两篇文章中,我们了解了ReentrantLock内部公平锁和非公平锁的实现原理,可以知道其底层基于AQS,使用双向链表实现,同时在线
- Feign进行调用@FeignClient 找不到通过Feign 进行调用这里配置spring-cloud 版本为 M8的 <
- 前言今天看到某一篇文章的一句话 单例DCL 前面加 V 。就这句话让我把 单例模式 又仔细看了一遍。Java
- 一、简介线程安全概念:线程安全是指在当一个线程访问该类的某个数据时,进行保护,其他线程不能进行访问直到该线程读取完,其他线程才可使用。不会出
- 概述ConcurrentHashMap(CHM)是日常开发中使用频率非常高的一种数据结构,想对于普通的HashMap,CHM提供了线程安全的