日本不卡不码高清免费观看,久久国产精品久久w女人spa,黄色aa久久,三上悠亚国产精品一区二区三区

您的位置:首頁技術(shù)文章
文章詳情頁

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

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

一、直接在全局?jǐn)r截器中獲取,偽代碼如下

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

存在的缺陷:其他攔截器無法再通過該方式獲取請(qǐng)求體(因?yàn)檎?qǐng)求體已被消費(fèi)),并且會(huì)拋出異常

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

異常原因:實(shí)際上spring-cloud-gateway反向代理的原理是,首先讀取原請(qǐng)求的數(shù)據(jù),然后構(gòu)造一個(gè)新的請(qǐng)求,將原請(qǐng)求的數(shù)據(jù)封裝到新的請(qǐng)求中,然后再轉(zhuǎn)發(fā)出去。然而我們?cè)谒庋b之前讀取了一次request body,而request body只能讀取一次。因此就出現(xiàn)了上面的錯(cuò)誤。

再者受版本限制

這種方法在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重新包裝,繼續(xù)向下傳遞傳遞

@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請(qǐng)求 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獲取不完整(因?yàn)楫惒皆颍?,只能獲取1024B的數(shù)據(jù)。并且請(qǐng)求體超過1024B,會(huì)出現(xiàn)響應(yīng)超慢(因?yàn)槲沂情_啟了熔斷)。

三、過濾器加路線定位器

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

/** * @description: 獲取POST請(qǐng)求的請(qǐng)求體 * ReadBodyPredicateFactory 發(fā)現(xiàn)里面緩存了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) { //通過負(fù)載均衡獲取服務(wù)實(shí)例 ServiceInstance instance = ribbonLoadBalancerClient.choose('PLATFORM-SERVICE'); //拼接路徑 StringBuilder forwardAddress = new StringBuilder(HTTP_PREFIX); forwardAddress.append(instance.getHost()) .append(COLON) .append(instance.getPort()); return builder.routes() //攔截請(qǐng)求類型為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()//獲取緩存中的請(qǐng)求體.readBody(Object.class, readBody -> { return true;}).and().path(SERVICE)//把請(qǐng)求體傳遞給攔截器reqTraceFilter.filters(f -> { f.filter(reqTraceFilter); return f;}).uri(forwardAddress.toString())).build(); } /** * @description: 過濾器,用于獲取請(qǐng)求體,和處理請(qǐng)求體業(yè)務(wù),列如記錄日志 * @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'; //獲取請(qǐng)求路由詳細(xì)信息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(); //判斷過濾器是否執(zhí)行 String requestUrl = RequestUtils.getCurrentRequest(request); if (!RequestUtils.isFilter(requestUrl)) { String bodyStr = ''; String contentType = request.getHeaders().getFirst(CONTENT_TYPE); String method = request.getMethodValue(); //判斷是否為POST請(qǐng)求 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('請(qǐng)求體內(nèi)容:{}',bodyStr); } return chain.filter(exchange); } @Override public int getOrder() { return 5; }}

該方案優(yōu)點(diǎn):這種解決,一不會(huì)帶來重復(fù)讀取問題,二不會(huì)帶來requestbody取不全問題。三在低版本的Spring Cloud Finchley.SR2也可以運(yùn)行。

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

四、通過org.springframework.cloud.gateway.filter.factory.rewrite包下有個(gè)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(); //判斷過濾器是否執(zhí)行 String requestUrl = RequestUtils.getCurrentRequest(request); if (!RequestUtils.isFilter(requestUrl)) { String bodyStr = ''; String contentType = request.getHeaders().getFirst(CONTENT_TYPE); String method = request.getMethodValue(); //判斷是否為POST請(qǐng)求 if (null != contentType && HttpMethod.POST.name().equalsIgnoreCase(method) && contentType.contains(CONTENT_TYPE_JSON)) { ServerRequest serverRequest = new DefaultServerRequest(exchange); List<String> list = new ArrayList<>(); // 讀取請(qǐng)求體 Mono<String> modifiedBody = serverRequest.bodyToMono(String.class) .flatMap(body -> { //記錄請(qǐng)求體日志 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); } /** * 保存請(qǐng)求日志 * * @param exchange * @param requestParameters * @return */ private String saveRequestOperLog(ServerWebExchange exchange, String requestParameters) { log.debug('接口請(qǐng)求參數(shù):{}', 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('當(dāng)前請(qǐng)求ID返回的數(shù)據(jù):{}', res); return res.getData(); } @Override public int getOrder() { return 5; }}

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

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

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

