jjzjj

4. Spring Cloud Gateway自定义Filter

91猿说编程 2024-05-14 原文

1. Filter介绍

Filter是实现在网关路由之前或之后加入一些逻辑,或者拦截过滤的工具, 称为过滤器。 在Spring Cloud Gateway中除了内置的Filter之外,也可以自定义Filter来实现自己想要的功能。

原理图如下:

2. 内置Filter

Spring cloud gateway 内置了很多的filter,单一的有32种,全局的有9种。这个数量实在是太惊人了,而且用法都比较简单,所以这里大家可以自行根据官网给出的演示进行练习 官方网址:Spring Cloud Gateway

GateWay内置的Filter生命周期为两种:

  1. pre(业务逻辑之前)

  2. post(业务逻辑之后)

    GateWay本身自带的Filter分为两种:

  3. GateWayFilter(单一)

  4. GlobalFilter(全局)

StripPrefix

这是一个内置的Filter,其功能是:将请求发送到下游之前要从请求中剥离部分路径。

需求如下:

我们有个后端服务,路径为http://localhost:8081/getServerPort,这个服务是其他人提供的,这个路径我们改不了, 但是我们网关提供的请求地址是带/test开头的, 例如:http://localhost:8888/test/getServerPort。这个访问地址我们也不能改,因为还有其他地方也是类似的地址,不能改统一的规则, 现在需要将这个地址路由到后端http://localhost:8081/getServerPort上,我们就可以通过StripPrefix来实现。

后端服务接口代码

@RestController
public class TestController {
    @Value("${server.port}")
    private String serverPort;

    @GetMapping("/getServerPort")
    public String getServerPort() {
        return "Hello Nacos Discovery Port:" + serverPort;
    }
}

配置网关

server.port=8888
spring.application.name=gateway-app
spring.cloud.nacos.discovery.server-addr=192.168.43.11:8848
management.endpoints.web.exposure.include=*
spring.cloud.gateway.discovery.locator.enabled=true

spring.cloud.gateway.routes[0].id=nacos-provider
spring.cloud.gateway.routes[0].uri=lb://nacos-provider
# 访问路径Path=/test/**
spring.cloud.gateway.routes[0].predicates[0]=Path=/test/**
# 网关访问/test/** 时,将转发给nacos-provider服务,会去除第一个路径/test。
# 所以最后访问地址是lb://nacos-provider/**
spring.cloud.gateway.routes[0].filters[0]=StripPrefix=1

通过网关访问服务

3. 自定义Filter

自定义filter也可以实现两种,一种是全局生效的,一种是某种路由生效的,需要配置。 我们先来实现,某种路由生效的,它要相对复杂一些。

1. 需求如下

我们有个后端服务,路径为http://localhost:8081/test/getServerPort,这个服务是其他人提供的,这个路径我们改不了, 但是我们网关提供的请求地址是不带/test开头的, 例如:http://localhost:8888/getServerPort。这个访问地址我们也不能改,因为还有其他地方也是类似的地址,不能改统一的规则, 现在需要将这个地址路由到后端http://localhost:8081/test/getServerPort上,我们就可以通过自定义Filter来实现。

可以看出该需求刚好和上一节的需求相反。

2. 建立AddPrefixFilter

这个AddPrefixFilter需要继承自AbstractGatewayFilterFactory<AddPrefixFilter.Config>

@Component
@Slf4j
public class AddPrefixFilter extends AbstractGatewayFilterFactory<AddPrefixFilter.Config> {
    public AddPrefixFilter() {
        //指定可接收配置数据的类
        super(AddPrefixFilter.Config.class);
    }
    public List<String> shortcutFieldOrder() {
        //返回可配置的字段
        return Collections.singletonList("prefix");
    }
    @Override
    public GatewayFilter apply(Config config) {
        return (exchange, chain) -> {
            ServerHttpRequest req = exchange.getRequest();
            String newPath = config.prefix + req.getURI().getRawPath();
            ServerHttpRequest request = req.mutate().path(newPath).build();
            return chain.filter(exchange.mutate().request(request).build());
        };
    }
    @Override
    public String name() {
        //返回用于配置的名称
        return "AddPrefix";
    }
    @Getter
    @Setter
    public static class Config {
        //用于接收可配置字段的值
        private String prefix;
    }
}

3. 添加配置如下

server.port=8888
spring.application.name=gateway-app
spring.cloud.nacos.discovery.server-addr=192.168.43.11:8848
management.endpoints.web.exposure.include=*
spring.cloud.gateway.discovery.locator.enabled=true

spring.cloud.gateway.routes[0].id=nacos-provider
spring.cloud.gateway.routes[0].uri=lb://nacos-provider
# 访问路径Path=/test/**
spring.cloud.gateway.routes[0].predicates[0]=Path=/**
# 自定义过滤器AddPrefix,在访问uri时添加/test路径在url前面
spring.cloud.gateway.routes[0].filters[0]=**AddPrefix=/test**

4. 后端服务接口

@RestController
public class TestController implements ProviderOpenApis {
    @Value("${server.port}")
    private String serverPort;

    @GetMapping("/test/getServerPort")
    public String getServerPort() {
        return "Hello Nacos Discovery Port:" + serverPort;
    }
}

5. 测试

4. 自定义全局Filter

自定义全局filter相对简单一些,因为全局filter不用管具体的那个路由器才生效。 所以在全局只有一个filter。

1. 需求如下

进入系统时如果参数中没有username用户名,则人为这个请求是非法请求,否则就通过请求。

2. 代码如下

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.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

/**
 * 进入系统时如果参数中没有username用户名,则人为这个请求是非法请求,否则就通过请求。
 * 实现Ordered,需要返回一个整数,表示加载过滤器的顺序, 整数数字越小优先级越高
 * 实现GlobalFilter,实现过滤器逻辑
 */
