SpringCloudGateway网关处拦截并修改请求的操作方法

 更新时间:2023年12月23日 10:04:40   作者:风 离  
这篇文章主要介绍了SpringCloudGateway网关处拦截并修改请求的操作方法,本文通过示例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧

SpringCloudGateway网关处拦截并修改请求

需求背景

老系统没有引入Token的概念,之前的租户Id拼接在请求上,有的是以Get,Param传参形式;有的是以Post,Body传参的。需要在网关层拦截请求并进行请求修改后转发到对应服务。

举个例子:

Get请求:

/user/getInfo?userId=1 经过网关处理后变为 /user/getInfo?userId=1&&tenantId=2333

Post请求:

/user/getInfo Body携带参数为:

{
  userId: "1"
}

经过网关处理后变为

{
  userId: "1",
  tenantId: "2333"
}

解决办法

  • 全局过滤器配置: 通过@Bean注解配置一个全局过滤器,用于在请求被转发到微服务前进行处理。
  • 处理GET请求: 如果是GET请求,直接修改URL并返回,不对请求体进行修改。
  • 处理非GET请求: 对非GET请求,使用装饰者模式创建ModifyRequestBodyServerHttpRequestDecorator对象,对请求体进行修改。
  • 去掉Content-Length头: 在修改请求体的同时,通过mutate()方法去掉请求头中的Content-Length。
  • 修改请求体的装饰者类: 定义了一个内部类ModifyRequestBodyServerHttpRequestDecorator,继承自ServerHttpRequestDecorator,用于实现请求体的修改。

代码示例:

// 导入必要的类和包
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();
            // 构建URI组件构建器,用于修改请求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();
                // 创建新的ServerWebExchange,该对象包含修改后的请求
                ServerWebExchange modifiedExchange = exchange.mutate().request(modifiedRequest).build();
                // 继续执行过滤器链
                return chain.filter(modifiedExchange);
            }
            // 使用装饰者模式修改请求体
            ServerHttpRequest modifiedRequest = new ModifyRequestBodyServerHttpRequestDecorator(request, tenantId, exchange.getResponse().bufferFactory());
            // 去掉Content-Length请求头
            modifiedRequest = modifiedRequest.mutate().header("Content-Length", (String) null).build();
            // 创建新的ServerWebExchange,该对象包含修改后的请求
            ServerWebExchange modifiedExchange = exchange.mutate().request(modifiedRequest).build();
            // 继续执行过滤器链
            return chain.filter(modifiedExchange);
        };
    }
    // 定义修改请求体的装饰者类
    private static class ModifyRequestBodyServerHttpRequestDecorator extends ServerHttpRequestDecorator {
        private final String tenantId;
        private final DataBufferFactory bufferFactory;
        private final ObjectMapper objectMapper = new ObjectMapper();
        // 构造方法,传入原始请求、tenantId和数据缓冲工厂
        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 -> {
                // 读取原始请求体数据
                byte[] bytes = new byte[dataBuffer.readableByteCount()];
                dataBuffer.read(bytes);
                String body = new String(bytes, StandardCharsets.UTF_8);
                // 修改请求体内容
                String newBody = modifyJsonBody(body);
                // 创建新的 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;
            }
        }
    }
}

解决路径文章参考

http://t.csdnimg.cn/9kos5

http://t.csdnimg.cn/Aklwh

关于装饰者模式

装饰者模式是一种结构型设计模式,它允许你通过将对象放入包含行为的特殊封装类中来为原始对象添加新的行为。这种模式能够在不修改原始对象的情况下,动态地扩展其功能。在上段代码里,主要使用装饰者模式去修改Body 的传参。

主要角色:

  • Component(组件): 定义一个抽象接口或抽象类,声明对象的一些基本操作。
  • ConcreteComponent(具体组件): 实现了Component接口,是被装饰的具体对象,也是我们最终要添加新行为的对象。
  • Decorator(装饰者抽象类): 继承了Component,并持有一个Component对象的引用,同时实现了Component定义的接口。它可以通过该引用调用Component的操作,同时可以添加、扩展或修改Component的行为。
  • ConcreteDecorator(具体装饰者): 扩展Decorator,具体实现新行为的类。

装饰者模式的工作流程:

  • 客户端通过Component接口与ConcreteComponent对象进行交互。
  • ConcreteComponent对象处理客户端的请求。
  • 客户端可以通过Decorator接口与ConcreteDecorator对象进行交互,Decorator持有ConcreteComponent的引用。
  • ConcreteDecorator在调用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) {
        // 创建一个简单的咖啡
        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());
    }
}

到此这篇关于SpringCloudGateway网关处拦截并修改请求的文章就介绍到这了,更多相关SpringCloud Gateway网关内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Java中并行执行任务的多种方式

    Java中并行执行任务的多种方式

    在Java编程中,经常会遇到需要并行执行任务的情况,特别是在处理大量数据或者需要异步处理的场景下,本文将介绍几种常用的并行执行任务的方式,文中有详细的代码示例供大家参考,需要的朋友可以参考下
    2024-04-04
  • eclipse老是自动跳到console解决办法

    eclipse老是自动跳到console解决办法

    eclipse启动服务后,想看一些properties信息或者别的,但老是自动跳转到console页面,本文给大家介绍了解决办法,对大家的学习或工作有一定的帮助,需要的朋友可以参考下
    2024-03-03
  • Java中的zookeeper常用命令详解

    Java中的zookeeper常用命令详解

    这篇文章主要介绍了Java中的zookeeper常用命令,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2022-06-06
  • Spring boot GC实现过程原理解析

    Spring boot GC实现过程原理解析

    这篇文章主要介绍了Spring boot GC实现过程原理解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-08-08
  • Java-IO流实验

    Java-IO流实验

    流是一种抽象概念,它代表了数据的无结构化传递。。用来进行输入输出操作的流就称为IO流。换句话说,IO流就是以流的方式进行输入输出,希望能给您带来帮助
    2021-06-06
  • Mybatis动态sql超详细讲解

    Mybatis动态sql超详细讲解

    动态SQL是MyBatis的强大特性之一,顾名思义就是会动的SQL,即是能够灵活的根据某种条件拼接出完整的SQL语句,下面这篇文章主要给大家介绍了关于Mybatis动态sql的相关资料,需要的朋友可以参考下
    2023-04-04
  • java将html转成图片代码实例(html2image)

    java将html转成图片代码实例(html2image)

    这篇文章主要介绍了java将html转成图片的相关资料,在Java开发中,将HTML转换为图片可以使用html2image库,文中通过代码及图文介绍的非常详细,需要的朋友可以参考下
    2024-09-09
  • 关于log4j日志扩展---自定义PatternLayout

    关于log4j日志扩展---自定义PatternLayout

    这篇文章主要介绍了关于log4j日志扩展---自定义PatternLayout,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-12-12
  • SpringMVC注解@CrossOrigin跨域问题详解

    SpringMVC注解@CrossOrigin跨域问题详解

    这篇文章主要介绍了SpringMVC注解@CrossOrigin跨域问题详解,跨域是浏览同源策略的造成,是浏览器对JavaScript施加的安全限制CORS是一种可以解决跨域问题的技术,需要的朋友可以参考下
    2023-11-11
  • commons io文件操作示例分享

    commons io文件操作示例分享

    这篇文章主要介绍了commons io文件操作示例分享,需要的朋友可以参考下
    2014-02-02

最新评论