欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

SpringCloud Finchley Gateway 緩存請(qǐng)求Body和Form表單的實(shí)現(xiàn)

 更新時(shí)間:2019年01月15日 15:05:41   作者:Evans  
在接入Spring-Cloud-Gateway時(shí),可能有需求進(jìn)行緩存Json-Body數(shù)據(jù)或者Form-Urlencoded數(shù)據(jù)的情況。這篇文章主要介紹了SpringCloud Finchley Gateway 緩存請(qǐng)求Body和Form表單的實(shí)現(xiàn),感興趣的小伙伴們可以參考一下

在接入Spring-Cloud-Gateway時(shí),可能有需求進(jìn)行緩存Json-Body數(shù)據(jù)或者Form-Urlencoded數(shù)據(jù)的情況。

由于Spring-Cloud-Gateway是以WebFlux為基礎(chǔ)的響應(yīng)式架構(gòu)設(shè)計(jì),所以在原有Zuul基礎(chǔ)上遷移過(guò)來(lái)的過(guò)程中,傳統(tǒng)的編程思路,并不適合于Reactor Stream的開(kāi)發(fā)。

網(wǎng)絡(luò)上有許多緩存案例,但是在測(cè)試過(guò)程中出現(xiàn)各種Bug問(wèn)題,在緩存Body時(shí),需要考慮整體的響應(yīng)式操作,才能更合理的緩存數(shù)據(jù)

下面提供緩存Json-Body數(shù)據(jù)或者Form-Urlencoded數(shù)據(jù)的具體實(shí)現(xiàn)方案,該方案經(jīng)測(cè)試,滿足各方面需求,以及避免了網(wǎng)絡(luò)上其他緩存方案所出現(xiàn)的問(wèn)題

定義一個(gè)GatewayContext類,用于存儲(chǔ)請(qǐng)求中緩存的數(shù)據(jù)

import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;

@Getter
@Setter
@ToString
public class GatewayContext {

  public static final String CACHE_GATEWAY_CONTEXT = "cacheGatewayContext";

  /**
   * cache json body
   */
  private String cacheBody;
  /**
   * cache formdata
   */
  private MultiValueMap<String, String> formData;
  /**
   * cache reqeust path
   */
  private String path;
}

實(shí)現(xiàn)GlobalFilter和Ordered接口用于緩存請(qǐng)求數(shù)據(jù)

1 . 該示例只支持緩存下面3種MediaType

  • APPLICATION_JSON--Json數(shù)據(jù)
  • APPLICATION_JSON_UTF8--Json數(shù)據(jù)
  • APPLICATION_FORM_URLENCODED--FormData表單數(shù)據(jù)

2 . 經(jīng)驗(yàn)總結(jié):

  • 在緩存Body時(shí),不能夠在Filter內(nèi)部直接進(jìn)行緩存,需要按照響應(yīng)式的處理方式,在異步操作路途上進(jìn)行緩存Body,由于Body只能讀取一次,所以要讀取完成后要重新封裝新的request和exchange才能保證請(qǐng)求正常傳遞到下游
  • 在緩存FormData時(shí),F(xiàn)ormData也只能讀取一次,所以在讀取完畢后,需要重新封裝request和exchange,這里要注意,如果對(duì)FormData內(nèi)容進(jìn)行了修改,則必須重新定義Header中的content-length已保證傳輸數(shù)據(jù)的大小一致
import com.choice.cloud.architect.usergate.option.FilterOrderEnum;
import com.choice.cloud.architect.usergate.support.GatewayContext;
import io.netty.buffer.ByteBufAllocator;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferUtils;
import org.springframework.core.io.buffer.NettyDataBufferFactory;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.codec.HttpMessageReader;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpRequestDecorator;
import org.springframework.util.MultiValueMap;
import org.springframework.web.reactive.function.server.HandlerStrategies;
import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.Map;

@Slf4j
public class GatewayContextFilter implements GlobalFilter, Ordered {

  /**
   * default HttpMessageReader
   */
  private static final List<HttpMessageReader<?>> messageReaders = HandlerStrategies.withDefaults().messageReaders();

