最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

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

 更新時(shí)間:2021年01月05日 14:23:51   作者:碼農(nóng)的進(jìn)擊  
這篇文章主要介紹了Spring Cloud Gateway 獲取請(qǐng)求體(Request Body)的多種方法,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下

一、直接在全局?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();
 
 }

存在的缺陷:其他攔截器無(wú)法再通過(guò)該方式獲取請(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 中不生效,總是為空

二、先在全局過(guò)濾器中獲取,然后再把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)求體超過(guò)1024B,會(huì)出現(xiàn)響應(yīng)超慢(因?yàn)槲沂情_(kāi)啟了熔斷)。

三、過(guò)濾器加路線定位器

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

/**
 * @description: 獲取POST請(qǐng)求的請(qǐng)求體
 * ReadBodyPredicateFactory 發(fā)現(xiàn)里面緩存了request body的信息,
 * 于是在自定義router中配置了ReadBodyPredicateFactory
 * @modified:
 */
@EnableAutoConfiguration
@Configuration
public class RouteLocatorRequestBoby{
   //自定義過(guò)濾器
 @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) {
  //通過(guò)負(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)求類(lèi)型為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: 過(guò)濾器,用于獲取請(qǐng)求體,和處理請(qǐng)求體業(yè)務(wù),列如記錄日志
 * @modified:
 */
@Component
public 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();
  //判斷過(guò)濾器是否執(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ì)帶來(lái)重復(fù)讀取問(wèn)題,二不會(huì)帶來(lái)requestbody取不全問(wèn)題。三在低版本的Spring Cloud Finchley.SR2也可以運(yùn)行。

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

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

@Component
@Slf4j
public class ReqTraceFilter implements GlobalFilter, GatewayFilter, Ordered {
 
 @Resource
 private IPlatformFeignClient platformFeignClient;
 
 /**
  * httpheader,traceId的key名稱(chēng)
  */
 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();
  //判斷過(guò)濾器是否執(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()) {
        @Override
        public 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;
        }
 
        @Override
        public 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;
 }
}

該方案:完美解決以上所有問(wèn)題

參考文檔: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)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Java Socket編程(二) Java面向連接的類(lèi)

    Java Socket編程(二) Java面向連接的類(lèi)

    Java Socket編程(二) Java面向連接的類(lèi)...
    2006-12-12
  • Springboot GET和POST請(qǐng)求參數(shù)獲取方式小結(jié)

    Springboot GET和POST請(qǐng)求參數(shù)獲取方式小結(jié)

    Spring Boot GET和POST請(qǐng)求參數(shù)獲取是開(kāi)發(fā)人員經(jīng)常需要解決的問(wèn)題,本文主要介紹了Springboot GET和POST請(qǐng)求參數(shù)獲取方式小結(jié),具有一定的參考價(jià)值,感興趣的可以了解一下
    2023-09-09
  • spring?webClient配置及使用簡(jiǎn)單代碼示例

    spring?webClient配置及使用簡(jiǎn)單代碼示例

    WebClient是Spring框架5.0引入的基于響應(yīng)式編程模型的HTTP客戶端,它提供一種簡(jiǎn)便的方式來(lái)處理HTTP請(qǐng)求和響應(yīng),支持異步和非阻塞式的請(qǐng)求和響應(yīng)處理,下面這篇文章主要給大家介紹了關(guān)于spring?webClient配置及使用的相關(guān)資料,需要的朋友可以參考下
    2024-03-03
  • java實(shí)現(xiàn)python session功能代碼實(shí)例

    java實(shí)現(xiàn)python session功能代碼實(shí)例

    這篇文章主要介紹了java實(shí)現(xiàn)python session功能代碼實(shí)例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2019-11-11
  • Jersey框架的統(tǒng)一異常處理機(jī)制分析

    Jersey框架的統(tǒng)一異常處理機(jī)制分析

    初學(xué)者往往不清楚java的異常為什么會(huì)設(shè)計(jì)成這個(gè)樣子,他們通常會(huì)對(duì)異常只進(jìn)行簡(jiǎn)單的處理
    2016-07-07
  • SpringCloud開(kāi)發(fā)課程查詢功能

    SpringCloud開(kāi)發(fā)課程查詢功能

    這篇文章主要介紹了SpringCloud開(kāi)發(fā)課程查詢功能,本文通過(guò)圖文實(shí)例相結(jié)合給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-12-12
  • @GrpcServise?注解的作用和使用示例詳解

    @GrpcServise?注解的作用和使用示例詳解

    @GrpcService 是一個(gè) Spring Boot 處理器,它會(huì)查找實(shí)現(xiàn)了 grpc::BindableService 接口的類(lèi),并將其包裝成一個(gè) Spring Bean 對(duì)象,這篇文章主要介紹了@GrpcServise?注解的作用和使用,需要的朋友可以參考下
    2023-05-05
  • 詳解如何使用SpringBoot實(shí)現(xiàn)下載JSON文件

    詳解如何使用SpringBoot實(shí)現(xiàn)下載JSON文件

    在?Spring?Boot?中實(shí)現(xiàn)文件下載功能,可以通過(guò)將?JSON?字符串作為文件內(nèi)容返回給客戶端從而實(shí)現(xiàn)JSON文件下載效果,下面我們就來(lái)看看具體操作吧
    2025-02-02
  • java批量插入數(shù)據(jù)的幾種方法

    java批量插入數(shù)據(jù)的幾種方法

    這篇文章主要給大家介紹了關(guān)于java批量插入數(shù)據(jù)的幾種方法,大家在Java項(xiàng)目中經(jīng)常會(huì)出現(xiàn)大量向數(shù)據(jù)庫(kù)中插入的情況,文中通過(guò)實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2023-06-06
  • 零基礎(chǔ)寫(xiě)Java知乎爬蟲(chóng)之先拿百度首頁(yè)練練手

    零基礎(chǔ)寫(xiě)Java知乎爬蟲(chóng)之先拿百度首頁(yè)練練手

    本來(lái)打算這篇文章直接抓取知乎的,但是想想還是先來(lái)個(gè)簡(jiǎn)單的吧,初級(jí)文章適合初學(xué)者,高手們請(qǐng)直接略過(guò)
    2014-11-11

最新評(píng)論

醴陵市| 北流市| 工布江达县| 遂平县| 靖边县| 阿拉善盟| 修水县| 岑巩县| 益阳市| 玛曲县| 辛集市| 平舆县| 双鸭山市| 沂水县| 永城市| 定陶县| 城市| 乐亭县| 阜宁县| 新宁县| 金寨县| 海安县| 二连浩特市| 勃利县| 新余市| 行唐县| 平乐县| 如东县| 岑巩县| 无极县| 孝昌县| 灵台县| 安徽省| 和田县| 泸定县| 霍邱县| 中江县| 巴林右旗| 贞丰县| 上虞市| 武隆县|