spring-cloud-gateway icon indicating copy to clipboard operation
spring-cloud-gateway copied to clipboard

Circuit Breaker doesn't support dynamic fallback URI in Java configuration

Open fisdam opened this issue 2 years ago • 3 comments

Configure a circuit breaker in Java to copy the segments in the fallback URI

`` @Configuration class RouteConfiguration {

@Bean
public RouteLocator gatewayRoutes(RouteLocatorBuilder builder) {
	
	builder.routes().route(r -> r.path("api/{*segments}").filters(f -> {
						    f.circuitBreaker(c -> c
										.setName("test")
										.setFallbackUri("forward:/inCaseOfFailureUseThis/{segments}")
										);
							}
							return f;
						})
						.uri(http://localhost:8080)
						);

``

Result:

Caused by: java.lang.IllegalArgumentException: Illegal character in path at index 32: forward:/inCaseOfFailureUseThis/{segments} at java.base/java.net.URI.create(URI.java:906) ~[na:na]

Caused by: java.net.URISyntaxException: Illegal character in path at index 32: forward:/inCaseOfFailureUseThis/{segments} at java.base/java.net.URI$Parser.fail(URI.java:2976) ~[na:na] at java.base/java.net.URI$Parser.checkChars(URI.java:3147) ~[na:na] at java.base/java.net.URI$Parser.parseHierarchical(URI.java:3229) ~[na:na] at java.base/java.net.URI$Parser.parse(URI.java:3177) ~[na:na] at java.base/java.net.URI.(URI.java:623) ~[na:na] at java.base/java.net.URI.create(URI.java:904) ~[na:na]

Documentation extract

`` @Configuration class RouteConfiguration {

@Bean
public RouterFunction<ServerResponse> gatewayRouterFunctionsCircuitBreakerFallback() {
    return route("circuitbreaker_route")
            .route(path("/consumingServiceEndpoint/{*segments}"), http("https://example.org"))
            .filter(circuitBreaker("myCircuitBreaker", URI.create("forward:/inCaseOfFailureUseThis/{segments}")))
            .build();
}

} ``

Note:

Using the config via yaml is not possible in my case as a part of the URI (prefix) has to be done in the code. So a configuration in Java of the fallback URI is required.

fisdam avatar Jan 02 '24 15:01 fisdam

I created a route config like the following in a unit test

.route("fallback_with_segments", r -> r.path("/fallback/{segments}").filters(f -> f.setPath("/circuitbreakerFallbackController/{segments}")).uri(uri))

and it worked.

Can you try removing * in {segments} and see if it works? If it doesn't can you please provide a sample to reproduce it?

ryanjbaxter avatar Jan 10 '24 20:01 ryanjbaxter

The usage of "{segments}" works well in a filter. The issue is only in Circuit Breaker. The code in your comment or test is not using Circuit Breaker. I need to copy the segments for the fallback route, otherwise this information is lost in the fallback route, Please test the piece of code provided previously (one comes from the documentation in this git repository). dependencies { implementation 'org.springframework.cloud:spring-cloud-starter-gateway' implementation 'org.springframework.cloud:spring-cloud-starter-circuitbreaker-reactor-resilience4j' ... Spring Cloud: 2022.0.4 Spring Boot: 3.1.6

The exception come from this class and function:

` package org.springframework.cloud.gateway.filter.factory; public abstract class SpringCloudCircuitBreakerFilterFactory extends AbstractGatewayFilterFactory<SpringCloudCircuitBreakerFilterFactory.Config> {

	public Config setFallbackUri(String fallbackUri) {
		return setFallbackUri(URI.create(fallbackUri));
	}

`

-> URI.create will create an exception due to the character "{"

fisdam avatar Jan 11 '24 12:01 fisdam

Thanks I was able to reproduce this and have a PR with a fix.

As a workaround I think you can do the following and it should work

UriComponentsBuilder.fromUriString("forward:/inCaseOfFailureUseThis/{segments}").build().toUri()

ryanjbaxter avatar Jan 12 '24 16:01 ryanjbaxter