Spring Cloud Gateway實踐(一):獲取參數

来源:https://www.cnblogs.com/penghusile/archive/2023/12/10/17892687.html
-Advertisement-
Play Games

SCG(Spring Cloud Gateway)就我個人理解,是想讓開發者把它作為一個較為簡單的網關框架,只需簡單在yml文件中寫幾個配置項就可以運行。所以它不大推薦在網關這一層獲取body數據或者做一下複雜的業務處理。故而在實際編寫代碼中,獲取queryParam很容易,但body數據就比較麻煩 ...


SCG(Spring Cloud Gateway)就我個人理解,是想讓開發者把它作為一個較為簡單的網關框架,只需簡單在yml文件中寫幾個配置項就可以運行。所以它不大推薦在網關這一層獲取body數據或者做一下複雜的業務處理。故而在實際編寫代碼中,獲取queryParam很容易,但body數據就比較麻煩了,如果要修改就更麻煩。在本篇文章主要討論如何獲取請求方式中的參數。
SCG獲取參數一般有兩種方式:

  1. 通過Filter過濾器
  2. 通過Predicate斷言

原理都類似,通過事先緩存doby到attribute中,再讀取。至於這兩種區別主要在於緩存方式:filter直接加一層globalFilter即可,而Predicate則需要加一個配置項。具體請看代碼

配置Filter獲取

import lombok.NonNull;  
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.buffer.DataBuffer;  
import org.springframework.core.io.buffer.DataBufferUtils;  
import org.springframework.http.server.reactive.ServerHttpRequest;  
import org.springframework.http.server.reactive.ServerHttpRequestDecorator;  
import org.springframework.stereotype.Component;  
import org.springframework.web.server.ServerWebExchange;  
import reactor.core.publisher.Flux;  
import reactor.core.publisher.Mono;  
   
@Component  
public class ReadParamFilter implements GlobalFilter, Ordered {  
  
    @Override  
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {  
        if (exchange.getRequest().getHeaders().getContentType() == null) {  
            return chain.filter(exchange);  
        } else {  
            return DataBufferUtils.join(exchange.getRequest().getBody())  
                    .flatMap(dataBuffer -> {  
                        DataBufferUtils.retain(dataBuffer);  
                        Flux<DataBuffer> cachedFlux = Flux  
                                .defer(() -> Flux.just(dataBuffer.slice(0, dataBuffer.readableByteCount())));  
                        ServerHttpRequest mutatedRequest = new ServerHttpRequestDecorator(exchange.getRequest()) {  
                            @Override  
                            public @NonNull Flux<DataBuffer> getBody() {  
                                return cachedFlux;  
                            }  
  
                        };  
                        exchange.getAttributes().put("cachedRequestBodyObject", cachedFlux);   
                    });  
        }  
    }  
  
    @Override  
    public int getOrder() {  
        return Ordered.HIGHEST_PRECEDENCE;  
    }  
}

缺點:在斷言階段不能獲取參數

配置Predicate獲取

predicate緩存request body需要加一個配置項

spring:  
  cloud:  
    gateway:  
      predicate:  
        read-body:  
          enabled: true 

另外再加一個讀取body的Predicate類。


## 斷言類

