API Routing and Common Pitfalls with Spring Cloud Gateway and Nacos
Dependency management utilizes the following technology stack versions:
<properties>
<spring-cloud.version>2020.0.1</spring-cloud.version>
<spring-cloud-alibaba.version>2.2.1.RELEASE</spring-cloud-alibaba.version>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
</properties>
<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>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>${spring-cloud-alibaba.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
Gateway Service Configuration
The gateway application requires WebFlux dependencies but not the standard Spring Web MVC starter.
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
</dependencies>
Nacos configuration (gateway-service.properties):
server.port=8080
spring.cloud.gateway.routes[0].id=user-core-route
spring.cloud.gateway.routes[0].uri=lb://user-core-service
spring.cloud.gateway.routes[0].predicates[0]=Path=/api/user/**
spring.cloud.gateway.routes[0].filters[0]=StripPrefix=1
Bootstrap configuration (bootstrap.yml):
spring:
application:
name: api-gateway-service
profiles:
active: dev
cloud:
nacos:
config:
server-addr: 127.0.0.1:8848
namespace: dev-namespace-id
group: DEFAULT_GROUP
file-extension: properties
refresh:
enabled: true
discovery:
server-addr: 127.0.0.1:8848
Spring Cloud Gateway operates on Netty. It does not honor the server.servlet.context-path setting, starting only the Netty server via server.port.
Downstream Microservice Setup
The downstream service uses a standard Spring Web MVC setup.
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
</dependencies>
Application properties:
server.port=9090
server.servlet.context-path=/user-svc
Test controller:
@RestController
@RequestMapping("/status")
public class HealthCheckController {
@GetMapping(value = "/ping")
public String checkAlive() {
return "Service is up";
}
}
Routing resolution: A request to http://localhost:8080/api/user/status/ping is intercepted by the gateway. It strips the first prefix (/api), making the path /user/status/ping. It then resolves user-core-service to 127.0.0.1:9090 and forwards the request to http://127.0.0.1:9090/user-svc/status/ping.
Issue 1: WebFlux vs Spring MVC Dependency Conflict
Introducing a shared utility module into the gateway project caused startup failure with the following error:
Consider defining a bean of type 'org.springframework.http.codec.ServerCodecConfigurer' in your configuration.
This exception occurs because the shared module pulled in spring-boot-starter-web as a transitive dependency. Spring Cloud Gateway is built on Spring WebFlux and Reactor Netty. Including Spring MVC (spring-boot-starter-web) in the classpath disables the WebFlux auto-configuration, leading to the conflict. The resolution is to exclude the web starter from the shared module dependency or remove it entirely from the shared module.
Issue 2: WebSocket SSL Termination on Gateway
When configuring the gateway to handle secure WebSocket connections (WSS) over SSL, a casting exception may occur:
java.lang.ClassCastException: org.apache.catalina.connector.ResponseFacade cannot be cast to reactor.netty.http.server.HttpServerResponse
Despite valid SSL configurations:
server.ssl.key-store=classpath:keystore/tls_cert.p12
server.ssl.key-store-password=secret
server.ssl.key-store-type=PKCS12
server.ssl.enabled=true
server.port=443
This problem stems from the incompatibility between the servlet container and the reactive Netty server used by the gateway, often triggered by accidental MVC dependencies or inherent WebFlux handling of WebSocket upgrades in certain routing scenarios. A reliable workaround involves proxying WSS traffic through a dedicated reverse proxy like Nginx, reserving the gateway strictly for standard HTTPS routing.