diff --git a/spring-cloud-gateway-server-mvc/src/main/java/org/springframework/cloud/gateway/server/mvc/common/MvcUtils.java b/spring-cloud-gateway-server-mvc/src/main/java/org/springframework/cloud/gateway/server/mvc/common/MvcUtils.java index 21b336e3eb..1afb1c52cc 100644 --- a/spring-cloud-gateway-server-mvc/src/main/java/org/springframework/cloud/gateway/server/mvc/common/MvcUtils.java +++ b/spring-cloud-gateway-server-mvc/src/main/java/org/springframework/cloud/gateway/server/mvc/common/MvcUtils.java @@ -24,6 +24,7 @@ import java.util.Arrays; import java.util.Collection; import java.util.Collections; +import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Optional; @@ -157,7 +158,9 @@ public static T getAttribute(ServerRequest request, String key) { public static Map getGatewayAttributes(ServerRequest request) { // This map is made in GatewayDelegatingRouterFunction.route() and persists across // attribute resetting in RequestPredicates - Map attributes = (Map) request.attributes().get(GATEWAY_ATTRIBUTES_ATTR); + // computeIfAbsent if the used vanilla RouterFunctions.route() + Map attributes = (Map) request.attributes() + .computeIfAbsent(GATEWAY_ATTRIBUTES_ATTR, s -> new HashMap()); return attributes; } diff --git a/spring-cloud-gateway-server-mvc/src/test/java/org/springframework/cloud/gateway/server/mvc/VanillaRouterFunctionTests.java b/spring-cloud-gateway-server-mvc/src/test/java/org/springframework/cloud/gateway/server/mvc/VanillaRouterFunctionTests.java new file mode 100644 index 0000000000..d187329db3 --- /dev/null +++ b/spring-cloud-gateway-server-mvc/src/test/java/org/springframework/cloud/gateway/server/mvc/VanillaRouterFunctionTests.java @@ -0,0 +1,83 @@ +/* + * Copyright 2013-2023 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.cloud.gateway.server.mvc; + +import java.util.Map; + +import org.junit.jupiter.api.Test; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.SpringBootConfiguration; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; +import org.springframework.boot.test.web.server.LocalServerPort; +import org.springframework.cloud.gateway.server.mvc.test.HttpbinTestcontainers; +import org.springframework.cloud.gateway.server.mvc.test.HttpbinUriResolver; +import org.springframework.cloud.gateway.server.mvc.test.TestLoadBalancerConfig; +import org.springframework.cloud.gateway.server.mvc.test.client.TestRestClient; +import org.springframework.cloud.loadbalancer.annotation.LoadBalancerClient; +import org.springframework.context.annotation.Bean; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.web.servlet.function.RouterFunction; +import org.springframework.web.servlet.function.RouterFunctions; +import org.springframework.web.servlet.function.ServerResponse; + +import static org.springframework.cloud.gateway.server.mvc.filter.BeforeFilterFunctions.modifyRequestBody; +import static org.springframework.cloud.gateway.server.mvc.handler.HandlerFunctions.http; +import static org.springframework.cloud.gateway.server.mvc.predicate.GatewayRequestPredicates.host; + +@SuppressWarnings("unchecked") +@SpringBootTest(properties = { "spring.cloud.gateway.mvc.http-client.type=jdk" }, + webEnvironment = WebEnvironment.RANDOM_PORT) +@ContextConfiguration(initializers = HttpbinTestcontainers.class) +public class VanillaRouterFunctionTests { + + @LocalServerPort + int port; + + @Autowired + TestRestClient restClient; + + @SuppressWarnings("rawtypes") + @Test + public void routerFunctionsRouteWorks() { + restClient.post().uri("/anything/routerfunctionsroute").header("Host", "www.routerfunctionsroute.org") + .bodyValue("hello").exchange().expectStatus().isOk().expectBody(Map.class).consumeWith(result -> { + System.out.println(); + }); + } + + @SpringBootConfiguration + @EnableAutoConfiguration + @LoadBalancerClient(name = "httpbin", configuration = TestLoadBalancerConfig.Httpbin.class) + protected static class TestConfiguration { + + @Bean + public RouterFunction routerFunctionsRoute() { + // @formatter:off + return RouterFunctions.route() + .POST("/anything/routerfunctionsroute", host("**.routerfunctionsroute.org"), http()) + .before(modifyRequestBody(String.class, String.class, null, (request, s) -> s.toUpperCase())) + .before(new HttpbinUriResolver()) + .build(); + // @formatter:on + } + + } + +}