SpringCloudGateway網(wǎng)關(guān)處攔截并修改請(qǐng)求的操作方法
SpringCloudGateway網(wǎng)關(guān)處攔截并修改請(qǐng)求

需求背景
老系統(tǒng)沒(méi)有引入Token的概念,之前的租戶Id拼接在請(qǐng)求上,有的是以Get,Param傳參形式;有的是以Post,Body傳參的。需要在網(wǎng)關(guān)層攔截請(qǐng)求并進(jìn)行請(qǐng)求修改后轉(zhuǎn)發(fā)到對(duì)應(yīng)服務(wù)。
舉個(gè)例子:
Get請(qǐng)求:
/user/getInfo?userId=1 經(jīng)過(guò)網(wǎng)關(guān)處理后變?yōu)?/user/getInfo?userId=1&&tenantId=2333
Post請(qǐng)求:
/user/getInfo Body攜帶參數(shù)為:
{
userId: "1"
}
經(jīng)過(guò)網(wǎng)關(guān)處理后變?yōu)?/p>
{
userId: "1",
tenantId: "2333"
}解決辦法
- 全局過(guò)濾器配置: 通過(guò)@Bean注解配置一個(gè)全局過(guò)濾器,用于在請(qǐng)求被轉(zhuǎn)發(fā)到微服務(wù)前進(jìn)行處理。
- 處理GET請(qǐng)求: 如果是GET請(qǐng)求,直接修改URL并返回,不對(duì)請(qǐng)求體進(jìn)行修改。
- 處理非GET請(qǐng)求: 對(duì)非GET請(qǐng)求,使用裝飾者模式創(chuàng)建ModifyRequestBodyServerHttpRequestDecorator對(duì)象,對(duì)請(qǐng)求體進(jìn)行修改。
- 去掉Content-Length頭: 在修改請(qǐng)求體的同時(shí),通過(guò)mutate()方法去掉請(qǐng)求頭中的Content-Length。
- 修改請(qǐng)求體的裝飾者類: 定義了一個(gè)內(nèi)部類ModifyRequestBodyServerHttpRequestDecorator,繼承自ServerHttpRequestDecorator,用于實(shí)現(xiàn)請(qǐng)求體的修改。
代碼示例:
// 導(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 {
// 配置全局過(guò)濾器
@Bean
public GlobalFilter customGlobalFilter() {
return (exchange, chain) -> {
// 獲取原始請(qǐng)求對(duì)象
ServerHttpRequest request = exchange.getRequest();
// 構(gòu)建URI組件構(gòu)建器,用于修改請(qǐng)求URL
UriComponentsBuilder uriBuilder = UriComponentsBuilder.fromUri(request.getURI());
// 初始化租戶ID
String tenantId = "";
// 檢查請(qǐng)求頭中是否包含 "TenantId",如果有則獲取其值
if (request.getHeaders().containsKey("TenantId")) {
tenantId = request.getHeaders().get("TenantId").get(0);
uriBuilder.queryParam("tenantId", tenantId);
}
// 如果請(qǐng)求是GET請(qǐng)求,則直接返回
if (request.getMethodValue().equals("GET")) {
log.info("請(qǐng)求是Get請(qǐng)求,url is {}", uriBuilder.build().toUri());
ServerHttpRequest modifiedRequest = request.mutate().uri(uriBuilder.build().toUri()).build();
// 創(chuàng)建新的ServerWebExchange,該對(duì)象包含修改后的請(qǐng)求
ServerWebExchange modifiedExchange = exchange.mutate().request(modifiedRequest).build();
// 繼續(xù)執(zhí)行過(guò)濾器鏈
return chain.filter(modifiedExchange);
}
// 使用裝飾者模式修改請(qǐng)求體
ServerHttpRequest modifiedRequest = new ModifyRequestBodyServerHttpRequestDecorator(request, tenantId, exchange.getResponse().bufferFactory());
// 去掉Content-Length請(qǐng)求頭
modifiedRequest = modifiedRequest.mutate().header("Content-Length", (String) null).build();
// 創(chuàng)建新的ServerWebExchange,該對(duì)象包含修改后的請(qǐng)求
ServerWebExchange modifiedExchange = exchange.mutate().request(modifiedRequest).build();
// 繼續(xù)執(zhí)行過(guò)濾器鏈
return chain.filter(modifiedExchange);
};
}
// 定義修改請(qǐng)求體的裝飾者類
private static class ModifyRequestBodyServerHttpRequestDecorator extends ServerHttpRequestDecorator {
private final String tenantId;
private final DataBufferFactory bufferFactory;
private final ObjectMapper objectMapper = new ObjectMapper();
// 構(gòu)造方法,傳入原始請(qǐng)求、tenantId和數(shù)據(jù)緩沖工廠
ModifyRequestBodyServerHttpRequestDecorator(ServerHttpRequest delegate, String tenantId, DataBufferFactory bufferFactory) {
super(delegate);
this.tenantId = tenantId;
this.bufferFactory = bufferFactory;
}
// 重寫獲取請(qǐng)求體的方法,對(duì)請(qǐng)求體進(jìn)行修改
@NotNull
@Override
public Flux<DataBuffer> getBody() {
return super.getBody().map(dataBuffer -> {
// 讀取原始請(qǐng)求體數(shù)據(jù)
byte[] bytes = new byte[dataBuffer.readableByteCount()];
dataBuffer.read(bytes);
String body = new String(bytes, StandardCharsets.UTF_8);
// 修改請(qǐng)求體內(nèi)容
String newBody = modifyJsonBody(body);
// 創(chuàng)建新的 DataBuffer
byte[] newData = newBody.getBytes(StandardCharsets.UTF_8);
return bufferFactory.wrap(newData);
});
}
// 對(duì) JSON 請(qǐng)求體進(jìn)行修改,添加 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è)計(jì)模式,它允許你通過(guò)將對(duì)象放入包含行為的特殊封裝類中來(lái)為原始對(duì)象添加新的行為。這種模式能夠在不修改原始對(duì)象的情況下,動(dòng)態(tài)地?cái)U(kuò)展其功能。在上段代碼里,主要使用裝飾者模式去修改Body 的傳參。
主要角色:
- Component(組件): 定義一個(gè)抽象接口或抽象類,聲明對(duì)象的一些基本操作。
- ConcreteComponent(具體組件): 實(shí)現(xiàn)了Component接口,是被裝飾的具體對(duì)象,也是我們最終要添加新行為的對(duì)象。
- Decorator(裝飾者抽象類): 繼承了Component,并持有一個(gè)Component對(duì)象的引用,同時(shí)實(shí)現(xiàn)了Component定義的接口。它可以通過(guò)該引用調(diào)用Component的操作,同時(shí)可以添加、擴(kuò)展或修改Component的行為。
- ConcreteDecorator(具體裝飾者): 擴(kuò)展Decorator,具體實(shí)現(xiàn)新行為的類。
裝飾者模式的工作流程:
- 客戶端通過(guò)Component接口與ConcreteComponent對(duì)象進(jìn)行交互。
- ConcreteComponent對(duì)象處理客戶端的請(qǐng)求。
- 客戶端可以通過(guò)Decorator接口與ConcreteDecorator對(duì)象進(jìn)行交互,Decorator持有ConcreteComponent的引用。
- ConcreteDecorator在調(diào)用ConcreteComponent的操作前后,可以添加、擴(kuò)展或修改行為。
給普通咖啡加點(diǎn)糖和牛奶
代碼示例:
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)建一個(gè)簡(jiǎn)單的咖啡
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)處攔截并修改請(qǐng)求的文章就介紹到這了,更多相關(guān)SpringCloud Gateway網(wǎng)關(guān)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
idea?maven依賴引入失效無(wú)法正常導(dǎo)入依賴問(wèn)題的解決方法
有時(shí)候idea導(dǎo)入一個(gè)新項(xiàng)目,或者pom文件修改(新增)了依賴,pom文件和代碼會(huì)報(bào)紅,提示依賴包不存在,下面這篇文章主要給大家介紹了關(guān)于idea?maven依賴引入失效無(wú)法正常導(dǎo)入依賴問(wèn)題的解決方法,需要的朋友可以參考下2023-04-04
Spring Boot簡(jiǎn)單實(shí)現(xiàn)快速搭建圖解
這篇文章主要介紹了Spring Boot簡(jiǎn)單實(shí)現(xiàn)快速搭建圖解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-11-11
JMeter自定義日志與日志分析的實(shí)現(xiàn)
JMeter與Java程序一樣,會(huì)記錄事件日志,本文就介紹一下JMeter自定義日志與日志分析的實(shí)現(xiàn),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-12-12
springboot集成mybatisplus實(shí)例詳解
這篇文章主要介紹了springboot集成mybatisplus實(shí)例詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-09-09
Java中?SLF4J和Logback和Log4j和Logging的區(qū)別與聯(lián)系
這篇文章主要介紹了Java中?SLF4J和Logback和Log4j和Logging的區(qū)別與聯(lián)系,文章通過(guò)圍繞主題展開(kāi)詳細(xì)的內(nèi)容介紹,具有一定的參考幾種,感興趣的小伙伴可以參考一下2022-09-09
Java實(shí)現(xiàn)把文件壓縮成zip文件的示例代碼
這篇文章主要為大家介紹了如何通過(guò)Java語(yǔ)言實(shí)現(xiàn)將文件壓縮成zip文件,本文中示例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-02-02
Java中字符串根據(jù)寬度(像素)換行的問(wèn)題
這篇文章主要介紹了Java中字符串根據(jù)寬度(像素)換行的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-09-09
JDK動(dòng)態(tài)代理與CGLib動(dòng)態(tài)代理的區(qū)別對(duì)比
今天小編就為大家分享一篇關(guān)于JDK動(dòng)態(tài)代理與CGLib動(dòng)態(tài)代理的區(qū)別對(duì)比,小編覺(jué)得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來(lái)看看吧2019-02-02

