成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

Spring Cloud Gateway修改請求和響應(yīng)body的內(nèi)容

ivyzhang / 3420人閱讀

摘要:歡迎訪問我的歡迎訪問我的內(nèi)容所有原創(chuàng)文章分類匯總及配套源碼,涉及等本篇概覽本篇概覽作為實戰(zhàn)系列的第九篇,咱們聊聊如何用修改原始請求和響應(yīng)內(nèi)容,以及修改過程中遇到的問題首先是修改請求,如下圖,瀏覽器是請求發(fā)起方,真實參數(shù)只有,經(jīng)過網(wǎng)關(guān)時被塞

歡迎訪問我的GitHub

https://github.com/zq2599/blog_demos

內(nèi)容:所有原創(chuàng)文章分類匯總及配套源碼,涉及Java、Docker、Kubernetes、DevOPS等;

本篇概覽

  • 作為《Spring Cloud Gateway實戰(zhàn)》系列的第九篇,咱們聊聊如何用Spring Cloud Gateway修改原始請求和響應(yīng)內(nèi)容,以及修改過程中遇到的問題

  • 首先是修改請求body,如下圖,瀏覽器是請求發(fā)起方,真實參數(shù)只有user-id,經(jīng)過網(wǎng)關(guān)時被塞入字段user-name,于是,后臺服務(wù)收到的請求就帶有user-name字段了

在這里插入圖片描述

  • 其次是修改響應(yīng),如下圖,服務(wù)提供方provider-hello的原始響應(yīng)只有response-tag字段,經(jīng)過網(wǎng)關(guān)時被塞入了gateway-response-tag字段,最終瀏覽器收到的響應(yīng)就是response-taggateway-response-tag兩個字段:

在這里插入圖片描述

  • 總的來說,今天要做具體事情如下:
  1. 準備工作:在服務(wù)提供者的代碼中新增一個web接口,用于驗證Gateway的操作是否有效
  2. 介紹修改請求body和響應(yīng)body的套路
  3. 按套路開發(fā)一個過濾器(filter),用于修改請求的body
  4. 按套路開發(fā)一個過濾器(filter),用于修改響應(yīng)的body
  5. 思考和嘗試:如何從Gateway返回錯誤?
  • 在實戰(zhàn)過程中,咱們順便搞清楚兩個問題:
  1. 代碼配置路由時,如何給一個路由添加多個filter?
  2. 代碼配置路由和yml配置是否可以混搭,兩者有沖突嗎?

源碼下載

名稱鏈接備注
項目主頁https://github.com/zq2599/blog_demos該項目在GitHub上的主頁
git倉庫地址(https)https://github.com/zq2599/blog_demos.git該項目源碼的倉庫地址,https協(xié)議
git倉庫地址(ssh)[email protected]:zq2599/blog_demos.git該項目源碼的倉庫地址,ssh協(xié)議
  • 這個git項目中有多個文件夾,本篇的源碼在spring-cloud-tutorials文件夾下,如下圖紅框所示:

在這里插入圖片描述

  • spring-cloud-tutorials文件夾下有多個子工程,本篇的代碼是gateway-change-body,如下圖紅框所示:

在這里插入圖片描述

準備工作

  • 為了觀察Gateway能否按預(yù)期去修改請求和響應(yīng)的body,咱們給服務(wù)提供者provider-hello增加一個接口,代碼在Hello.java中,如下:
    @PostMapping("/change")    public Map change(@RequestBody Map map) {        map.put("response-tag", dateStr());        return map;    }
  • 可見新增的web接口很簡單:將收到的請求數(shù)據(jù)作為返回值,在里面添加了一個鍵值對,然后返回給請求方,有了這個接口,咱們就能通過觀察返回值來判斷Gateway對請求和響應(yīng)的操作是否生效

  • 來試一下,先啟動nacos(provider-hello需要的)

  • 再運行provider-hello應(yīng)用,用Postman向其發(fā)請求試試,如下圖,符合預(yù)期:

在這里插入圖片描述

  • 準備工作已完成,開始開發(fā)吧