@Component
@Slf4j
public class UserNameCheckFilter implements Ordered, GlobalFilter {
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        String username = exchange.getRequest().getQueryParams().getFirst("username");
        if (username == null) {
            //如果username为空,返回状态码为406,不可接受的请求
            exchange.getResponse().setStatusCode(HttpStatus.NOT_ACCEPTABLE);
            return exchange.getResponse().setComplete();
        }
        return chain.filter(exchange);
    }

    @Override
    public int getOrder() {
        return 0;
    }
}

3. 测试

访问时不带用户名username字段,返回406

访问是带了username,正常请求

5. Spring cloud gateway中的默认filter

具体请参考官方文档:

Spring Cloud Gateway

可以使用翻译成中文查看。

有关4. Spring Cloud Gateway自定义Filter的更多相关文章

  1. ruby - Facter::Util::Uptime:Module 的未定义方法 get_uptime (NoMethodError) - 2

    我正在尝试设置一个puppet节点,但ruby​​gems似乎不正常。如果我通过它自己的二进制文件(/usr/lib/ruby/gems/1.8/gems/facter-1.5.8/bin/facter)在cli上运行facter,它工作正常,但如果我通过由ruby​​gems(/usr/bin/facter)安装的二进制文件,它抛出:/usr/lib/ruby/1.8/facter/uptime.rb:11:undefinedmethod`get_uptime'forFacter::Util::Uptime:Module(NoMethodError)from/usr/lib/ruby

  2. ruby-on-rails - Rails 3.2.1 中 ActionMailer 中的未定义方法 'default_content_type=' - 2

    我在我的项目中添加了一个系统来重置用户密码并通过电子邮件将密码发送给他,以防他忘记密码。昨天它运行良好(当我实现它时)。当我今天尝试启动服务器时,出现以下错误。=>BootingWEBrick=>Rails3.2.1applicationstartingindevelopmentonhttp://0.0.0.0:3000=>Callwith-dtodetach=>Ctrl-CtoshutdownserverExiting/Users/vinayshenoy/.rvm/gems/ruby-1.9.3-p0/gems/actionmailer-3.2.1/lib/action_mailer

  3. ruby-on-rails - form_for 中不在模型中的自定义字段 - 2

    我想向我的Controller传递一个参数,它是一个简单的复选框,但我不知道如何在模型的form_for中引入它,这是我的观点:{:id=>'go_finance'}do|f|%>Transferirde:para:Entrada:"input",:placeholder=>"Quantofoiganho?"%>Saída:"output",:placeholder=>"Quantofoigasto?"%>Nota:我想做一个额外的复选框,但我该怎么做,模型中没有一个对象,而是一个要检查的对象,以便在Controller中创建一个ifelse,如果没有检查,请帮助我,非常感谢,谢谢

  4. ruby - 主要 :Object when running build from sublime 的未定义方法 `require_relative' - 2

    我已经从我的命令行中获得了一切,所以我可以运行rubymyfile并且它可以正常工作。但是当我尝试从sublime中运行它时,我得到了undefinedmethod`require_relative'formain:Object有人知道我的sublime设置中缺少什么吗?我正在使用OSX并安装了rvm。 最佳答案 或者,您可以只使用“require”,它应该可以正常工作。我认为“require_relative”仅适用于ruby​​1.9+ 关于ruby-主要:Objectwhenrun

  5. ruby - 在 Ruby 中有条件地定义函数 - 2

    我有一些代码在几个不同的位置之一运行:作为具有调试输出的命令行工具,作为不接受任何输出的更大程序的一部分,以及在Rails环境中。有时我需要根据代码的位置对代码进行细微的更改,我意识到以下样式似乎可行:print"Testingnestedfunctionsdefined\n"CLI=trueifCLIdeftest_printprint"CommandLineVersion\n"endelsedeftest_printprint"ReleaseVersion\n"endendtest_print()这导致:TestingnestedfunctionsdefinedCommandLin

  6. ruby - 定义方法参数的条件 - 2

    我有一个只接受一个参数的方法:defmy_method(number)end如果使用number调用方法,我该如何引发错误??通常,我如何定义方法参数的条件?比如我想在调用的时候报错:my_method(1) 最佳答案 您可以添加guard在函数的开头,如果参数无效则引发异常。例如:defmy_method(number)failArgumentError,"Inputshouldbegreaterthanorequalto2"ifnumbereputse.messageend#=>Inputshouldbegreaterthano

  7. ruby - 如何在 Grape 中定义哈希数组? - 2

    我使用Ember作为我的前端和GrapeAPI来为我的API提供服务。前端发送类似:{"service"=>{"name"=>"Name","duration"=>"30","user"=>nil,"organization"=>"org","category"=>nil,"description"=>"description","disabled"=>true,"color"=>nil,"availabilities"=>[{"day"=>"Saturday","enabled"=>false,"timeSlots"=>[{"startAt"=>"09:00AM","endAt"=>

  8. ruby - 获取模块中定义的所有常量的值 - 2

    我想获取模块中定义的所有常量的值:moduleLettersA='apple'.freezeB='boy'.freezeendconstants给了我常量的名字:Letters.constants(false)#=>[:A,:B]如何获取它们的值的数组,即["apple","boy"]? 最佳答案 为了做到这一点,请使用mapLetters.constants(false).map&Letters.method(:const_get)这将返回["a","b"]第二种方式:Letters.constants(false).map{|c

  9. ruby - 这两个 Ruby 类初始化定义有什么区别? - 2

    我正在阅读一本关于Ruby的书,作者在编写类初始化定义时使用的形式与他在本书前几节中使用的形式略有不同。它看起来像这样:classTicketattr_accessor:venue,:datedefinitialize(venue,date)self.venue=venueself.date=dateendend在本书的前几节中,它的定义如下:classTicketattr_accessor:venue,:datedefinitialize(venue,date)@venue=venue@date=dateendend在第一个示例中使用setter方法与在第二个示例中使用实例变量之间是

  10. ruby-on-rails - before_filter 运行多个方法 - 2

    是否有可能:before_filter:authenticate_user!||:authenticate_admin! 最佳答案 before_filter:do_authenticationdefdo_authenticationauthenticate_user!||authenticate_admin!end 关于ruby-on-rails-before_filter运行多个方法,我们在StackOverflow上找到一个类似的问题: https://

随机推荐