標(biāo)簽: Spring
相關(guān)文章:
日本不卡不码高清免费观看,久久国产精品久久w女人spa,黄色aa久久,三上悠亚国产精品一区二区三区
午夜性色一区二区三区免费视频| 国产精品一区二区三区美女| 亚洲精品成人图区| 欧美在线亚洲综合一区| 国产成人精品一区二区三区视频 | 欧美1区2区3区| 午夜欧美巨大性欧美巨大| 中文字幕成人| 91免费精品国偷自产在线在线| 色综合视频一区二区三区日韩 | 日韩一区二区三区免费视频| 亚洲影院天堂中文av色| 亚洲精品乱码久久久久久蜜桃麻豆 | 亚洲最新av| 一本一道久久a久久| 四虎精品永久免费| 日韩成人午夜精品| 欧美日本不卡| 青青草伊人久久| 国产精品久久久久久久久久齐齐| 国产精品日本一区二区不卡视频| 国产精选一区| 国产精品66| 日韩成人精品一区| 欧美久久天堂| se01亚洲视频 | 日韩欧美午夜| 九一成人免费视频| 亚洲深深色噜噜狠狠爱网站| 日韩激情网站| 久久伊人亚洲| 蜜臀久久精品| 免费中文字幕日韩欧美| 日韩精品久久久久久| 精品免费在线| 狠狠色综合网| 日韩精品久久理论片| 国际精品欧美精品| 欧美日韩在线观看视频小说| 亚洲手机视频| 日韩高清一区二区| 精品精品99| 午夜欧美精品| 日韩黄色av| 女生影院久久| 久久av在线| 美女在线视频一区| 久久久夜夜夜| 少妇精品久久久一区二区三区| 国产精品亚洲人成在99www| 日韩电影在线视频| 日韩精品一级二级 | 国产精品亚洲四区在线观看| 成人福利视频| 日韩精品社区| 成人日韩在线观看| 深夜日韩欧美| 欧美三级网址| 亚洲丝袜啪啪| 国产一区二区三区精品在线观看| 亚洲精品午夜av福利久久蜜桃| 中文字幕亚洲影视| 超级白嫩亚洲国产第一| 蜜臀国产一区二区三区在线播放 | 精品淫伦v久久水蜜桃| 天堂日韩电影| 日韩高清三区| 日韩精品免费一区二区三区| 亚州av日韩av| 蜜桃精品在线| 国产精品久久久久久久久久齐齐 | 视频在线观看一区二区三区| 亚洲激情五月| 国产精品调教视频| 亚洲一区久久| 日韩一区自拍| 久久激情av| 好吊视频一区二区三区四区| 国产精品高清一区二区| 久热re这里精品视频在线6| 精品三级av| 亚洲日产国产精品| 亚洲手机在线| 国产成人精品一区二区三区免费 | 国产精品久久久久久久免费软件| 亚洲高清不卡| 粉嫩av一区二区三区四区五区 | 男女男精品视频网| 国产精品精品国产一区二区| 日本不卡视频在线观看 | 成人羞羞在线观看网站| 亚洲精品麻豆| 久久久影院免费| 欧美91在线|欧美| 免费黄网站欧美| 欧美日韩亚洲在线观看| 亚洲黄色免费av| 欧美激情视频一区二区三区免费| 蜜桃久久精品一区二区| 亚洲精品97| 日韩一区二区三区在线免费观看| 国产精品一区二区99| 亚洲一区有码| 制服诱惑一区二区| 久久裸体视频| 国产欧洲在线| 久久精品99国产精品日本| 影音国产精品| 精品亚洲美女网站| 国产成人免费| 国产精品伦一区二区| 亚洲精品欧美| 午夜亚洲福利在线老司机| 久久精品观看| 色婷婷久久久| 天堂√中文最新版在线| 美女尤物国产一区| 国产区精品区| 欧美日一区二区在线观看| 日韩中文欧美在线| 久久夜色精品| 午夜在线视频观看日韩17c| 亚洲福利国产| 美女毛片一区二区三区四区| 色婷婷精品视频| 亚洲天堂成人| 九一精品国产| 麻豆亚洲精品| 蜜臀va亚洲va欧美va天堂 | 国产欧美激情| 国产精品chinese| 国产精品s色| 精品视频在线你懂得| 国产精品99精品一区二区三区∴ | 日韩一区精品视频| 中文视频一区| 日本不卡中文字幕| 欧美日韩一区二区三区四区在线观看| 日韩国产在线观看| 国产精品天天看天天狠| 美女视频黄 久久| 超级白嫩亚洲国产第一| 久久国产精品成人免费观看的软件| 91精品婷婷色在线观看| 亚洲午夜一级| 国产综合精品| 亚洲视频www| 久久国产精品99国产| 亚洲精品乱码久久久久久蜜桃麻豆| 日韩不卡在线观看日韩不卡视频| 国产区精品区| 韩国久久久久久| 免费精品国产| 亚洲小说欧美另类婷婷| 美女精品在线| 日本电影久久久| 精品日产乱码久久久久久仙踪林| 日韩深夜视频| 国产亚洲永久域名| 日韩欧美另类中文字幕| 97久久亚洲| 精品五月天堂| 在线日韩电影| 日韩精品亚洲一区二区三区免费| 国产精品久久久久77777丨 | 国产在线不卡一区二区三区| 日韩久久电影| 亚洲狼人精品一区二区三区| 国产午夜久久av| 四虎成人av| 免费日韩一区二区| 日韩黄色在线观看| 国产精品片aa在线观看| 国精品产品一区| 免费精品国产| 日本国产欧美| 久久婷婷亚洲| 色婷婷成人网| 国产调教精品| 欧美色图国产精品| 91av一区| 欧美日韩免费观看视频| 在线精品福利| 激情国产在线| 男女性色大片免费观看一区二区 | 国产一区二区三区国产精品| 日韩精品免费一区二区三区| 亚洲精品进入| 国产一区视频在线观看免费| 国产乱论精品| 欧美一级一区| 五月婷婷亚洲| 精品91福利视频| 蜜桃视频第一区免费观看| 岛国av在线网站| 欧美视频二区| 99日韩精品| zzzwww在线看片免费| 久久国产三级| 午夜一级久久|