  @Override
  public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
    /**
     * save request path and serviceId into gateway context
     */
    ServerHttpRequest request = exchange.getRequest();
    String path = request.getPath().pathWithinApplication().value();
    GatewayContext gatewayContext = new GatewayContext();
    gatewayContext.getAllRequestData().addAll(request.getQueryParams());
    gatewayContext.setPath(path);
    /**
     * save gateway context into exchange
     */
    exchange.getAttributes().put(GatewayContext.CACHE_GATEWAY_CONTEXT,gatewayContext);
    HttpHeaders headers = request.getHeaders();
    MediaType contentType = headers.getContentType();
    long contentLength = headers.getContentLength();
    if(contentLength>0){
      if(MediaType.APPLICATION_JSON.equals(contentType) || MediaType.APPLICATION_JSON_UTF8.equals(contentType)){
        return readBody(exchange, chain,gatewayContext);
      }
      if(MediaType.APPLICATION_FORM_URLENCODED.equals(contentType)){
        return readFormData(exchange, chain,gatewayContext);
      }
    }
    log.debug("[GatewayContext]ContentType:{},Gateway context is set with {}",contentType, gatewayContext);
    return chain.filter(exchange);

  }


  @Override
  public int getOrder() {
    return Integer.MIN_VALUE;
  }

  /**
   * ReadFormData
   * @param exchange
   * @param chain
   * @return
   */
  private Mono<Void> readFormData(ServerWebExchange exchange,GatewayFilterChain chain,GatewayContext gatewayContext){
    HttpHeaders headers = exchange.getRequest().getHeaders();
    return exchange.getFormData()
        .doOnNext(multiValueMap -> {
          gatewayContext.setFormData(multiValueMap);
          log.debug("[GatewayContext]Read FormData:{}",multiValueMap);
        })
        .then(Mono.defer(() -> {
          Charset charset = headers.getContentType().getCharset();
          charset = charset == null? StandardCharsets.UTF_8:charset;
          String charsetName = charset.name();
          MultiValueMap<String, String> formData = gatewayContext.getFormData();
          /**
           * formData is empty just return
           */
          if(null == formData || formData.isEmpty()){
            return chain.filter(exchange);
          }
          StringBuilder formDataBodyBuilder = new StringBuilder();
          String entryKey;
          List<String> entryValue;
          try {
            /**
             * remove system param ,repackage form data
             */
            for (Map.Entry<String, List<String>> entry : formData.entrySet()) {
              entryKey = entry.getKey();
              entryValue = entry.getValue();
              if (entryValue.size() > 1) {
                for(String value : entryValue){
                  formDataBodyBuilder.append(entryKey).append("=").append(URLEncoder.encode(value, charsetName)).append("&");
                }
              } else {
                formDataBodyBuilder.append(entryKey).append("=").append(URLEncoder.encode(entryValue.get(0), charsetName)).append("&");
              }
            }
          }catch (UnsupportedEncodingException e){
            //ignore URLEncode Exception
          }
          /**
           * substring with the last char '&'
           */
          String formDataBodyString = "";
          if(formDataBodyBuilder.length()>0){
            formDataBodyString = formDataBodyBuilder.substring(0, formDataBodyBuilder.length() - 1);
          }
          /**
           * get data bytes
           */
          byte[] bodyBytes = formDataBodyString.getBytes(charset);
          int contentLength = bodyBytes.length;
          ServerHttpRequestDecorator decorator = new ServerHttpRequestDecorator(
              exchange.getRequest()) {
            /**
             * change content-length
             * @return
             */
            @Override
            public HttpHeaders getHeaders() {
              HttpHeaders httpHeaders = new HttpHeaders();
              httpHeaders.putAll(super.getHeaders());
              if (contentLength > 0) {
                httpHeaders.setContentLength(contentLength);
              } else {
                httpHeaders.set(HttpHeaders.TRANSFER_ENCODING, "chunked");
              }
              return httpHeaders;
            }

            /**
             * read bytes to Flux<Databuffer>
             * @return
             */
            @Override
            public Flux<DataBuffer> getBody() {
              return DataBufferUtils.read(new ByteArrayResource(bodyBytes),new NettyDataBufferFactory(ByteBufAllocator.DEFAULT),contentLength);
            }
          };
          ServerWebExchange mutateExchange = exchange.mutate().request(decorator).build();
          log.debug("[GatewayContext]Rewrite Form Data :{}",formDataBodyString);
          return chain.filter(mutateExchange);
        }));
  }

  /**
   * ReadJsonBody
   * @param exchange
   * @param chain
   * @return
   */
  private Mono<Void> readBody(ServerWebExchange exchange,GatewayFilterChain chain,GatewayContext gatewayContext){
    /**
     * join the body
     */
    return DataBufferUtils.join(exchange.getRequest().getBody())
        .flatMap(dataBuffer -> {
          /**
           * read the body Flux<Databuffer>
           */
          DataBufferUtils.retain(dataBuffer);
          Flux<DataBuffer> cachedFlux = Flux.defer(() -> Flux.just(dataBuffer.slice(0, dataBuffer.readableByteCount())));
          /**
           * repackage ServerHttpRequest
           */
          ServerHttpRequest mutatedRequest = new ServerHttpRequestDecorator(exchange.getRequest()) {
            @Override
            public Flux<DataBuffer> getBody() {
              return cachedFlux;
            }
          };
          /**
           * mutate exchage with new ServerHttpRequest
           */
          ServerWebExchange mutatedExchange = exchange.mutate().request(mutatedRequest).build();
          /**
           * read body string with default messageReaders
           */
          return ServerRequest.create(mutatedExchange, messageReaders)
              .bodyToMono(String.class)
              .doOnNext(objectValue -> {
                gatewayContext.setCacheBody(objectValue);
                log.debug("[GatewayContext]Read JsonBody:{}",objectValue);
              }).then(chain.filter(mutatedExchange));
        });
  }

}

