Spring cloud gateway(Web MVC)的使用全集

Spring cloud gateway(Web MVC)的使用全集
一定是spring boot项目,spring cloud gateway基于spring boot

文章目录


前言

提示:本文所用的是spring cloud gateway web mvc特性
本文提供了对spring cloud gateway网关的使用案例,并没有列举出所有过滤器或谓词的使用示例,但是包含了从工程搭建及基本使用至概念理解再到一些自定义客制化的使用。为你打开基本使用到深入使用的一扇门。所配关键代码都有注释,一定可以为你答疑解惑。

一、前置条件

提示:spring cloud gateway是基于spring boot,项目一定是spring boot项目,一定是spring boot项目
jdk:17
构建工具:maven 3.6.3
spring相关jar:spring-cloud-starter-gateway-server-webmvc 4.3.0(必须),spring boot 3.5.8(被间接依赖至项目中),spring cloud 2025.0.0(作为pom)
参考资料:https://docs.spring.io/spring-cloud-gateway/reference/4.3/spring-cloud-gateway-server-webmvc.html
开发工具:idea 2021.2

二、工程搭建(创建一个普通maven工程,添加必要依赖及构建工具即可,本案例是一个maven子工程)

提示:按需可引入spring boot starter parent,直接按需用spring 配套的插件及依赖包,更为方便
父工程pom.xml (仅列出关键依赖)

pom配置


工程目录

工程目录

pom.xml文件

