电脑知识|欧美黑人一区二区三区|软件|欧美黑人一级爽快片淫片高清|系统|欧美黑人狂野猛交老妇|数据库|服务器|编程开发|网络运营|知识问答|技术教程文章 - 好吧啦网

您的位置:首頁技術文章
文章詳情頁

Spring Cloud Gateway 獲取請求體(Request Body)的多種方法

瀏覽:39日期:2023-07-27 09:10:14

一、直接在全局攔截器中獲取,偽代碼如下

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()); }); return bodyRef.get(); }

存在的缺陷:其他攔截器無法再通過該方式獲取請求體(因為請求體已被消費),并且會拋出異常

Only one connection receive subscriber allowed.Caused by: java.lang.IllegalStateException: Only one connection receive subscriber allowed.

異常原因:實際上spring-cloud-gateway反向代理的原理是,首先讀取原請求的數據,然后構造一個新的請求,將原請求的數據封裝到新的請求中,然后再轉發出去。然而我們在他封裝之前讀取了一次request body,而request body只能讀取一次。因此就出現了上面的錯誤。

再者受版本限制

這種方法在spring-boot-starter-parent 2.0.6.RELEASE + Spring Cloud Finchley.SR2 body 中生效,

但是在spring-boot-starter-parent 2.1.0.RELEASE + Spring Cloud Greenwich.M3 body 中不生效,總是為空

二、先在全局過濾器中獲取,然后再把request重新包裝,繼續向下傳遞傳遞

