spring cloud gateway中如何读取请求参数


Posted in Java/Android onJuly 15, 2021

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. 测试输出,我这边测试没有问题,日志正常输出

spring cloud gateway中如何读取请求参数

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;
}

以上为个人经验,希望能给大家一个参考,也希望大家多多支持三水点靠木。

Java/Android 相关文章推荐
Java多条件判断场景中规则执行器的设计
Jun 26 Java/Android
分析JVM源码之Thread.interrupt系统级别线程打断
Jun 29 Java/Android
java基础——多线程
Jul 03 Java/Android
dubbo服务整合zipkin详解
Jul 26 Java/Android
关于ObjectUtils.isEmpty() 和 null 的区别
Feb 28 Java/Android
解析探秘fescar分布式事务实现原理
Feb 28 Java/Android
MyBatis配置文件解析与MyBatis实例演示
Apr 07 Java/Android
Android Flutter实现图片滑动切换效果
Apr 07 Java/Android
Spring IOC容器Bean的作用域及生命周期实例
May 30 Java/Android
httpclient调用远程接口的方法
Aug 14 Java/Android
基于Android10渲染Surface的创建过程
Aug 14 Java/Android
JDK8中String的intern()方法实例详细解读
Sep 23 Java/Android
Spring Cloud Gateway去掉url前缀
Jul 15 #Java/Android
小程序与后端Java接口交互实现HelloWorld入门
Java生成读取条形码和二维码的简单示例
Jul 09 #Java/Android
详细了解java监听器和过滤器
Jul 09 #Java/Android
Java使用jmeter进行压力测试
java解析XML详解
使用@Value值注入及配置文件组件扫描
Jul 09 #Java/Android
You might like
剧场版动画《PSYCHO-PASS 3 FIRST INSPECTOR》3月27日日本上映!
2020/03/06 日漫
推荐个功能齐全的发送PHP邮件类
2007/01/03 PHP
基于empty函数的输出详解
2013/06/17 PHP
Laravel中使用FormRequest进行表单验证方法及问题汇总
2016/06/19 PHP
PHP魔术方法之__call与__callStatic使用方法
2017/07/23 PHP
Laravel 5.4因特殊字段太长导致migrations报错的解决
2017/10/22 PHP
原来Jquery.load的方法可以一直load下去
2011/03/28 Javascript
js点击更换背景颜色或图片的实例代码
2013/06/25 Javascript
JS 页面计时器示例代码
2013/10/28 Javascript
Jquery.Form 异步提交表单的简单实例
2014/03/03 Javascript
用jquery仿做发微博功能示例
2014/04/18 Javascript
Node.js(安装,启动,测试)
2014/06/09 Javascript
JS实现单行文字不间断向上滚动的方法
2015/01/29 Javascript
jQuery Mobile框架中的表单组件基础使用教程
2016/05/17 Javascript
jquery移除了live()、die(),新版事件绑定on()、off()的方法
2016/10/26 Javascript
javascript实现根据函数名称字符串动态执行函数的方法示例
2016/12/28 Javascript
BootStrap daterangepicker 双日历控件
2017/06/02 Javascript
Vue学习笔记进阶篇之vue-cli安装及介绍
2017/07/18 Javascript
JS实现页面内跳转的简单代码
2017/09/03 Javascript
vue利用better-scroll实现轮播图与页面滚动详解
2017/10/20 Javascript
Angular6 发送手机验证码按钮倒计时效果实现方法
2019/01/08 Javascript
vue项目中将element-ui table表格写成组件的实现代码
2019/06/12 Javascript
使用layui日期控件laydate对开始和结束时间进行联动控制的方法
2019/09/06 Javascript
JavaScript中的相等操作符使用详解
2019/12/21 Javascript
原生js中运算符及流程控制示例详解
2021/01/05 Javascript
[01:25:09]2014 DOTA2国际邀请赛中国区预选赛 5 23 CIS VS DT第二场
2014/05/24 DOTA
剖析Django中模版标签的解析与参数传递
2015/07/21 Python
Python学生成绩管理系统简洁版
2020/04/05 Python
解决pyCharm中 module 调用失败的问题
2020/02/12 Python
使用CSS3实现input多选框自定义样式的方法示例
2019/07/19 HTML / CSS
大学生的网络创业计划书
2013/12/26 职场文书
学校先进集体事迹材料
2014/05/31 职场文书
授权收款委托书
2014/09/23 职场文书
2016七夕情人节感言
2015/12/09 职场文书
远程教育集中轮训基层干部培训班学习心得体会
2016/01/09 职场文书
win10双系统怎么删除一个系统?win10电脑有两个系统删除一个的操作方法
2022/07/15 数码科技