<?xml version="1.0" encoding="UTF-8"?><projectxmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><parent><artifactId>springCloudMicroService</artifactId><groupId>org.example</groupId><version>1.0-SNAPSHOT</version></parent><modelVersion>4.0.0</modelVersion><artifactId>gatewayServerWebMvcDemo</artifactId><properties><maven.compiler.source>17</maven.compiler.source><maven.compiler.target>17</maven.compiler.target><!-- 设置编码格式,防止maven打包构建时没有统一编码格式导致有中文注释的文件解析异常,影响项目启动 --><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding></properties><dependencies><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-gateway-server-webmvc</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency></dependencies><!-- spring boot构建相关配置 --><build><plugins><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin</artifactId><configuration><parameters>true</parameters></configuration></plugin><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId><version>3.5.8</version><configuration><mainClass>${start-class}</mainClass><layout>JAR</layout></configuration><executions><execution><goals><goal>repackage</goal></goals></execution></executions></plugin><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-resources-plugin</artifactId><version>3.1.0</version><configuration><!-- 【很关键】自定义${}占位符,用于将pom文件的变量动态替换到applicaton**.yml的某处 例如这里的application.yml文件,使用${spring.profiles.active}取 将pom.xml->properties->profile->properties->spring.profiles.active属性值 --><delimiters><delimiter>${}</delimiter></delimiters><!-- 禁用默认的占位符@xxPropertiesName@ --><useDefaultDelimiters>false</useDefaultDelimiters><resources><resource><directory>src/main/resources</directory><filtering>true</filtering><!-- 根据需要开启或关闭 --><includes><include>public/**</include><include>static/**</include><include>templates/**</include><include>mapper/**</include><include>properties/**</include><include>i18n/**</include><include>logback*.xml</include><!-- 包含默认的配置文件,这个配置文件仅有spring.profiles.active属性,这是不加profiles启动的关键 --><include>application.yml</include><!-- 按maven不同的打包参数,打包不同的环境相关的配置文件 --><include>application-${spring.profiles.active}.*</include><include>log*-${spring.profiles.active}.xml</include></includes></resource></resources></configuration></plugin></plugins></build><!-- profiles 相关配置 --><profiles><!-- profile中的<id>div</id>元素值指的是-P 的参数, 如:mvn clean install -P div profile中的<properties><spring.profiles.active>dev</spring.profiles.active></properties>中的spring.profiles.active元素及值 值指的是-D 的参数, 如:mvn clean install -Dspring.profiles.active=dev --><profile><!-- 开发环境 --><id>dev</id><properties><spring.profiles.active>dev</spring.profiles.active></properties><activation><activeByDefault>true</activeByDefault></activation></profile><!-- 测试环境 --><profile><id>sit</id><properties><spring.profiles.active>sit</spring.profiles.active></properties><activation><activeByDefault>false</activeByDefault></activation></profile><!-- 生产环境 --><profile><id>prod</id><properties><spring.profiles.active>prod</spring.profiles.active></properties><activation><activeByDefault>false</activeByDefault></activation></profile></profiles></project>

三、功能实现范围

  • 配置文件或java编程方式的路由配置
  • 自定义过滤器(filter)、自定义谓词(predicate)的实现与使用
  • 路由到本服务接口(就是用于spring mvc的函数式端点的使用)

四、需要路由访问的服务(应用)准备

网关自身(服务/应用):http://localhost:8888
应用一:http://localhost:8081/entitleservice
应用二:http://localhost:8082/functonservletpath

五、路由配置(配置文件方式)

提示:两种路由配置方式二选一,二选一,二选一!!!
默认配置文件(application.yml)

server:server-name: gatewaymvcdemo port:8888spring:profiles:# active: javaconfigactive: fileconfig 

路由配置文件(application-fileconfig.yml)

# 使用配置文件方式配置路由spring:cloud:gateway:server:webmvc:routes:# 配置路由到function demo服务-id: function_demo_route uri: http://localhost:8082predicates:- Method=GET,PUT,DELETE,POST - Path=/functionservletpath/**filters:- AddRequestParameter=code, gateway for functiondemo - AddRequestHeader=function-Request-Id,function_header_value -id: entitle_route uri: http://localhost:8081predicates:- Method=GET,PUT,DELETE,POST - Path=/entitleservice/info/**,/entitleservice/gatewayInfo/**,/entitleservice/user/**,/entitleservice/role/**- predicateHeaderExists=custom_header filters:- AddRequestParameter=code, gate way for entitleservice - AddRequestHeader=entitle-Request-Id,entitle_header_value - instrumentForFilter=req_header,rep_header 

六、路由配置(java编程方式)

提示:两种路由配置方式二选一,二选一,二选一!!!!
默认配置文件(application.yml)

server:server-name: gatewaymvcdemo port:8888spring:profiles:active: javaconfig 

路由配置文件(application-javaconfig.yml)
提示:路由配置文件为空,因使用java编程方式配置
java配置文件(ApplicationRoutingConfig.java)

packagecom.mrhan.config;importcom.mrhan.service.GatewayHandler;importcom.mrhan.service.UserHandler;importorg.slf4j.Logger;importorg.slf4j.LoggerFactory;importorg.springframework.context.annotation.Bean;importorg.springframework.context.annotation.Configuration;importorg.springframework.context.annotation.Profile;importorg.springframework.http.HttpMethod;importorg.springframework.http.MediaType;importorg.springframework.web.servlet.function.*;importjava.time.ZonedDateTime;importjava.util.function.BiFunction;importjava.util.function.Function;importstaticorg.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.*;importstaticorg.springframework.cloud.gateway.server.mvc.handler.GatewayRouterFunctions.route;importstaticorg.springframework.cloud.gateway.server.mvc.handler.HandlerFunctions.http;importstaticorg.springframework.cloud.gateway.server.mvc.predicate.GatewayRequestPredicates.*;importstaticorg.springframework.web.servlet.function.RequestPredicates.accept;/** * @author Mr han * @date 2025/12/6 * @description 路由配置,此处配置了路由后,application.yml中配置的路由将无效,两种配置只能存在一种,不能并存 * 同等效果配置文件方式配置@see application.yml,自定义的filter和predicate在两种配置方式都能够生效 * * */@Configuration(proxyBeanMethods =false)@Profile("javaconfig")publicclassApplicationRoutingConfig{privatestaticfinalLogger log =LoggerFactory.getLogger(ApplicationRoutingConfig.class);/** * * @return * @date 2025/12/17 * @description 使用java代码方式配置路由,等价于@see application.yml文件配置方式 * */@BeanpublicRouterFunction<ServerResponse>customRoutes(GatewayHandler gatewayHandler){// @formatter:offreturnroute("path_route").GET("/gateway/getRole",accept(MediaType.TEXT_HTML),gatewayHandler::getRole).POST("/gateway/updateRole/{roleId}",accept(MediaType.APPLICATION_JSON), gatewayHandler::updateRole).before(request ->{String path = request.requestPath().value(); log.error("=========开始进入本服务请求:{}=========",path);return request;})// 添加请求之后的函数处理器.after((request,response)->{ log.error("==========本服务请求处理完毕:{},响应的状态码:",request.requestPath().value(),response.statusCode());return response;}).build().and(route("entitle_route").route(path("/entitleservice/**").and(SampleRequestPredicates.predicateHeaderExists("entitle_header")),http()).before(uri("http://localhost:8081")).before(addRequestParameter("code","Java gateway for entitlementservice")).before(addRequestHeader("java_config_header","java config value")).filter(SampleHandlerFilterFunctions.instrumentForFilter("req_header","rep_header")).build().and(route("function_demo_route").route(path("/functionservletpath/**").and(SampleRequestPredicates.predicateHeaderExists("function_header")),http()).before(uri("http://localhost:8082")).before(addRequestParameter("code","Java config gateway for functiondemo")).before(addRequestHeader("function-Request-Id","function_header_value")).build()));}/** * @author Mr han * @date 2025/12/15 * @description 自定义的predicate 是否包含某个固定的请求头,此类的实现等等同于方法 @see {@link SampleRequestPredicates#predicateHeaderExists(String)} * */privatestaticclassCustomHeaderExistsimplementsRequestPredicate{// 请求头名称privatestaticfinalString HEADER_NAME ="custom_header";/** * * @return * @date 2025/12/15 * @description 需要包含custom_header请求头 * */@Overridepublicbooleantest(ServerRequest request){return request.headers().asHttpHeaders().containsKey(HEADER_NAME);}}/** * * @return * @date 2025/12/15 * @description 使用函数式的方法,自定义过滤器,使用函数式方法实现过滤器 * 在自定义的过滤器中可以实现日志的打印,已经权限的认证等逻辑,这里仅仅是增加请求头及响应头 * 此方法等同于@see {@link SampleHandlerFilterFunctions#instrumentForFilter(String, String)} * */privatestaticHandlerFilterFunction<ServerResponse,ServerResponse>instrumentWithFilter(String requestHeader,String responseHeader){return(request,next)->{ServerRequest modifier =ServerRequest.from(request).header(requestHeader,"hello").build();ServerResponse responseHandler = next.handle(modifier); responseHandler.headers().add(responseHeader,"world");return responseHandler;};}/** * * @return * @date 2025/12/15 * @description 使用函数式的方法,定义before方式的过滤器 * */privatestaticFunction<ServerRequest,ServerRequest>instrumentWithBefore(String requestHeader){return request ->{returnServerRequest.from(request).header(requestHeader,"before_header_value").build();};}/** * * @return * @date 2025/12/15 * @description 使用函数式的方法,定义after方式的过滤器 * */privatestaticBiFunction<ServerRequest,ServerResponse,ServerResponse>instrumentWithAfter(String header){return(request,response)->{ response.headers().add(header,"after_header_value");return response;};}}

七、自定义过滤器

· 提示:定义的过滤器类按一个类一个静态方法,多增加几个静态方法spring也只会加载第一个静态方法,静态方法一定要加@Shotcut注解,不然启动报错,报错原因分析及解决,将在另一篇关于gateway疑难杂症解决的文章中展开。
过滤器定义(SampleHandlerFilterFunctions.java):一个类一个静态过滤器方法(方法上一定要加上@Shortcut注解),多加了也没用spring只会加载第一个静态方法

packagecom.mrhan.config;importorg.slf4j.Logger;importorg.slf4j.LoggerFactory;importorg.springframework.cloud.gateway.server.mvc.common.Configurable;importorg.springframework.cloud.gateway.server.mvc.common.Shortcut;importorg.springframework.util.StringUtils;importorg.springframework.web.servlet.function.HandlerFilterFunction;importorg.springframework.web.servlet.function.ServerRequest;importorg.springframework.web.servlet.function.ServerResponse;importjava.util.Objects;importjava.util.function.Function;/** * @author Mr han * @date 2025/12/15 * @description 定义包含所有自定义filter的函数式过滤器类,供filtersupplier类使用暴露到项目中 * 一个类只能定义一个静态方法,spring cloud gateway只注册一个静态方法 */publicclassSampleHandlerFilterFunctions{privatestaticfinalLogger log =LoggerFactory.getLogger(SampleHandlerFilterFunctions.class);@ShortcutpublicstaticHandlerFilterFunction<ServerResponse,ServerResponse>instrumentForFilter(String reqHeaderName,String repHeaderName){return(request, next)->{ log.error("==========进入自定义filter处理逻辑,请求头:{},响应头{}===========",reqHeaderName,repHeaderName);ServerRequest modified =ServerRequest.from(request).header(reqHeaderName,"request header for filter").build();ServerResponse response = next.handle(modified); response.headers().add(repHeaderName,"response header for filter");return response;};}

过滤器提供者(CustomFilterSupplier.java):就是一个继承了SimpleFilterSupplier类的类而已,重写了继承方法

packagecom.mrhan.config;importorg.springframework.cloud.gateway.server.mvc.filter.SimpleFilterSupplier;/** * @author Mr han * @date 2025/12/15 * @description 创建字对应的filterSupplier,用于注册提供自定义的filter */publicclassCustomFilterSupplierextendsSimpleFilterSupplier{publicCustomFilterSupplier(){super(SampleHandlerFilterFunctions.class);}}

过滤器配置类(FilterConfiguration.java):只是为了将过滤器提供者注册为Bean

packagecom.mrhan.config;importjakarta.annotation.PostConstruct;importorg.slf4j.Logger;importorg.slf4j.LoggerFactory;importorg.springframework.context.annotation.Bean;importorg.springframework.context.annotation.Configuration;importorg.springframework.context.annotation.Profile;/** * @author Mr han * @date 2025/12/17 * @description 注册自定义的过滤器,用于配置在application.yml文件中,目前使用此配置方式无实际效果 TODO */@Configuration@Profile("fileconfig")publicclassFilterConfiguration{privatestaticfinalLogger log =LoggerFactory.getLogger(FilterConfiguration.class);@PostConstructpublicvoidinit(){ log.error("========开始加载FilterConfiguration配置============");}@BeanpublicCustomFilterSuppliercustomFilterSupplier(){returnnewCustomFilterSupplier();}}

八、自定义谓词

提示:定义的谓词按一个类一个静态方法,多增加几个静态方法spring也只会加载第一个静态方法,静态方法一定要加@Shortcut注解,不然启动报错,报错原因分析及解决,将在另一篇关于gateway疑难杂症解决的文章中展开。
谓词定义(SampleRequestPredicates.java):一个类一个静态过滤器方法(一定要加上@ShortCut注解),多加了也没用spring只会加载第一个静态方法

packagecom.mrhan.config;importorg.slf4j.Logger;importorg.slf4j.LoggerFactory;importorg.springframework.cloud.gateway.server.mvc.common.Configurable;importorg.springframework.cloud.gateway.server.mvc.common.Shortcut;importorg.springframework.web.servlet.function.RequestPredicate;/** * @author Mr han * @date 2025/12/15 * @description 定义包含自定义Predicate的所有实现的类 */publicclassSampleRequestPredicates{privatestaticfinalLogger log =LoggerFactory.getLogger(SampleRequestPredicates.class);/** * * @return * @date 2025/12/15 * @description 请求头的判断(是否包含某个名称的请求头),必须添加@Configurable注解且只能有一个参数,不然无法生效 * */@ShortcutpublicstaticRequestPredicatepredicateHeaderExists(String name){ log.error("======进入了自定义predicate的处理逻辑所需请求头:{}==========",name);return request ->{return request.headers().asHttpHeaders().containsKey(name);};}}

谓词提供者(CustomPredicateSupplier.java):就是一个实现了PredicateSupplier接口的类

packagecom.mrhan.config;importorg.springframework.cloud.gateway.server.mvc.predicate.PredicateSupplier;importjava.lang.reflect.Method;importjava.util.Arrays;importjava.util.Collection;publicclassCustomPredicateSupplierimplementsPredicateSupplier{@OverridepublicCollection<Method>get(){returnArrays.asList(SampleRequestPredicates.class.getMethods());}}

谓词配置类(PredicateConfiguration.java):只是为了将谓词提供者注册为Bean

packagecom.mrhan.config;importjakarta.annotation.PostConstruct;importorg.slf4j.Logger;importorg.slf4j.LoggerFactory;importorg.springframework.cloud.gateway.server.mvc.predicate.PredicateSupplier;importorg.springframework.context.annotation.Bean;importorg.springframework.context.annotation.Configuration;importorg.springframework.context.annotation.Profile;/** * @author Mr han * @date 2025/12/17 * @description 注册自定义的predicate,用在application.yml中 */@Configuration@Profile("fileconfig")publicclassPredicateConfiguration{privatestaticfinalLogger log =LoggerFactory.getLogger(PredicateConfiguration.class);@PostConstructpublicvoidinit(){ log.error("========开始加载PredicateConfiguration配置============");}@BeanpublicCustomPredicateSuppliercustomPredicateSupplier(){returnnewCustomPredicateSupplier();}}

九、自定义谓词、自定义过滤器的使用(配置文件或java编程方式)

配置文件方式:格式"静态方法名(首字母大写小写都可)=参数值"

配置文件

java编程方式:直接调用过滤器或谓词方法即可

java编程方式

十、效果验证(配置文件方式的路由效果)

未配置指定请求头:访问404

未配置


配置指定请求头:验证自定义的过滤器,谓词等效果

配置文件

十一、一些疑问、困惑的解释

  • 配置中的uri是干嘛的?
    解释:处理转发过来的请求的服务器地址
  • 怎么理解网关?
    解释:将匹配(含你定义的请求路径、携带了某个请求头、请求参数等)你谓词(predicate)的请求经过一系列处理(过滤器的路径重写、添加请求头、参数等)后转给你指定的处理器(可能是访问的服务地址,可能是你指定处理该请求的一个方法)做处理,是请求访问的守门员。

总结

每天成长,每天干货,关注我不迷路,用最小的成本掌握最关键的技术。

Read more

Linux网络 | 理解Web路径 以及 实现一个简单的helloworld网页

Linux网络 | 理解Web路径 以及 实现一个简单的helloworld网页

前言:本节内容承接上节课的http相关的概念, 主要是实现一个简单的接收http协议请求的服务。这个程序对于我们理解后面的http协议的格式,报头以及网络上的资源的理解, 以及本节web路径等等都有着重要作用。 可以说我们就用代码来理解这些东西。 那么废话不多说, 现在开始我们的学习吧。         ps:本节内容建议先看一下上一篇文章http的相关概念哦:linux网络 | 深度学习http的相关概念-ZEEKLOG博客 目录  准备文件  makefile HttpServer.hpp 类内成员 封装sockfd start  ThreadRun  全部代码 运行结果 响应书写 Web路径  准备文件         首先准备文件: 这里面Httpserver.cc用来运行接收http请求的服务。 HttpServer.hpp用来定义http请求。Log.hpp就是一个打印日志的小组件, Socket.hpp同样是套接字的组件。 到使用直接调用相关接口即可。(Log.hpp和Socket.hpp如何实现不讲解, 如果想要知道

前端八股文面经大全:字节前端一面(2026-2-1)·面经深度解析

前端八股文面经大全:字节前端一面(2026-2-1)·面经深度解析

前言 大家好,我是木斯佳。 在这个春节假期,当大家都在谈论返乡、团圆与休息时,作为一名技术人,我的思考却不由自主地转向了行业的「冬」与「春」。 相信很多人都感受到了,在AI浪潮的席卷之下,前端领域的门槛在变高,纯粹的“增删改查”岗位正在肉眼可见地减少。曾经热闹非凡的面经分享,如今也沉寂了许多。但我们都知道,市场的潮水退去,留下的才是真正在踏实准备、努力沉淀的人。学习的需求,从未消失,只是变得更加务实和深入。 正值春节,也是复盘与规划的好时机。结合ZEEKLOG这次「春节代码贺新年」活动所提倡的“用技术视角记录春节、复盘成长”,我决定在这个假期持续更新专栏,帮助年后参加春招的同学。 这个专栏的初衷很简单:拒绝过时的、流水线式的PDF引流贴,专注于收集和整理当下最新、最真实的前端面试资料。 我会在每一份面经和八股文的基础上,尝试从面试官的角度去拆解问题背后的逻辑,而不仅仅是提供一份静态的背诵答案。无论你是校招还是社招,目标是中大厂还是新兴团队,只要是真实发生、有价值的面试经历,我都会在这个专栏里为你沉淀下来。 温馨提示:市面上的面经鱼龙混杂,

无需编程!Fun-ASR WebUI界面手把手操作教程

无需编程!Fun-ASR WebUI界面手把手操作教程 你是不是也遇到过这些情况:会议录音堆在文件夹里没时间听,客户语音留言转文字总出错,培训音频想整理成笔记却要花半天?别再复制粘贴到网页版工具、别再折腾Python环境、更别担心“CUDA out of memory”报错——今天这篇教程,专为完全不写代码的人准备。 Fun-ASR WebUI 是钉钉联合通义实验室推出的语音识别系统,由开发者“科哥”深度优化并封装成开箱即用的图形界面。它不是命令行里的冰冷指令,也不是需要配置10个参数才能跑起来的实验项目,而是一个像微信一样点点就能用的本地语音转文字工具。全程不需要安装Python包、不用改配置文件、不用查GPU型号——只要你会打开浏览器,就能把一段30分钟的采访音频,5分钟内变成带标点、分段清晰、数字自动规整的可编辑文本。 下面我将带你从零开始,像教朋友一样,一步步操作每一个按钮、解释每一处设置、避开所有新手踩坑点。你不需要懂“VAD”是什么,也不用知道“ITN”怎么拼,只需要跟着做,就能立刻上手。 1. 启动与访问:两步打开你的语音助手

ollama v0.17.0 更新:OpenClaw 一键自动安装、Web 搜索支持、全新 Context 动态分配与 Tokenizer 性能大幅优化!

2026 年 2 月 24 日,Ollama 发布了全新的 v0.17.0 版本。这一次更新可谓意义重大,不仅引入了全新的 OpenClaw 自动化集成与安装能力,还针对核心 Tokenizer 性能、VRAM 动态上下文分配、系统配置迁移逻辑、Web 搜索插件 等功能进行了深层次优化。这是一次面向 AI 模型本地化与云端融合、性能与易用性双提升的版本升级。 本文将全面解析 Ollama v0.17.0 的更新亮点、底层代码变化及其背后的设计逻辑,帮助开发者和高级用户快速理解这一版本的重要意义。 一、版本概览 版本号: v0.17.0 发布日期: 2026 年 2 月 24 日