热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

springcloudgateway读取requestbody数据

2019独角兽企业重金招聘Python工程师标准springcloudgateway为了记录访问记录,需要记录请求体里面的内容,但是requestbody是只能读取一次的,如

2019独角兽企业重金招聘Python工程师标准>>> hot3.png

spring cloud gateway为了记录访问记录,需要记录请求体里面的内容,但是 request body是只能读取一次的,如果读取以后不封装回去,则会造成后面的服务无法读取body数据. 在网关里添加一个过滤器RequestRecordFilter类:

@Slf4j
@Component
public class RequestRecordFilter implements GlobalFilter, Ordered {@Overridepublic Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) {ServerHttpRequest request = exchange.getRequest();URI requestUri = request.getURI();//只记录 http 请求(包含 https)String schema = requestUri.getScheme();if ((!"http".equals(schema) && !"https".equals(schema))){return chain.filter(exchange);}AccessRecord accessRecord = new AccessRecord();accessRecord.setPath(requestUri.getPath());accessRecord.setQueryString(request.getQueryParams());exchange.getAttributes().put("startTime", System.currentTimeMillis());String method = request.getMethodValue();String contentType = request.getHeaders().getFirst("Content-Type");//此处要排除流文件类型,比如上传的文件if ("POST".equals(method) && !contentType.startsWith("multipart/form-data")){String bodyStr = resolveBodyFromRequest(request);//下面将请求体再次封装写回到 request 里,传到下一级.URI ex = UriComponentsBuilder.fromUri(requestUri).build(true).toUri();ServerHttpRequest newRequest = request.mutate().uri(ex).build();DataBuffer bodyDataBuffer = stringBuffer(bodyStr);Flux bodyFlux = Flux.just(bodyDataBuffer);newRequest = new ServerHttpRequestDecorator(newRequest) {@Overridepublic Flux getBody() {return bodyFlux;}};accessRecord.setBody(formatStr(bodyStr));ServerWebExchange newExchange = exchange.mutate().request(newRequest).build();return returnMono(chain, newExchange, accessRecord);} else {return returnMono(chain, exchange, accessRecord);}}private Mono returnMono(GatewayFilterChain chain,ServerWebExchange exchange, AccessRecord accessRecord){return chain.filter(exchange).then(Mono.fromRunnable(()->{Long startTime = exchange.getAttribute("startTime");if (startTime != null){long executeTime = (System.currentTimeMillis() - startTime);accessRecord.setExpendTime(executeTime);accessRecord.setHttpCode(Objects.requireNonNull(exchange.getResponse().getStatusCode()).value());writeAccessLog(JSON.toJSONString(accessRecord) + "\r\n");}}));}@Overridepublic int getOrder() {return 1;}/*** 获取请求体中的字符串内容* @param serverHttpRequest* @return*/private String resolveBodyFromRequest(ServerHttpRequest serverHttpRequest){//获取请求体Flux body = serverHttpRequest.getBody();StringBuilder sb = new StringBuilder();body.subscribe(buffer -> {byte[] bytes = new byte[buffer.readableByteCount()];buffer.read(bytes);DataBufferUtils.release(buffer);String bodyString = new String(bytes, StandardCharsets.UTF_8);sb.append(bodyString);});return sb.toString();}/*** 去掉空格,换行和制表符* @param str* @return*/private String formatStr(String str){if (str != null && str.length() > 0) {Pattern p = Pattern.compile("\\s*|\t|\r|\n");Matcher m = p.matcher(str);return m.replaceAll("");}return str;}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;}/*** 访问记录对象*/@Dataprivate class AccessRecord{private String path;private String body;private MultiValueMap queryString;private long expendTime;private int httpCode;}private void writeAccessLog(String str){File file = new File("access.log");if (!file.exists()){try {if (file.createNewFile()){file.setWritable(true);}} catch (IOException e) {log.error("创建访问日志文件失败.{}",e.getMessage(),e);}}try(FileWriter fileWriter = new FileWriter(file.getName(),true)){fileWriter.write(str);} catch (IOException e) {log.error("写访问日志到文件失败. {}", e.getMessage(),e);}}
}

网上有个获取 body的写法, 但是这种写法对请求体的字符串长度有限制,稍微长一点, 就会转换不完整,方法如下:

private String resolveBodyFromRequest(ServerHttpRequest serverHttpRequest) {//获取请求体Flux body &#61; serverHttpRequest.getBody();AtomicReference bodyRef &#61; new AtomicReference<>();body.subscribe(buffer -> {CharBuffer charBuffer &#61; StandardCharsets.UTF_8.decode(buffer.asByteBuffer());DataBufferUtils.release(buffer);bodyRef.set(charBuffer.toString());});//获取request bodyreturn bodyRef.get();}

对于出现 Only one connection receive subscriber allowed&#xff0c;或者 response is set之类的信息&#xff0c;这个是springboot2.0.5之后的一个bug&#xff0c;需要在类里面添加以下方法去实现&#xff08;这个是springboot开发成员提供的方法&#xff09;&#xff1a;

&#64;Beanpublic HiddenHttpMethodFilter hiddenHttpMethodFilter() {return new HiddenHttpMethodFilter() {&#64;Overridepublic Mono filter(ServerWebExchange exchange, WebFilterChain chain) {return chain.filter(exchange);}};}


转:https://my.oschina.net/zcqshine/blog/2875060



推荐阅读
  • 向QTextEdit拖放文件的方法及实现步骤
    本文介绍了在使用QTextEdit时如何实现拖放文件的功能,包括相关的方法和实现步骤。通过重写dragEnterEvent和dropEvent函数,并结合QMimeData和QUrl等类,可以轻松实现向QTextEdit拖放文件的功能。详细的代码实现和说明可以参考本文提供的示例代码。 ... [详细]
  • 原文地址:https:www.cnblogs.combaoyipSpringBoot_YML.html1.在springboot中,有两种配置文件,一种 ... [详细]
  • XML介绍与使用的概述及标签规则
    本文介绍了XML的基本概念和用途,包括XML的可扩展性和标签的自定义特性。同时还详细解释了XML标签的规则,包括标签的尖括号和合法标识符的组成,标签必须成对出现的原则以及特殊标签的使用方法。通过本文的阅读,读者可以对XML的基本知识有一个全面的了解。 ... [详细]
  • JavaWeb中读取文件资源的路径问题及解决方法
    在JavaWeb开发中,读取文件资源的路径是一个常见的问题。本文介绍了使用绝对路径和相对路径两种方法来解决这个问题,并给出了相应的代码示例。同时,还讨论了使用绝对路径的优缺点,以及如何正确使用相对路径来读取文件。通过本文的学习,读者可以掌握在JavaWeb中正确找到和读取文件资源的方法。 ... [详细]
  • 本文介绍了网页播放视频的三种实现方式,分别是使用html5的video标签、使用flash来播放以及使用object标签。其中,推荐使用html5的video标签来简单播放视频,但有些老的浏览器不支持html5。另外,还可以使用flash来播放视频,需要使用object标签。 ... [详细]
  • Request对象和Response对象request:(请求)当一个页面被请求时,Django就会创建一个包含本次请求原信息的HttpRequest对象。Djang ... [详细]
  • 这是原文链接:sendingformdata许多情况下,我们使用表单发送数据到服务器。服务器处理数据并返回响应给用户。这看起来很简单,但是 ... [详细]
  • 本文讨论了一个关于cuowu类的问题,作者在使用cuowu类时遇到了错误提示和使用AdjustmentListener的问题。文章提供了16个解决方案,并给出了两个可能导致错误的原因。 ... [详细]
  • 本文介绍了PhysioNet网站提供的生理信号处理工具箱WFDB Toolbox for Matlab的安装和使用方法。通过下载并添加到Matlab路径中或直接在Matlab中输入相关内容,即可完成安装。该工具箱提供了一系列函数,可以方便地处理生理信号数据。详细的安装和使用方法可以参考本文内容。 ... [详细]
  • 开发笔记:Java是如何读取和写入浏览器Cookies的
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了Java是如何读取和写入浏览器Cookies的相关的知识,希望对你有一定的参考价值。首先我 ... [详细]
  • 使用正则表达式爬取36Kr网站首页新闻的操作步骤和代码示例
    本文介绍了使用正则表达式来爬取36Kr网站首页所有新闻的操作步骤和代码示例。通过访问网站、查找关键词、编写代码等步骤,可以获取到网站首页的新闻数据。代码示例使用Python编写,并使用正则表达式来提取所需的数据。详细的操作步骤和代码示例可以参考本文内容。 ... [详细]
  • 深入理解Kafka服务端请求队列中请求的处理
    本文深入分析了Kafka服务端请求队列中请求的处理过程,详细介绍了请求的封装和放入请求队列的过程,以及处理请求的线程池的创建和容量设置。通过场景分析、图示说明和源码分析,帮助读者更好地理解Kafka服务端的工作原理。 ... [详细]
  • 在springmvc框架中,前台ajax调用方法,对图片批量下载,如何弹出提示保存位置选框?Controller方法 ... [详细]
  • Imtryingtofigureoutawaytogeneratetorrentfilesfromabucket,usingtheAWSSDKforGo.我正 ... [详细]
  • 解决nginx启动报错epoll_wait() reported that client prematurely closed connection的方法
    本文介绍了解决nginx启动报错epoll_wait() reported that client prematurely closed connection的方法,包括检查location配置是否正确、pass_proxy是否需要加“/”等。同时,还介绍了修改nginx的error.log日志级别为debug,以便查看详细日志信息。 ... [详细]
author-avatar
万花_筒乙姬睦美
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有