SpringCloudGateway網(wǎng)關(guān)處攔截并修改請求的操作方法
SpringCloudGateway網(wǎng)關(guān)處攔截并修改請求
需求背景
老系統(tǒng)沒有引入Token的概念,之前的租戶Id拼接在請求上,有的是以Get,Param傳參形式;有的是以Post,Body傳參的。需要在網(wǎng)關(guān)層攔截請求并進行請求修改后轉(zhuǎn)發(fā)到對應(yīng)服務(wù)。
舉個例子:
Get請求:
/user/getInfo?userId=1
經(jīng)過網(wǎng)關(guān)處理后變?yōu)?/user/getInfo?userId=1&&tenantId=2333
Post請求:
/user/getInfo
Body攜帶參數(shù)為:
{ userId: "1" }
經(jīng)過網(wǎng)關(guān)處理后變?yōu)?/p>
{ userId: "1", tenantId: "2333" }
解決辦法
- 全局過濾器配置: 通過@Bean注解配置一個全局過濾器,用于在請求被轉(zhuǎn)發(fā)到微服務(wù)前進行處理。
- 處理GET請求: 如果是GET請求,直接修改URL并返回,不對請求體進行修改。
- 處理非GET請求: 對非GET請求,使用裝飾者模式創(chuàng)建ModifyRequestBodyServerHttpRequestDecorator對象,對請求體進行修改。
- 去掉Content-Length頭: 在修改請求體的同時,通過mutate()方法去掉請求頭中的Content-Length。
- 修改請求體的裝飾者類: 定義了一個內(nèi)部類ModifyRequestBodyServerHttpRequestDecorator,繼承自ServerHttpRequestDecorator,用于實現(xiàn)請求體的修改。
代碼示例:
// 導(dǎo)入必要的類和包 package com.***.gateway.config; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ObjectNode; import lombok.extern.slf4j.Slf4j; import org.jetbrains.annotations.NotNull; import org.springframework.cloud.gateway.filter.GlobalFilter; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.io.buffer.DataBuffer; import org.springframework.core.io.buffer.DataBufferFactory; import org.springframework.http.server.reactive.ServerHttpRequest; import org.springframework.http.server.reactive.ServerHttpRequestDecorator; import org.springframework.web.server.ServerWebExchange; import org.springframework.web.util.UriComponentsBuilder; import reactor.core.publisher.Flux; import java.io.IOException; import java.nio.charset.StandardCharsets; @Configuration @Slf4j public class GatewayConfig { // 配置全局過濾器 @Bean public GlobalFilter customGlobalFilter() { return (exchange, chain) -> { // 獲取原始請求對象 ServerHttpRequest request = exchange.getRequest(); // 構(gòu)建URI組件構(gòu)建器,用于修改請求URL UriComponentsBuilder uriBuilder = UriComponentsBuilder.fromUri(request.getURI()); // 初始化租戶ID String tenantId = ""; // 檢查請求頭中是否包含 "TenantId",如果有則獲取其值 if (request.getHeaders().containsKey("TenantId")) { tenantId = request.getHeaders().get("TenantId").get(0); uriBuilder.queryParam("tenantId", tenantId); } // 如果請求是GET請求,則直接返回 if (request.getMethodValue().equals("GET")) { log.info("請求是Get請求,url is {}", uriBuilder.build().toUri()); ServerHttpRequest modifiedRequest = request.mutate().uri(uriBuilder.build().toUri()).build(); // 創(chuàng)建新的ServerWebExchange,該對象包含修改后的請求 ServerWebExchange modifiedExchange = exchange.mutate().request(modifiedRequest).build(); // 繼續(xù)執(zhí)行過濾器鏈 return chain.filter(modifiedExchange); } // 使用裝飾者模式修改請求體 ServerHttpRequest modifiedRequest = new ModifyRequestBodyServerHttpRequestDecorator(request, tenantId, exchange.getResponse().bufferFactory()); // 去掉Content-Length請求頭 modifiedRequest = modifiedRequest.mutate().header("Content-Length", (String) null).build(); // 創(chuàng)建新的ServerWebExchange,該對象包含修改后的請求 ServerWebExchange modifiedExchange = exchange.mutate().request(modifiedRequest).build(); // 繼續(xù)執(zhí)行過濾器鏈 return chain.filter(modifiedExchange); }; } // 定義修改請求體的裝飾者類 private static class ModifyRequestBodyServerHttpRequestDecorator extends ServerHttpRequestDecorator { private final String tenantId; private final DataBufferFactory bufferFactory; private final ObjectMapper objectMapper = new ObjectMapper(); // 構(gòu)造方法,傳入原始請求、tenantId和數(shù)據(jù)緩沖工廠 ModifyRequestBodyServerHttpRequestDecorator(ServerHttpRequest delegate, String tenantId, DataBufferFactory bufferFactory) { super(delegate); this.tenantId = tenantId; this.bufferFactory = bufferFactory; } // 重寫獲取請求體的方法,對請求體進行修改 @NotNull @Override public Flux<DataBuffer> getBody() { return super.getBody().map(dataBuffer -> { // 讀取原始請求體數(shù)據(jù) byte[] bytes = new byte[dataBuffer.readableByteCount()]; dataBuffer.read(bytes); String body = new String(bytes, StandardCharsets.UTF_8); // 修改請求體內(nèi)容 String newBody = modifyJsonBody(body); // 創(chuàng)建新的 DataBuffer byte[] newData = newBody.getBytes(StandardCharsets.UTF_8); return bufferFactory.wrap(newData); }); } // 對 JSON 請求體進行修改,添加 tenantId 字段 private String modifyJsonBody(String originalBody) { try { JsonNode jsonNode = objectMapper.readTree(originalBody); ((ObjectNode) jsonNode).put("tenantId", tenantId); return objectMapper.writeValueAsString(jsonNode); } catch (IOException e) { log.error("Error modifying JSON body", e); return originalBody; } } } }
解決路徑文章參考
關(guān)于裝飾者模式
裝飾者模式是一種結(jié)構(gòu)型設(shè)計模式,它允許你通過將對象放入包含行為的特殊封裝類中來為原始對象添加新的行為。這種模式能夠在不修改原始對象的情況下,動態(tài)地擴展其功能。在上段代碼里,主要使用裝飾者模式去修改Body 的傳參。
主要角色:
- Component(組件): 定義一個抽象接口或抽象類,聲明對象的一些基本操作。
- ConcreteComponent(具體組件): 實現(xiàn)了Component接口,是被裝飾的具體對象,也是我們最終要添加新行為的對象。
- Decorator(裝飾者抽象類): 繼承了Component,并持有一個Component對象的引用,同時實現(xiàn)了Component定義的接口。它可以通過該引用調(diào)用Component的操作,同時可以添加、擴展或修改Component的行為。
- ConcreteDecorator(具體裝飾者): 擴展Decorator,具體實現(xiàn)新行為的類。
裝飾者模式的工作流程:
- 客戶端通過Component接口與ConcreteComponent對象進行交互。
- ConcreteComponent對象處理客戶端的請求。
- 客戶端可以通過Decorator接口與ConcreteDecorator對象進行交互,Decorator持有ConcreteComponent的引用。
- ConcreteDecorator在調(diào)用ConcreteComponent的操作前后,可以添加、擴展或修改行為。
給普通咖啡加點糖和牛奶
代碼示例:
public class DecoratorPatternExample { // Component(組件) interface Coffee { String getDescription(); double cost(); } // ConcreteComponent(具體組件) static class SimpleCoffee implements Coffee { @Override public String getDescription() { return "Simple Coffee"; } @Override public double cost() { return 1.0; } } // Decorator(裝飾者抽象類) abstract static class CoffeeDecorator implements Coffee { protected Coffee decoratedCoffee; public CoffeeDecorator(Coffee coffee) { this.decoratedCoffee = coffee; } @Override public String getDescription() { return decoratedCoffee.getDescription(); } @Override public double cost() { return decoratedCoffee.cost(); } } // ConcreteDecorator(具體裝飾者) static class MilkDecorator extends CoffeeDecorator { public MilkDecorator(Coffee coffee) { super(coffee); } @Override public String getDescription() { return super.getDescription() + ", with Milk"; } @Override public double cost() { return super.cost() + 0.5; } } // ConcreteDecorator(具體裝飾者) static class SugarDecorator extends CoffeeDecorator { public SugarDecorator(Coffee coffee) { super(coffee); } @Override public String getDescription() { return super.getDescription() + ", with Sugar"; } @Override public double cost() { return super.cost() + 0.2; } } public static void main(String[] args) { // 創(chuàng)建一個簡單的咖啡 Coffee simpleCoffee = new SimpleCoffee(); System.out.println("Cost: " + simpleCoffee.cost() + ", Description: " + simpleCoffee.getDescription()); // 使用裝飾者模式添加牛奶和糖 Coffee milkSugarCoffee = new MilkDecorator(new SugarDecorator(simpleCoffee)); System.out.println("Cost: " + milkSugarCoffee.cost() + ", Description: " + milkSugarCoffee.getDescription()); } }
到此這篇關(guān)于SpringCloudGateway網(wǎng)關(guān)處攔截并修改請求的文章就介紹到這了,更多相關(guān)SpringCloud Gateway網(wǎng)關(guān)內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
idea?maven依賴引入失效無法正常導(dǎo)入依賴問題的解決方法
有時候idea導(dǎo)入一個新項目,或者pom文件修改(新增)了依賴,pom文件和代碼會報紅,提示依賴包不存在,下面這篇文章主要給大家介紹了關(guān)于idea?maven依賴引入失效無法正常導(dǎo)入依賴問題的解決方法,需要的朋友可以參考下2023-04-04Java中?SLF4J和Logback和Log4j和Logging的區(qū)別與聯(lián)系
這篇文章主要介紹了Java中?SLF4J和Logback和Log4j和Logging的區(qū)別與聯(lián)系,文章通過圍繞主題展開詳細的內(nèi)容介紹,具有一定的參考幾種,感興趣的小伙伴可以參考一下2022-09-09JDK動態(tài)代理與CGLib動態(tài)代理的區(qū)別對比
今天小編就為大家分享一篇關(guān)于JDK動態(tài)代理與CGLib動態(tài)代理的區(qū)別對比,小編覺得內(nèi)容挺不錯的,現(xiàn)在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧2019-02-02