修改請求body的套路

  • 如何用Spring Cloud Gateway修改請求的body?來看看其中的套路:
  1. 修改請求body是通過自定義filter實現(xiàn)的
  2. 配置路由及其filter的時候,有yml配置文件和代碼配置兩種方式可以配置路由,官方文檔給出的demo是代碼配置的,因此今天咱們也參考官方做法,通過代碼來配置路由和過濾器
  3. 在代碼配置路由的時候,調(diào)用filters方法,該方法的入?yún)⑹莻€lambda表達式
  4. 此lambda表達式固定調(diào)用modifyRequestBody方法,咱們只要定義好modifyRequestBody方法的三個入?yún)⒓纯?/li>
  5. modifyRequestBody方法的第一個入?yún)⑹禽斎腩愋?/li>
  6. 第二個入?yún)⑹欠祷仡愋?/li>
  7. 第三個是RewriteFunction接口的實現(xiàn),這個代碼需要您自己寫,內(nèi)容是將輸入數(shù)據(jù)轉(zhuǎn)換為返回類型數(shù)據(jù)具體邏輯,咱們來看官方Demo,也就是上述套路了:
@Beanpublic RouteLocator routes(RouteLocatorBuilder builder) {    return builder.routes()        .route("rewrite_request_obj", r -> r.host("*.rewriterequestobj.org")            .filters(f -> f.prefixPath("/httpbin")                .modifyRequestBody(String.class, Hello.class, MediaType.APPLICATION_JSON_VALUE,                    (exchange, s) -> return Mono.just(new Hello(s.toUpperCase())))).uri(uri))        .build();}

修改響應(yīng)body的套路

  • 用Spring Cloud Gateway修改響應(yīng)body的套路和前面的請求body如出一轍
  1. 通過代碼來配置路由和過濾器
  2. 在代碼配置路由的時候,調(diào)用filters方法,該方法的入?yún)⑹莻€lambda表達式
  3. 此lambda表達式固定調(diào)用modifyResponseBody方法,咱們只要定義好modifyResponseBody方法的三個入?yún)⒓纯?/li>
  4. modifyRequestBody方法的第一個入?yún)⑹禽斎腩愋?/li>
  5. 第二個入?yún)⑹欠祷仡愋?/li>
  6. 第三個是RewriteFunction接口的實現(xiàn),這個代碼要您自己寫,內(nèi)容是將輸入數(shù)據(jù)轉(zhuǎn)換為返回類型數(shù)據(jù)具體邏輯,咱們來看官方Demo,其實就是上述套路:
@Beanpublic RouteLocator routes(RouteLocatorBuilder builder) {    return builder.routes()        .route("rewrite_response_upper", r -> r.host("*.rewriteresponseupper.org")            .filters(f -> f.prefixPath("/httpbin")                .modifyResponseBody(String.class, String.class,                    (exchange, s) -> Mono.just(s.toUpperCase()))).uri(uri))        .build();}
  • 套路總結(jié)出來了,接下來,咱們一起擼代碼?

按套路開發(fā)一個修改請求body的過濾器(filter)

  • 廢話不說,在父工程spring-cloud-tutorials下新建子工程gateway-change-body,pom.xml無任何特殊之處,注意依賴spring-cloud-starter-gateway即可

  • 啟動類毫無新意:

package com.bolingcavalry.changebody;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplicationpublic class ChangeBodyApplication {    public static void main(String[] args) {        SpringApplication.run(ChangeBodyApplication.class,args);    }}
  • 配置文件千篇一律:
server:  #服務(wù)端口  port: 8081spring:  application:    name: gateway-change-body
  • 然后是核心邏輯:修改請求body的代碼,既RewriteFunction的實現(xiàn)類,代碼很簡單,將原始的請求body解析成Map對象,取出user-id字段,生成user-name字段放回map,apply方法返回的是個Mono:
package com.bolingcavalry.changebody.function;import com.fasterxml.jackson.databind.ObjectMapper;import lombok.extern.slf4j.Slf4j;import org.reactivestreams.Publisher;import org.springframework.cloud.gateway.filter.factory.rewrite.RewriteFunction;import org.springframework.web.server.ServerWebExchange;import reactor.core.publisher.Mono;import java.util.Map;@Slf4jpublic class RequestBodyRewrite implements RewriteFunction {    private ObjectMapper objectMapper;    public RequestBodyRewrite(ObjectMapper objectMapper) {        this.objectMapper = objectMapper;    }    /**     * 根據(jù)用戶ID獲取用戶名稱的方法,可以按實際情況來內(nèi)部實現(xiàn),例如查庫或緩存,或者遠程調(diào)用     * @param userId     * @return     */    private  String mockUserName(int userId) {        return "user-" + userId;    }    @Override    public Publisher apply(ServerWebExchange exchange, String body) {        try {            Map map = objectMapper.readValue(body, Map.class);            // 取得id            int userId = (Integer)map.get("user-id");            // 得到nanme后寫入map            map.put("user-name", mockUserName(userId));            // 添加一個key/value            map.put("gateway-request-tag", userId + "-" + System.currentTimeMillis());            return Mono.just(objectMapper.writeValueAsString(map));        } catch (Exception ex) {            log.error("1. json process fail", ex);            // json操作出現(xiàn)異常時的處理            return Mono.error(new Exception("1. json process fail", ex));        }    }}
  • 然后是按部就班的基于代碼實現(xiàn)路由配置,重點是lambda表達式執(zhí)行modifyRequestBody方法,并且將RequestBodyRewrite作為參數(shù)傳入:
package com.bolingcavalry.changebody.config;import com.bolingcavalry.changebody.function.RequestBodyRewrite;import com.bolingcavalry.changebody.function.ResponseBodyRewrite;import com.fasterxml.jackson.databind.ObjectMapper;import org.springframework.cloud.gateway.route.RouteLocator;import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.http.MediaType;import reactor.core.publisher.Mono;@Configurationpublic class FilterConfig {    @Bean    public RouteLocator routes(RouteLocatorBuilder builder, ObjectMapper objectMapper) {        return builder                .routes()                .route("path_route_change",                        r -> r.path("/hello/change")                                .filters(f -> f                                        .modifyRequestBody(String.class,String.class,new RequestBodyRewrite(objectMapper))                                        )                        .uri("http://127.0.0.1:8082"))                .build();    }}
  • 代碼寫完了,運行工程gateway-change-body,在postman發(fā)起請求,得到響應(yīng)如下圖,紅框中可見Gateway添加的內(nèi)容已成功:

在這里插入圖片描述

  • 現(xiàn)在修改請求body已經(jīng)成功,接下來再來修改服務(wù)提供者響應(yīng)的body

修改響應(yīng)body

  • 接下來開發(fā)修改響應(yīng)body的代碼

  • 新增RewriteFunction接口的實現(xiàn)類ResponseBodyRewrite.java

package com.bolingcavalry.changebody.function;import com.fasterxml.jackson.databind.ObjectMapper;import lombok.extern.slf4j.Slf4j;import org.reactivestreams.Publisher;import org.springframework.cloud.gateway.filter.factory.rewrite.RewriteFunction;import org.springframework.web.server.ServerWebExchange;import reactor.core.publisher.Mono;import java.util.Map;@Slf4jpublic class ResponseBodyRewrite implements RewriteFunction {    private ObjectMapper objectMapper;    public ResponseBodyRewrite(ObjectMapper objectMapper) {        this.objectMapper = objectMapper;    }    @Override    public Publisher apply(ServerWebExchange exchange, String body) {        try {            Map map = objectMapper.readValue(body, Map.class);            // 取得id            int userId = (Integer)map.get("user-id");            // 添加一個key/value            map.put("gateway-response-tag", userId + "-" + System.currentTimeMillis());            return Mono.just(objectMapper.writeValueAsString(map));        } catch (Exception ex) {            log.error("2. json process fail", ex);            return Mono.error(new Exception("2. json process fail", ex));        }    }}
  • 路由配置代碼中,lambda表達式里面,filters方法內(nèi)部調(diào)用modifyResponseBody,第三個入?yún)⑹荝esponseBodyRewrite:
package com.bolingcavalry.changebody.config;import com.bolingcavalry.changebody.function.RequestBodyRewrite;import com.bolingcavalry.changebody.function.ResponseBodyRewrite;import com.fasterxml.jackson.databind.ObjectMapper;import org.springframework.cloud.gateway.route.RouteLocator;import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.http.MediaType;import reactor.core.publisher.Mono;@Configurationpublic class FilterConfig {    @Bean    public RouteLocator routes(RouteLocatorBuilder builder, ObjectMapper objectMapper) {        return builder                .routes()                .route("path_route_change",                        r -> r.path("/hello/change")                                .filters(f -> f                                        .modifyRequestBody(String.class,String.class,new RequestBodyRewrite(objectMapper))                                        .modifyResponseBody(String.class, String.class, new ResponseBodyRewrite(objectMapper))                                        )                        .uri("http://127.0.0.1:8082"))                .build();    }}
  • 還記得咱們的第一個問題嗎?通過上面的代碼,您應(yīng)該已經(jīng)看到了答案:用代碼配置路由時,多個過濾器的配置方法就是在filters方法中反復調(diào)用內(nèi)置的過濾器相關(guān)API,下圖紅框中的都可以:

在這里插入圖片描述

  • 運行服務(wù),用Postman驗證效果,如下圖紅框,Gateway在響應(yīng)body中成功添加了一個key&value:

在這里插入圖片描述

代碼配置路由和yml配置是否可以混搭?

  • 前面有兩個問題,接下來回答第二個,咱們在application.yml中增加一個路由配置:
server:  #服務(wù)端口  port: 8081spring:  application:    name: gateway-change-body  cloud:    gateway:      routes:        - id: path_route_str          uri: http://127.0.0.1:8082          predicates:            - Path=/hello/str
  • gateway-change-body服務(wù)啟動起來,此時已經(jīng)有了兩個路由配置,一個在代碼中,一個在yml中,先試試yml中的這個,如下圖沒問題:

在這里插入圖片描述

  • 再試試代碼配置的路由,如下圖,結(jié)論是代碼配置路由和yml配置可以混搭

在這里插入圖片描述

如何處理異常

  • 還有個問題必須要面對:修改請求或者響應(yīng)body的過程中,如果發(fā)現(xiàn)問題需要提前返回錯誤(例如必要的字段不存在),代碼該怎么寫?

  • 咱們修改請求body的代碼集中在RequestBodyRewrite.java,增加下圖紅框內(nèi)容:

在這里插入圖片描述

  • 再來試試,這次請求參數(shù)中不包含user-id,收到Gateway返回的錯誤信息如下圖:

在這里插入圖片描述

  • 看看控制臺,能看到代碼中拋出的異常信息:

在這里插入圖片描述

  • 此時,聰明的您應(yīng)該發(fā)現(xiàn)問題所在了:咱們想告訴客戶端具體的錯誤,但實際上客戶端收到的是被Gateway框架處理后的內(nèi)容

  • 篇幅所限,上述問題從分析到解決的過程,就留給下一篇文章吧

  • 本篇的最后,請容許欣宸嘮叨兩句,聊聊為何要網(wǎng)關(guān)來修改請求和響應(yīng)body的內(nèi)容,如果您沒興趣還請忽略

網(wǎng)關(guān)(Gateway)為什么要做這些?

  • 看過開篇的兩個圖,聰明的您一定發(fā)現(xiàn)了問題:為什么要破壞原始數(shù)據(jù),一旦系統(tǒng)出了問題如何定位是服務(wù)提供方還是網(wǎng)關(guān)?

  • 按照欣宸之前的經(jīng)驗,盡管網(wǎng)關(guān)會破壞原始數(shù)據(jù),但只做一些簡單固定的處理,一般以添加數(shù)據(jù)為主,網(wǎng)關(guān)不了解業(yè)務(wù),最常見的就是鑒權(quán)、添加身份或標簽等操作

  • 前面的圖中確實感受不到網(wǎng)關(guān)的作用,但如果網(wǎng)關(guān)后面有多個服務(wù)提供者,如下圖,這時候諸如鑒權(quán)、獲取賬號信息等操作由網(wǎng)關(guān)統(tǒng)一完成,比每個后臺分別實現(xiàn)一次更有效率,后臺可以更加專注于自身業(yè)務(wù):

在這里插入圖片描述

  • 經(jīng)驗豐富的您可能會對我的狡辯不屑一顧:網(wǎng)關(guān)統(tǒng)一鑒權(quán)、獲取身份,一般會把身份信息放入請求的header中,也不會修改請求和響應(yīng)的內(nèi)容啊,欣宸前面的一堆解釋還是沒說清楚為啥要在網(wǎng)關(guān)位置修改請求和響應(yīng)的內(nèi)容!

  • 好吧,面對聰明的您,我攤牌了:本篇只是從技術(shù)上演示Spring Cloud Gateway如何修改請求和響應(yīng)內(nèi)容,請不要將此技術(shù)與實際后臺業(yè)務(wù)耦合;

你不孤單,欣宸原創(chuàng)一路相伴

  1. Java系列
  2. Spring系列
  3. Docker系列
  4. kubernetes系列
  5. 數(shù)據(jù)庫+中間件系列
  6. DevOps系列

歡迎關(guān)注公眾號:程序員欣宸

微信搜索「程序員欣宸」,我是欣宸,期待與您一同暢游Java世界...
https://github.com/zq2599/blog_demos

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/124815.html

相關(guān)文章

  • Spring Cloud Gateway實戰(zhàn)之五:內(nèi)置filter

    摘要:歡迎訪問我的歡迎訪問我的內(nèi)容所有原創(chuàng)文章分類匯總及配套源碼,涉及等本篇概覽本篇概覽作為實戰(zhàn)系列的第五篇,是時候了解過濾器的作用了,本篇咱們一起來了解內(nèi)置好的過濾器,真是種類繁多功能強大過濾器顧名思義,就是在請求頭部添加指定的內(nèi)容帶有的完整配歡迎訪問我的GitHubhttps://github.com/zq2599/blog_demos內(nèi)容:所有原創(chuàng)文章分類匯總及配套源碼,涉及Java、Doc...

    reclay 評論0 收藏0
  • SpringCloud組件: GateWay整合Eureka轉(zhuǎn)發(fā)服務(wù)請求

    摘要:單服務(wù)我們簡單編寫一個請求地址,輸出字符串信息,添加依賴如下所示配置文件如下所示服務(wù)名注冊到服務(wù)端口號配置該服務(wù)的服務(wù)名稱為,這里對應(yīng)的。 在上一篇文章Spring Cloud GateWay 路由轉(zhuǎn)發(fā)規(guī)則介紹中我們講解了SpringCloud Gateway內(nèi)部提供的斷言、謂語,讓我們可以組合更精確的業(yè)務(wù)場景進行請求,既然SpringCloud GateWay擔任了網(wǎng)關(guān)的角色,在之前...

    warmcheng 評論0 收藏0
  • Spring Cloud GateWay 路由轉(zhuǎn)發(fā)規(guī)則介紹

    摘要:組合示例相同的也可以配置多個,請求的轉(zhuǎn)發(fā)是必須滿足所有的后才可以進行路由轉(zhuǎn)發(fā),組合使用示例如下所示總結(jié)本章節(jié)講解了的相關(guān)謂詞斷言基本使用方式,內(nèi)部提供了很多種靈活的路由轉(zhuǎn)發(fā)規(guī)則,在同一個路由內(nèi)存在多個時,同時滿足規(guī)則后請求才會被路由轉(zhuǎn)發(fā)。 Spring在因Netflix開源流產(chǎn)事件后,在不斷的更換Netflix相關(guān)的組件,比如:Eureka、Zuul、Feign、Ribbon等,Zuu...

    zgbgx 評論0 收藏0
  • Spring Cloud Gateway限流實戰(zhàn)

    摘要:歡迎訪問我的歡迎訪問我的內(nèi)容所有原創(chuàng)文章分類匯總及配套源碼,涉及等本篇概覽本篇概覽本文是實戰(zhàn)系列的第八篇,經(jīng)過前面的學習,咱們對過濾器已了解得差不多,今天來補全過濾器的最后一個版塊限流默認的限流器是基于實現(xiàn)的,限流算法是大家熟悉的令牌桶關(guān)于歡迎訪問我的GitHubhttps://github.com/zq2599/blog_demos內(nèi)容:所有原創(chuàng)文章分類匯總及配套源碼,涉及Java、Doc...

    stonezhu 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<