在后續(xù)Filter中,可以直接從ServerExchange中獲取GatewayContext,就可以獲取到緩存的數(shù)據(jù),如果需要緩存其他數(shù)據(jù),則可以根據(jù)自己的需求,添加到GatewayContext中即可

復(fù)制代碼 代碼如下:
GatewayContext gatewayContext = exchange.getAttribute(GatewayContext.CACHE_GATEWAY_CONTEXT);

以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • java finally塊執(zhí)行時(shí)機(jī)全面分析

    java finally塊執(zhí)行時(shí)機(jī)全面分析

    下面小編就為大家?guī)?lái)一篇java finally塊執(zhí)行時(shí)機(jī)全面分析。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2016-08-08
  • 解析Tars-Java客戶端源碼

    解析Tars-Java客戶端源碼

    Tars是基于名字服務(wù)使用Tars協(xié)議的高性能RPC開(kāi)發(fā)框架,同時(shí)配套一體化的服務(wù)治理平臺(tái),幫助個(gè)人或者企業(yè)快速的以微服務(wù)的方式構(gòu)建自己穩(wěn)定可靠的分布式應(yīng)用
    2021-06-06
  • Nacos-SpringBoot框架啟動(dòng)不加載bootstrap.yml的解決

    Nacos-SpringBoot框架啟動(dòng)不加載bootstrap.yml的解決

    這篇文章主要介紹了Nacos-SpringBoot框架啟動(dòng)不加載bootstrap.yml的解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-11-11
  • 詳解spring cloud中使用Ribbon實(shí)現(xiàn)客戶端的軟負(fù)載均衡

    詳解spring cloud中使用Ribbon實(shí)現(xiàn)客戶端的軟負(fù)載均衡

    這篇文章主要介紹了詳解spring cloud中使用Ribbon實(shí)現(xiàn)客戶端的軟負(fù)載均衡,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2018-01-01
  • java音樂(lè)播放器編寫源碼

    java音樂(lè)播放器編寫源碼

    這篇文章主要為大家詳細(xì)介紹了java音樂(lè)播放器的編寫源碼,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-06-06
  • eclipse實(shí)現(xiàn)DSA數(shù)字簽名

    eclipse實(shí)現(xiàn)DSA數(shù)字簽名

    這篇文章主要為大家詳細(xì)介紹了eclipse實(shí)現(xiàn)DSA數(shù)字簽名算法,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2020-06-06
  • Java中將字符串String轉(zhuǎn)換為整數(shù)int的多種方法

    Java中將字符串String轉(zhuǎn)換為整數(shù)int的多種方法

    在Java中將String類型轉(zhuǎn)換為int類型是一個(gè)常見(jiàn)的操作,下面這篇文章主要給大家介紹了關(guān)于Java中將字符串String轉(zhuǎn)換為整數(shù)int的多種方法,文中通過(guò)代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2024-07-07
  • java?System類和Arrays類詳解

    java?System類和Arrays類詳解

    這篇文章主要介紹了java?System類和Arrays類詳解,文章圍繞主題展開(kāi)詳細(xì)的內(nèi)容介紹,具有一定的參考價(jià)值,需要的小伙伴可以參考一下
    2022-08-08
  • Required?request?body?is?missing的問(wèn)題及解決

    Required?request?body?is?missing的問(wèn)題及解決

    這篇文章主要介紹了Required?request?body?is?missing的問(wèn)題及解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-12-12
  • 解決ApplicationContext獲取不到Bean的問(wèn)題

    解決ApplicationContext獲取不到Bean的問(wèn)題

    這篇文章主要介紹了解決ApplicationContext獲取不到Bean的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-06-06

最新評(píng)論