You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Cloud Gateway自定义路由失效问题求助

Spring Cloud Gateway自定义路由返回404问题

我正在搭建Spring Cloud Gateway服务器,为两个已开发的微服务配置自定义路由。启动网关后,通过actuator/gateway/routes能看到自定义路由已注册,但访问/api/account或/api/profile这类自定义路径时返回404,改用/accountsservice这类自动生成的路径却能正常请求。尝试关闭lowerCaseServiceId后问题依旧,求解决。


网关pom文件

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.1.2</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.geminibank</groupId>
    <artifactId>apigateway</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>apigateway</name>
    <description>Demo project for Spring Boot</description>
    <properties>
        <java.version>17</java.version>
        <spring-cloud.version>2022.0.3</spring-cloud.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-gateway</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-webflux</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

网关配置文件

server.port=8090
spring.application.name = apigateway

spring.cloud.gateway.discovery.locator.enabled = true
spring.cloud.gateway.discovery.locator.lowerCaseServiceId = true

management.endpoint.gateway.enabled=true
management.endpoints.web.exposure.include=health,gateway

eureka.instance.prefer-ip-address = true
eureka.instance.hostname=localhost
eureka.client.fetch-registry = true
eureka.client.register-with-eureka = true
eureka.client.serviceUrl.defaultZone = http://${eureka.instance.hostname}:8082/eureka/

Gateway配置类

@Configuration
public class GatewayConfig {
    
    @Bean
    public RouteLocator customeRouteLocator(RouteLocatorBuilder builder) {
         RouteLocator routes = builder.routes()
                .route("profile-service", p -> p
                        .path("/api/profile/**")
                        .uri("lb://PROFILESERVICE"))
                .route("account-service", p -> p
                        .path("/api/account/**")
                        .uri("lb://ACCOUNTSSERVICE"))
                .build();
         routes.getRoutes().log();
         return routes;
    }
}

已注册路由列表(来自actuator/gateway/routes)

[
    {
        "predicate": "Paths: [/api/profile/**], match trailing slash: true",
        "route_id": "profile-service",
        "filters": [],
        "uri": "lb://PROFILESERVICE",
        "order": 0
    },
    {
        "predicate": "Paths: [/api/account/**], match trailing slash: true",
        "route_id": "account-service",
        "filters": [],
        "uri": "lb://ACCOUNTSSERVICE",
        "order": 0
    },
    {
        "predicate": "Paths: [/profileservice/**], match trailing slash: true",
        "metadata": {
            "jmx.port": "51067",
            "management.port": "8083"
        },
        "route_id": "ReactiveCompositeDiscoveryClient_PROFILESERVICE",
        "filters": [
            "[[RewritePath /profileservice/?(?<remaining>.*) = '/${remaining}'], order = 1]"
        ],
        "uri": "lb://PROFILESERVICE",
        "order": 0
    },
    {
        "predicate": "Paths: [/accountsservice/**], match trailing slash: true",
        "metadata": {
            "jmx.port": "51001",
            "management.port": "8084"
        },
        "route_id": "ReactiveCompositeDiscoveryClient_ACCOUNTSSERVICE",
        "filters": [
            "[[RewritePath /accountsservice/?(?<remaining>.*) = '/${remaining}'], order = 1]"
        ],
        "uri": "lb://ACCOUNTSSERVICE",
        "order": 0
    },
    {
        "predicate": "Paths: [/apigateway/**], match trailing slash: true",
        "metadata": {
            "jmx.port": "59682",
            "management.port": "8090"
        },
        "route_id": "ReactiveCompositeDiscoveryClient_APIGATEWAY",
        "filters": [
            "[[RewritePath /apigateway/?(?<remaining>.*) = '/${remaining}'], order = 1]"
        ],
        "uri": "lb://APIGATEWAY",
        "order": 0
    }
]

问题原因及解决方案

核心原因

自动生成的服务路由(如/accountsservice/**)带有RewritePath过滤器,会将请求路径中的服务名前缀移除后转发到微服务;而你的自定义路由没有配置该过滤器,导致完整的/api/account/**路径被转发到微服务,但微服务本身的接口路径可能并不包含/api/account前缀,因此返回404。

修复步骤

修改自定义路由配置,添加RewritePath过滤器,移除自定义前缀后再转发:

修改后的Gateway配置类

@Configuration
public class GatewayConfig {
    
    @Bean
    public RouteLocator customeRouteLocator(RouteLocatorBuilder builder) {
         return builder.routes()
                .route("profile-service", p -> p
                        .path("/api/profile/**")
                        // 移除/api/profile前缀,转发剩余路径到微服务
                        .filters(f -> f.rewritePath("/api/profile/(?<remaining>.*)", "/${remaining}"))
                        .uri("lb://PROFILESERVICE"))
                .route("account-service", p -> p
                        .path("/api/account/**")
                        // 移除/api/account前缀,转发剩余路径到微服务
                        .filters(f -> f.rewritePath("/api/account/(?<remaining>.*)", "/${remaining}"))
                        .uri("lb://ACCOUNTSSERVICE"))
                .build();
    }
}

额外检查点

  1. 确认微服务接口路径:确保微服务的接口路径确实不包含/api/account或/api/profile前缀。如果微服务接口本身带有这些前缀,则不需要添加RewritePath过滤器。
  2. Eureka服务名匹配:检查lb://PROFILESERVICE和lb://ACCOUNTSSERVICE中的服务名是否与Eureka中注册的微服务名称完全一致(注意大小写,Eureka服务名默认区分大小写)。
  3. 路由优先级:如果自定义路由和自动生成路由的order相同,可能存在匹配顺序问题,可以通过order()方法调整自定义路由的优先级,比如设置为order(-1)让其先匹配。

内容的提问来源于stack exchange,提问作者Shady98

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.14 14:52:03