@Override public GatewayFilter apply(NameValueConfig nameValueConfig) { return (exchange, chain) -> { URI uri = exchange.getRequest().getURI(); URI ex = UriComponentsBuilder.fromUri(uri).build(true).toUri(); ServerHttpRequest request = exchange.getRequest().mutate().uri(ex).build(); if('POST'.equalsIgnoreCase(request.getMethodValue())){//判斷是否為POST請求 Flux<DataBuffer> body = request.getBody(); AtomicReference<String> bodyRef = new AtomicReference<>(); body.subscribe(dataBuffer -> { CharBuffer charBuffer = StandardCharsets.UTF_8.decode(dataBuffer.asByteBuffer()); DataBufferUtils.release(dataBuffer); bodyRef.set(charBuffer.toString()); });//讀取request body到緩存 String bodyStr = bodyRef.get();//獲取request body System.out.println(bodyStr);//這里是我們需要做的操作 DataBuffer bodyDataBuffer = stringBuffer(bodyStr); Flux<DataBuffer> bodyFlux = Flux.just(bodyDataBuffer); request = new ServerHttpRequestDecorator(request){ @Override public Flux<DataBuffer> getBody() { return bodyFlux; } };//封裝我們的request } return chain.filter(exchange.mutate().request(request).build()); }; } protected 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; }

該方案的缺陷:request body獲取不完整(因為異步原因),只能獲取1024B的數據。并且請求體超過1024B,會出現響應超慢(因為我是開啟了熔斷)。

三、過濾器加路線定位器

翻查源碼發現ReadBodyPredicateFactory里面緩存了request body的信息,于是在自定義router中配置了ReadBodyPredicateFactory,然后在filter中通過cachedRequestBodyObject緩存字段獲取request body信息。

/** * @description: 獲取POST請求的請求體 * ReadBodyPredicateFactory 發現里面緩存了request body的信息, * 于是在自定義router中配置了ReadBodyPredicateFactory * @modified: */@EnableAutoConfiguration@Configurationpublic class RouteLocatorRequestBoby{ //自定義過濾器 @Resource private ReqTraceFilter reqTraceFilter; @Resource private RibbonLoadBalancerClient ribbonLoadBalancerClient; private static final String SERVICE = '/leap/**'; private static final String HTTP_PREFIX = 'http://'; private static final String COLON = ':'; @Bean public RouteLocator myRoutes(RouteLocatorBuilder builder) { //通過負載均衡獲取服務實例 ServiceInstance instance = ribbonLoadBalancerClient.choose('PLATFORM-SERVICE'); //拼接路徑 StringBuilder forwardAddress = new StringBuilder(HTTP_PREFIX); forwardAddress.append(instance.getHost()) .append(COLON) .append(instance.getPort()); return builder.routes() //攔截請求類型為POST Content-Type application/json application/json;charset=UTF-8 .route(r -> r.header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE + MediaType.APPLICATION_JSON_UTF8_VALUE).and().method(HttpMethod.POST).and()//獲取緩存中的請求體.readBody(Object.class, readBody -> { return true;}).and().path(SERVICE)//把請求體傳遞給攔截器reqTraceFilter.filters(f -> { f.filter(reqTraceFilter); return f;}).uri(forwardAddress.toString())).build(); } /** * @description: 過濾器,用于獲取請求體,和處理請求體業務,列如記錄日志 * @modified: */@Componentpublic class ReqTraceFilter implements GlobalFilter, GatewayFilter,Ordered { private static final String CONTENT_TYPE = 'Content-Type'; private static final String CONTENT_TYPE_JSON = 'application/json'; //獲取請求路由詳細信息Route route = exchange.getAttribute(GATEWAY_ROUTE_BEAN) private static final String GATEWAY_ROUTE_BEAN = 'org.springframework.cloud.gateway.support.ServerWebExchangeUtils.gatewayRoute'; private static final String CACHE_REQUEST_BODY_OBJECT_KEY = 'cachedRequestBodyObject'; @Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { ServerHttpRequest request = exchange.getRequest(); //判斷過濾器是否執行 String requestUrl = RequestUtils.getCurrentRequest(request); if (!RequestUtils.isFilter(requestUrl)) { String bodyStr = ''; String contentType = request.getHeaders().getFirst(CONTENT_TYPE); String method = request.getMethodValue(); //判斷是否為POST請求 if (null != contentType && HttpMethod.POST.name().equalsIgnoreCase(method) && contentType.contains(CONTENT_TYPE_JSON)) { Object cachedBody = exchange.getAttribute(CACHE_REQUEST_BODY_OBJECT_KEY); if(null != cachedBody){ bodyStr = cachedBody.toString(); } } if (HttpMethod.GET.name().equalsIgnoreCase(method)) { bodyStr = request.getQueryParams().toString(); } log.info('請求體內容:{}',bodyStr); } return chain.filter(exchange); } @Override public int getOrder() { return 5; }}

該方案優點:這種解決,一不會帶來重復讀取問題,二不會帶來requestbody取不全問題。三在低版本的Spring Cloud Finchley.SR2也可以運行。

缺點:不支持multipart/form-data(異常415),這個致命。

四、通過org.springframework.cloud.gateway.filter.factory.rewrite包下有個ModifyRequestBodyGatewayFilterFactory,顧名思義,這就是修改 Request Body 的過濾器工廠類。

@Component@Slf4jpublic class ReqTraceFilter implements GlobalFilter, GatewayFilter, Ordered { @Resource private IPlatformFeignClient platformFeignClient; /** * httpheader,traceId的key名稱 */ private static final String REQUESTID = 'traceId'; private static final String CONTENT_TYPE = 'Content-Type'; private static final String CONTENT_TYPE_JSON = 'application/json'; private static final String GATEWAY_ROUTE_BEAN = 'org.springframework.cloud.gateway.support.ServerWebExchangeUtils.gatewayRoute'; @Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { ServerHttpRequest request = exchange.getRequest(); //判斷過濾器是否執行 String requestUrl = RequestUtils.getCurrentRequest(request); if (!RequestUtils.isFilter(requestUrl)) { String bodyStr = ''; String contentType = request.getHeaders().getFirst(CONTENT_TYPE); String method = request.getMethodValue(); //判斷是否為POST請求 if (null != contentType && HttpMethod.POST.name().equalsIgnoreCase(method) && contentType.contains(CONTENT_TYPE_JSON)) { ServerRequest serverRequest = new DefaultServerRequest(exchange); List<String> list = new ArrayList<>(); // 讀取請求體 Mono<String> modifiedBody = serverRequest.bodyToMono(String.class) .flatMap(body -> { //記錄請求體日志 final String nId = saveRequestOperLog(exchange, body); //記錄日志id list.add(nId); return Mono.just(body); }); BodyInserter bodyInserter = BodyInserters.fromPublisher(modifiedBody, String.class); HttpHeaders headers = new HttpHeaders(); headers.putAll(exchange.getRequest().getHeaders()); headers.remove(HttpHeaders.CONTENT_LENGTH); CachedBodyOutputMessage outputMessage = new CachedBodyOutputMessage(exchange, headers); return bodyInserter.insert(outputMessage, new BodyInserterContext()) .then(Mono.defer(() -> { ServerHttpRequestDecorator decorator = new ServerHttpRequestDecorator( exchange.getRequest()) {@Overridepublic HttpHeaders getHeaders() { long contentLength = headers.getContentLength(); HttpHeaders httpHeaders = new HttpHeaders(); httpHeaders.putAll(super.getHeaders()); httpHeaders.put(REQUESTID,list); if (contentLength > 0) { httpHeaders.setContentLength(contentLength); } else { httpHeaders.set(HttpHeaders.TRANSFER_ENCODING, 'chunked'); } return httpHeaders;} @Overridepublic Flux<DataBuffer> getBody() { return outputMessage.getBody();} };return chain.filter(exchange.mutate().request(decorator).build()); })); } if (HttpMethod.GET.name().equalsIgnoreCase(method)) { bodyStr = request.getQueryParams().toString(); String nId = saveRequestOperLog(exchange, bodyStr); ServerHttpRequest userInfo = exchange.getRequest().mutate() .header(REQUESTID, nId).build(); return chain.filter(exchange.mutate().request(userInfo).build()); } } return chain.filter(exchange); } /** * 保存請求日志 * * @param exchange * @param requestParameters * @return */ private String saveRequestOperLog(ServerWebExchange exchange, String requestParameters) { log.debug('接口請求參數:{}', requestParameters); ServerHttpRequest request = exchange.getRequest(); String ip = Objects.requireNonNull(request.getRemoteAddress()).getAddress().getHostAddress(); SaveOperLogVO vo = new SaveOperLogVO(); vo.setIp(ip); vo.setReqUrl(RequestUtils.getCurrentRequest(request)); vo.setReqMethod(request.getMethodValue()); vo.setRequestParameters(requestParameters); Route route = exchange.getAttribute(GATEWAY_ROUTE_BEAN); //是否配置路由 if (route != null) { vo.setSubsystem(route.getId()); } ResEntity<String> res = platformFeignClient.saveOperLog(vo); log.debug('當前請求ID返回的數據:{}', res); return res.getData(); } @Override public int getOrder() { return 5; }}

該方案:完美解決以上所有問題

參考文檔:https://www.codercto.com/a/52970.html

到此這篇關于Spring Cloud Gateway 獲取請求體(Request Body)的多種方法的文章就介紹到這了,更多相關Spring Cloud Gateway 獲取請求體內容請搜索好吧啦網以前的文章或繼續瀏覽下面的相關文章希望大家以后多多支持好吧啦網!

標簽: Spring
相關文章:
主站蜘蛛池模板: 合肥白癜风医院_合肥治疗白癜风医院_合肥看白癜风医院哪家好_合肥华研白癜风医院 | 登车桥动力单元-非标液压泵站-非标液压系统-深圳市三好科技有限公司 | 富森高压水枪-柴油驱动-养殖场高压清洗机-山东龙腾环保科技有限公司 | 品牌策划-品牌设计-济南之式传媒广告有限公司官网-提供品牌整合丨影视创意丨公关活动丨数字营销丨自媒体运营丨数字营销 | 防爆型气象站_农业气象站_校园气象站_农业四情监测系统「山东万象环境科技有限公司」 | 直流电能表-充电桩电能表-导轨式电能表-智能电能表-浙江科为电气有限公司 | 定制防伪标签_防伪标签印刷_防伪标签厂家-510品保防伪网 | 电线电缆厂家|沈阳电缆厂|电线厂|沈阳英联塑力线缆有限公司 | 实战IT培训机构_IT培训班选大学生IT技术培训中心_中公优就业 | 烟雾净化器-滤筒除尘器-防爆除尘器-除尘器厂家-东莞执信环保科技有限公司 | 暴风影音| 聚氨酯催化剂K15,延迟催化剂SA-1,叔胺延迟催化剂,DBU,二甲基哌嗪,催化剂TMR-2,-聚氨酯催化剂生产厂家 | 建筑资质代办-建筑资质转让找上海国信启航| 农业四情_农业气象站_田间小型气象站_智慧农业气象站-山东风途物联网 | 外观设计_设备外观设计_外观设计公司_产品外观设计_机械设备外观设计_东莞工业设计公司-意品深蓝 | 皮带机-带式输送机价格-固定式胶带机生产厂家-河南坤威机械 | 超声波焊接机,振动摩擦焊接机,激光塑料焊接机,超声波焊接模具工装-德召尼克(常州)焊接科技有限公司 | 电镀标牌_电铸标牌_金属标贴_不锈钢标牌厂家_深圳市宝利丰精密科技有限公司 | 北京公寓出租网-北京酒店式公寓出租平台| SF6环境监测系统-接地环流在线监测装置-瑟恩实业 | 北京易通慧公司从事北京网站优化,北京网络推广、网站建设一站式服务商-北京网站优化公司 | 钛合金标准件-钛合金螺丝-钛管件-钛合金棒-钛合金板-钛合金锻件-宝鸡远航钛业有限公司 | 激光内雕_led玻璃_发光玻璃_内雕玻璃_导光玻璃-石家庄明晨三维科技有限公司 激光内雕-内雕玻璃-发光玻璃 | 旅游规划_旅游策划_乡村旅游规划_景区规划设计_旅游规划设计公司-北京绿道联合旅游规划设计有限公司 | 百度关键词优化_网站优化_SEO价格 - 云无限好排名 | 铝机箱_铝外壳加工_铝外壳厂家_CNC散热器加工-惠州市铂源五金制品有限公司 | 校园文化空间设计-数字化|中医文化空间设计-党建|法治廉政主题文化空间施工-山东锐尚文化传播公司 | 气力输送_输送机械_自动化配料系统_负压吸送_制造主力军江苏高达智能装备有限公司! | 悬浮拼装地板_篮球场木地板翻新_运动木地板价格-上海越禾运动地板厂家 | 广州云仓代发-昊哥云仓专业电商仓储托管外包代发货服务 | 长沙中央空调维修,中央空调清洗维保,空气能热水工程,价格,公司就找维小保-湖南维小保环保科技有限公司 | 东莞画册设计_logo/vi设计_品牌包装设计 - 华略品牌设计公司 | 三效蒸发器_多效蒸发器价格_四效三效蒸发器厂家-青岛康景辉 | 办公室家具_板式办公家具定制厂家-FMARTS福玛仕办公家具 | 车牌识别道闸_停车场收费系统_人脸识别考勤机_速通门闸机_充电桩厂家_中全清茂官网 | 酒万铺-酒水招商-酒水代理| 出国劳务公司_正规派遣公司[严海]| 培训中心-海南香蕉蛋糕加盟店技术翰香原中心官网总部 | 茅茅虫AI论文写作助手-免费AIGC论文查重_写毕业论文降重 | 南京精锋制刀有限公司-纵剪机刀片_滚剪机刀片_合金刀片厂家 | 交变/复合盐雾试验箱-高低温冲击试验箱_安奈设备产品供应杭州/江苏南京/安徽马鞍山合肥等全国各地 |