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

OCR增强与空间感知升级|Qwen3-VL-WEBUI在Dify中的实战应用

OCR增强与空间感知升级|Qwen3-VL-WEBUI在Dify中的实战应用 💡 获取更多AI镜像 想探索更多AI镜像和应用场景?访问 ZEEKLOG星图镜像广场,提供丰富的预置镜像,覆盖大模型推理、图像生成、视频生成、模型微调等多个领域,支持一键部署。 1. 引言:视觉智能的“低代码革命” 在企业数字化转型加速的今天,如何让AI真正“看懂世界”并快速落地到业务流程中,已成为技术团队的核心挑战。传统多模态系统开发周期长、依赖专业算法工程师、部署复杂——尤其在OCR识别、GUI理解、空间关系分析等任务中,往往需要定制化模型训练与大量工程适配。 而随着阿里通义千问发布 Qwen3-VL-WEBUI 镜像,这一局面正在被打破。该镜像内置了最新一代视觉语言模型 Qwen3-VL-4B-Instruct,不仅具备强大的图文理解能力,更在OCR鲁棒性、空间感知、GUI代理等方面实现全面升级。结合低代码平台 Dify,开发者无需编写任何后端代码,即可构建出具备“视觉认知+逻辑决策”能力的智能应用。 本文将深入解析 Qwen3-VL 的核心技术增强点,并通过实际案例展示其在 Dify

Flutter 三方库 jwt_io 的鸿蒙化适配指南 - 在鸿蒙系统上构建极致、严谨、全能的 JSON Web Token (JWT) 加解密与身份安全验证引擎

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.ZEEKLOG.net Flutter 三方库 jwt_io 的鸿蒙化适配指南 - 在鸿蒙系统上构建极致、严谨、全能的 JSON Web Token (JWT) 加解密与身份安全验证引擎 在鸿蒙(OpenHarmony)系统的端云一体化登录、政企应用的安全审计或复杂的跨端权限校验场景中,如何确保来自云端授信中心的 JWT Token 既能被正确解析(Decode),又能被严密地校验其合法性与过期时间?jwt_io 为开发者提供了一套工业级的、基于 RFC 7519 标准的 JSON Web Token 深度处理方案。本文将深入实战其在鸿蒙应用安全底座中的应用。 前言 什么是 JWT IO?它不仅是一个简单的 Base64 解码器,而是一个具备深厚 RFC

部署OpenClaw首选远程软件——UU远程:从准备到落地,新手也能轻松上手

部署OpenClaw首选远程软件——UU远程:从准备到落地,新手也能轻松上手

前言 在企业为客户远程部署、技术博主带粉丝实操教学、远程技术支持等真实场景中,稳定、低延迟、高同步的远程工具是完成 AI 工具部署的关键。本地部署无需依赖云服务器,成本更低、更安全,但传统远程软件往往延迟高、操作卡顿,严重影响部署效率与体验。 本文将以OpenClaw轻量 AI 辅助服务工具为部署对象,全程依托网易 UU 远程实现流畅远程控制与协助,详细讲解网易 UU 远程的核心优势,从 UU 远程环境准备、OpenClaw 远程部署,到基于网易UU远程的实时监视 OpenClaw 状态,零门槛、无复杂配置。借助网易 UU 远程的低延迟与高稳定性,企业可高效为客户远程交付,博主可轻松带粉丝同步实操,新手也能跟着完整落地。 本篇文章分别从准备工作、远程部署、远程监视三个维度进行实操教学,一步步拆解如何运用远程UU进行远程部署openclaw。 一、网易UU远程介绍 网易UU远程是网易出品的一款轻量化、零配置、高稳定的远程控制工具,区别于传统远程工具(

3分钟搭建本地Web服务器:Web Server for Chrome完全指南

3分钟搭建本地Web服务器:Web Server for Chrome完全指南 【免费下载链接】web-server-chromeAn HTTP Web Server for Chrome (chrome.sockets API) 项目地址: https://gitcode.com/gh_mirrors/we/web-server-chrome 还在为复杂的本地开发环境配置而头疼吗?Web Server for Chrome是一款基于Chrome扩展的轻量级HTTP服务器,让您无需任何技术背景就能快速启动本地Web服务。这款工具已经被20多万用户验证,是替代python -m SimpleHTTPServer的完美选择。 🎯 为什么选择Web Server for Chrome? 对比维度Web Server for Chrome传统本地服务器安装配置⭐⭐⭐⭐⭐(一键安装)⭐⭐(环境依赖)启动速度⭐⭐⭐⭐⭐(秒级启动)⭐⭐⭐(较慢)操作难度⭐⭐⭐⭐⭐(图形界面)⭐⭐(命令行)功能完整⭐