```java
@Component  
public class BodyPredicate implements Predicate {  
   @Override  
   public boolean test(Object o) {  
      return true;  
   }  
}


緩存到之後,後邊獲取參數就比較方便了。

獲取參數

@Service  
public class ParamFactory {  
   @Autowired  
   Map<String, ParamStrategy> getParamFactoryMap;  
  
   public ParamStrategy getParamStrategy(HttpMethod requestMethod){  
      return getParamFactoryMap.get(requestMethod.name());  
   }  
}

獲取參數策略

public abstract class ParamStrategy {  
  
    public RequestParamBO analyzeRequestParam(ServerWebExchange exchange) {  
        return doAnalyzeRequestParam(exchange);  
    }  
  
    /**  
     * 解析請求數據  
     *  
     * @param exchange  
    * @return  
     */  
    protected abstract RequestParamBO doAnalyzeRequestParam(ServerWebExchange exchange);  
  
    /**  
     * 獲取某個請求參數  
     *  
     * @param requestMessage  
     * @param paramKey  
     * @param position  
     * @return  
     */  
    public abstract String getParamValue(RequestMessageBO requestMessage, String paramKey,  
                                         String position);  
  
  
}

get

@Component("GET")  
public class GetParamStrategy extends ParamStrategy {  
  
    /**  
     * 解析請求數據  
     *  
     * @param exchange@return  
     */    @Override  
    protected RequestParamBO doAnalyzeRequestParam(ServerWebExchange exchange) {  
        Map<String, String> paramMap = new HashMap<>();  
        MultiValueMap<String, String> queryParams = exchange.getRequest().getQueryParams();  
        if (!queryParams.isEmpty()) {  
            paramMap =  
                    queryParams.entrySet().stream()  
                            .collect(  
                                    Collectors.toMap(  
                                            Map.Entry::getKey,  
                                            entry -> {  
                                                List<String> list =  
                                                        new ArrayList<>(entry.getValue());  
                                                // list包含空數據  
                                                list.removeIf(Objects::isNull);  
                                                if (list.size() != 0) {  
                                                    return entry.getValue().get(0);  
                                                } else {  
                                                    return "";  
                                                }  
                                            }));  
        }  
        return RequestParamBO.builder()  
                .queryParams(paramMap)  
                .build();  
    }  
  
    @Override  
    public String getParamValue(RequestMessageBO requestMessage, String paramKey, String position) {  
        Map<String,String> queryParam = requestMessage.getParam().getQueryParams();  
        if (CollectionUtils.isEmpty(queryParam)){  
            return null;  
        }  
        return queryParam.get(paramKey);  
    }  

}

post

@Component("POST")  
public class PostParamStrategy extends ParamStrategy {  
  
    private static final String XW_FORM_PARAM_REGEX = "&";  
    private static final String XW_KEY_VALUE_REGEX = "=";  
  
    /**  
     * 解析請求數據  
     *  
     * @param exchange  
     * @return  
     */  
    @Override  
    protected RequestParamBO doAnalyzeRequestParam(ServerWebExchange exchange) {  
        Map<String, String> paramMap = new HashMap<>();  
        Map<String, Object> attributes = exchange.getAttributes();  
        MultiValueMap<String, String> queryParams = exchange.getRequest().getQueryParams();  
        RequestParamBO requestParams = new RequestParamBO();  
        if (CollectionUtil.isNotEmpty(queryParams)) {  
            paramMap =  
                    queryParams.entrySet().stream()  
                            .collect(  
                                    Collectors.toMap(  
                                            Map.Entry::getKey,  
                                            entry -> {  
                                                List<String> list =  
                                                        new ArrayList<>(entry.getValue());  
                                                // list包含空數據  
                                                list.removeIf(Objects::isNull);  
                                                if (list.size() != 0) {  
                                                    return entry.getValue().get(0);  
                                                } else {  
                                                    return "";  
                                                }  
                                            }));  
        }  
        requestParams.setQueryParams(paramMap);  
        MediaType contentType = exchange.getRequest().getHeaders().getContentType();  
        String body = (String) attributes.get(CACHE_REQUEST_BODY_OBJECT);  
  
        if (MULTIPART_FORM_DATA.isCompatibleWith(contentType)) {  
            assert contentType != null;  
            requestParams.setFormParams(getFormParam(contentType.toString(), body));  
        } else if (APPLICATION_FORM_URLENCODED.isCompatibleWith(contentType)) {  
            requestParams.setFormParams(getXwFormParam(body));  
        } else if (APPLICATION_JSON.isCompatibleWith(contentType)) {  
            //json body就直接作為String處理保存了,
            requestParams.setJsonBody(body);  
        }  
  
        return requestParams;  
    }  
  
    @Override  
    public String getParamValue(RequestMessageBO requestMessage, String paramKey, String position) {  
        MediaType mediaType = requestMessage.getMediaType();  
        if (APPLICATION_JSON.isCompatibleWith(mediaType)) {  
            Object document = Configuration.defaultConfiguration()  
                    .jsonProvider().parse(requestMessage.getParam().getJsonBody());  
            JSONArray paramValues = Objects.requireNonNull(JsonPath.read(document, position));  
            return String.valueOf(paramValues.get(0));  
        }else {  
            return requestMessage.getParam().getFormParams().get(paramKey);  
        }  
    }  
  
   //獲取 表單數據
  
    @SneakyThrows  
    private Map<String, String> getFormParam(String contentType, String bodyString) {  
  
        String boundary = contentType.substring(contentType.lastIndexOf("boundary=") + 9);  
        Map<String, String> formMap = Maps.newHashMap();  
        String part =  
                "^\r\nContent-Disposition: form-data; name=\"([^/?]+)\"\r\n\r\n([^/?]+)\r\n--?$";  
        Pattern r = Pattern.compile(part);  
        String[] split = bodyString.split(boundary);  
        for (int x = 1; x < split.length - 1; x++) {  
            Matcher m = r.matcher(split[x]);  
            if (m.find()) {  
                String name = m.group(1);  
                String value = m.group(2);  
                formMap.put(name, value);  
            }  
        }  
        return formMap;  
    }  
  //獲取xw表單數據
    private Map<String, String> getXwFormParam(String bodyStr) {  
        Map<String, String> paramMap = new HashMap<>();  
        try {  
            bodyStr = URLDecoder.decode(bodyStr, "utf-8");  
        } catch (UnsupportedEncodingException e) {  
            throw new RuntimeException(e);  
        }  
        String[] params = bodyStr.split(XW_FORM_PARAM_REGEX);  
        for (String paramKeyValue : params) {  
            String[] keyValue = paramKeyValue.split(XW_KEY_VALUE_REGEX);  
            if (keyValue.length == 2) {  
                paramMap.put(keyValue[0], keyValue[1]);  
            }  
        }  
        return paramMap;  
    }
}

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • spring Boot應用啟動器基本的一共有44種,具體如下 1)spring-boot-starter 這是Spring Boot的核心啟動器,包含了自動配置、日誌和YAML。 2)spring-boot-starter-actuator 幫助監控和管理應用。 3)spring-boot-star ...
  • python原生數據類型有int、float、str、tuple、list、set、dict七類,本次隨筆介紹了幾種類型的定義,同時用實驗驗證各數據類型之間的嵌套,以實驗結果證明各個數據類型的基本特征。 ...
  • 本文首發於公眾號:Hunter後端 原文鏈接:Django筆記四十四之Nginx+uWSGI部署Django以及Nginx負載均衡操作 這一篇筆記介紹如何使用 Nginx + uWSGI 來部署 Django。 上一篇筆記中有介紹直接使用 uWSGI 作為 web 伺服器來部署 Django,這一篇 ...
  • 本文是我從業多年開發生涯中針對線上業務的處理經驗總結而來,這些業務或多或少相信大家都遇到過,因此在這裡分享給大家,大家也可以看看是不是遇到過類似場景。本文大綱如下, 後臺上傳文件 線上後臺項目有一個消息推送的功能,運營新建一條通知消息時,需要一起上傳一列包含用戶 id 的文件,來給文件中包含的指定用 ...
  • Java為我們提供了許多啟動線程和管理線程的方法。在本文中,我們將介紹一些在Java中進行併發編程的選項。我們將介紹結構化併發的概念,然後討論Java 21中一組預覽類——它使將任務拆分為子任務、收集結果並對其進行操作變得非常容易,而且不會不小心留下任何掛起的任務。 1 基礎方法 通過Lambda表 ...
  • 1 問題復現 (1)登錄DVMA後,設置DVWA Security為Low。 (2)進入File Inclusion,訪問dvwa/vulnerabilities/fi目錄下的的test.txt文件(自己創建的測試文件)。 (3)報錯ERROR: File not found! 找不到文件。 2 抓 ...
  • 當我們談論編程中的數據結構時,順序容器是不可忽視的一個重要概念。順序容器是一種能夠按照元素添加的順序來存儲和檢索數據的數據結構。它們提供了簡單而直觀的方式來組織和管理數據,為程式員提供了靈活性和性能的平衡。Qt 中提供了豐富的容器類,用於方便地管理和操作數據。這些容器類涵蓋了各種不同的用途,從簡單的... ...
  • 最近想著把工作中使用過的java命令都梳理一下,方便日後查閱。雖然這類文章很多,但自己梳理總結後,還是會有一些新的收穫。這也是這篇筆記的由來。 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...