From 950bba9e78773dbffb2f92fab12a51bb1b37f1f3 Mon Sep 17 00:00:00 2001 From: wy471x Date: Fri, 22 May 2026 13:49:23 +0800 Subject: [PATCH 1/6] feat(client):add many path register for spring mvc client. --- .../init/SpringMvcClientEventListener.java | 53 +++++++++++++++---- 1 file changed, 44 insertions(+), 9 deletions(-) diff --git a/shenyu-client/shenyu-client-http/shenyu-client-springmvc/src/main/java/org/apache/shenyu/client/springmvc/init/SpringMvcClientEventListener.java b/shenyu-client/shenyu-client-http/shenyu-client-springmvc/src/main/java/org/apache/shenyu/client/springmvc/init/SpringMvcClientEventListener.java index d2f89f8816d8..0a81871a622d 100644 --- a/shenyu-client/shenyu-client-http/shenyu-client-springmvc/src/main/java/org/apache/shenyu/client/springmvc/init/SpringMvcClientEventListener.java +++ b/shenyu-client/shenyu-client-http/shenyu-client-springmvc/src/main/java/org/apache/shenyu/client/springmvc/init/SpringMvcClientEventListener.java @@ -44,6 +44,7 @@ import org.springframework.lang.NonNull; import org.springframework.lang.Nullable; import org.springframework.stereotype.Controller; +import org.springframework.util.ReflectionUtils; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.util.UriComponentsBuilder; @@ -167,20 +168,54 @@ protected String getClientName() { return RpcTypeEnum.HTTP.getName(); } + @Override + protected void handle(final String beanName, final Object bean) { + Class clazz = getCorrectedClass(bean); + final ShenyuSpringMvcClient beanShenyuClient = AnnotatedElementUtils.findMergedAnnotation(clazz, getAnnotationType()); + final List superPaths = buildApiSuperPaths(clazz, beanShenyuClient); + final Method[] methods = ReflectionUtils.getUniqueDeclaredMethods(clazz); + for (String superPath : superPaths) { + if (Objects.nonNull(beanShenyuClient) && superPath.contains("*")) { + handleClass(clazz, bean, beanShenyuClient, superPath); + continue; + } + for (Method method : methods) { + handleMethod(bean, clazz, beanShenyuClient, method, superPath); + } + } + } + @Override protected String buildApiSuperPath(final Class clazz, @Nullable final ShenyuSpringMvcClient beanShenyuClient) { - final String servletPath = StringUtils.defaultString(this.env.getProperty("spring.mvc.servlet.path"), ""); - final String servletContextPath = StringUtils.defaultString(this.env.getProperty("server.servlet.context-path"), ""); - final String rootPath = String.format("/%s/%s/", servletContextPath, servletPath); - if (Objects.nonNull(beanShenyuClient) && StringUtils.isNotBlank(beanShenyuClient.path()[0])) { - return formatPath(String.format("%s/%s", rootPath, beanShenyuClient.path()[0])); + List paths = buildApiSuperPaths(clazz, beanShenyuClient); + return paths.isEmpty() ? formatPath(buildRootPath()) : paths.get(0); + } + + protected List buildApiSuperPaths(final Class clazz, @Nullable final ShenyuSpringMvcClient beanShenyuClient) { + final String rootPath = buildRootPath(); + if (Objects.nonNull(beanShenyuClient) && ArrayUtils.isNotEmpty(beanShenyuClient.path())) { + return Arrays.stream(beanShenyuClient.path()) + .filter(StringUtils::isNotBlank) + .map(p -> formatPath(String.format("%s/%s", rootPath, p))) + .collect(Collectors.toList()); } RequestMapping requestMapping = AnnotationUtils.findAnnotation(clazz, RequestMapping.class); - // Only the first path is supported temporarily - if (Objects.nonNull(requestMapping) && ArrayUtils.isNotEmpty(requestMapping.path()) && StringUtils.isNotBlank(requestMapping.path()[0])) { - return formatPath(String.format("%s/%s", rootPath, requestMapping.path()[0])); + if (Objects.nonNull(requestMapping) && ArrayUtils.isNotEmpty(requestMapping.path())) { + List paths = Arrays.stream(requestMapping.path()) + .filter(StringUtils::isNotBlank) + .map(p -> formatPath(String.format("%s/%s", rootPath, p))) + .collect(Collectors.toList()); + if (!paths.isEmpty()) { + return paths; + } } - return formatPath(rootPath); + return Collections.singletonList(formatPath(rootPath)); + } + + private String buildRootPath() { + final String servletPath = Optional.ofNullable(this.env.getProperty("spring.mvc.servlet.path")).orElse(""); + final String servletContextPath = Optional.ofNullable(this.env.getProperty("server.servlet.context-path")).orElse(""); + return String.format("/%s/%s/", servletContextPath, servletPath); } @Override From 6d959477bfa1a75bc343c9c3d63cc62e647fd836 Mon Sep 17 00:00:00 2001 From: wy471x Date: Sat, 23 May 2026 00:04:53 +0800 Subject: [PATCH 2/6] feat(client):improve code coverage for SpringMvcClientEventListener. --- .../SpringMvcClientEventListenerTest.java | 84 +++++++++++++++++++ 1 file changed, 84 insertions(+) diff --git a/shenyu-client/shenyu-client-http/shenyu-client-springmvc/src/test/java/org/apache/shenyu/client/springmvc/init/SpringMvcClientEventListenerTest.java b/shenyu-client/shenyu-client-http/shenyu-client-springmvc/src/test/java/org/apache/shenyu/client/springmvc/init/SpringMvcClientEventListenerTest.java index a9e4ba8c49e2..757703d77903 100644 --- a/shenyu-client/shenyu-client-http/shenyu-client-springmvc/src/test/java/org/apache/shenyu/client/springmvc/init/SpringMvcClientEventListenerTest.java +++ b/shenyu-client/shenyu-client-http/shenyu-client-springmvc/src/test/java/org/apache/shenyu/client/springmvc/init/SpringMvcClientEventListenerTest.java @@ -21,8 +21,11 @@ import org.apache.shenyu.client.core.exception.ShenyuClientIllegalArgumentException; import org.apache.shenyu.client.core.register.ShenyuClientRegisterRepositoryFactory; import org.apache.shenyu.client.springmvc.annotation.ShenyuSpringMvcClient; +import org.apache.shenyu.common.enums.ApiHttpMethodEnum; +import org.apache.shenyu.common.enums.RpcTypeEnum; import org.apache.shenyu.common.exception.ShenyuException; import org.apache.shenyu.client.core.utils.PortUtils; +import org.javatuples.Sextet; import org.apache.shenyu.register.client.api.ShenyuClientRegisterRepository; import org.apache.shenyu.register.client.http.utils.RegisterUtils; import org.apache.shenyu.register.common.config.ShenyuClientConfig; @@ -44,10 +47,15 @@ import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RestController; +import java.lang.reflect.Method; +import java.util.Arrays; +import java.util.Collections; import java.util.HashMap; import java.util.LinkedHashMap; +import java.util.List; import java.util.Map; import java.util.Optional; import java.util.Properties; @@ -192,6 +200,57 @@ public void testOnBuildApiSuperPath() { registerUtilsMockedStatic.close(); } + @Test + public void testBuildApiDocSextetDefaultProducesConsumes() throws NoSuchMethodException { + SpringMvcClientEventListener listener = buildSpringMvcClientEventListener(false, false); + Method method = ApiDocTestBean.class.getDeclaredMethod("getDefault"); + Sextet result = + listener.buildApiDocSextet(method, null, Collections.emptyMap()); + + Assertions.assertArrayEquals(new String[]{"/get-default"}, result.getValue0()); + Assertions.assertEquals("*/*", result.getValue1()); + Assertions.assertEquals("*/*", result.getValue2()); + Assertions.assertArrayEquals(new ApiHttpMethodEnum[]{ApiHttpMethodEnum.GET}, result.getValue3()); + Assertions.assertEquals(RpcTypeEnum.HTTP, result.getValue4()); + Assertions.assertEquals("v0.01", result.getValue5()); + registerUtilsMockedStatic.close(); + } + + @Test + public void testBuildApiDocSextetExplicitProducesConsumesAndMethod() throws NoSuchMethodException { + SpringMvcClientEventListener listener = buildSpringMvcClientEventListener(false, false); + Method method = ApiDocTestBean.class.getDeclaredMethod("postExplicit", String.class); + Sextet result = + listener.buildApiDocSextet(method, null, Collections.emptyMap()); + + Assertions.assertArrayEquals(new String[]{"/post-explicit"}, result.getValue0()); + Assertions.assertEquals("application/json", result.getValue1()); + Assertions.assertEquals("application/json", result.getValue2()); + Assertions.assertArrayEquals(new ApiHttpMethodEnum[]{ApiHttpMethodEnum.POST}, result.getValue3()); + Assertions.assertEquals(RpcTypeEnum.HTTP, result.getValue4()); + Assertions.assertEquals("v0.01", result.getValue5()); + registerUtilsMockedStatic.close(); + } + + @Test + public void testBuildApiDocSextetMultipleMethodsProducesConsumes() throws NoSuchMethodException { + SpringMvcClientEventListener listener = buildSpringMvcClientEventListener(false, false); + Method method = ApiDocTestBean.class.getDeclaredMethod("multi", String.class); + Sextet result = + listener.buildApiDocSextet(method, null, Collections.emptyMap()); + + Assertions.assertArrayEquals(new String[]{"/multi"}, result.getValue0()); + Assertions.assertEquals("application/json,application/xml", result.getValue1()); + Assertions.assertEquals("application/json,application/xml", result.getValue2()); + List methods = Arrays.asList(result.getValue3()); + Assertions.assertTrue(methods.contains(ApiHttpMethodEnum.GET)); + Assertions.assertTrue(methods.contains(ApiHttpMethodEnum.POST)); + Assertions.assertEquals(2, methods.size()); + Assertions.assertEquals(RpcTypeEnum.HTTP, result.getValue4()); + Assertions.assertEquals("v0.01", result.getValue5()); + registerUtilsMockedStatic.close(); + } + @RestController @RequestMapping("/order") @ShenyuSpringMvcClient(path = "/order") @@ -238,4 +297,29 @@ public String test(final String hello) { } } + @RestController + static class ApiDocTestBean { + + @GetMapping(value = "/get-default") + public String getDefault() { + return "ok"; + } + + @RequestMapping(value = "/post-explicit", + method = RequestMethod.POST, + produces = "application/json", + consumes = "application/json") + public String postExplicit(@RequestBody final String input) { + return input; + } + + @RequestMapping(value = "/multi", + method = {RequestMethod.GET, RequestMethod.POST}, + produces = {"application/json", "application/xml"}, + consumes = {"application/json", "application/xml"}) + public String multi(@RequestBody final String input) { + return input; + } + } + } From bda0005b9c1d9c91e259504a1d95d0bd3edf694d Mon Sep 17 00:00:00 2001 From: wy471x Date: Sat, 23 May 2026 19:36:32 +0800 Subject: [PATCH 3/6] feat(client):add many path register for spring mvc client. --- .../init/SpringMvcClientEventListener.java | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/shenyu-client/shenyu-client-http/shenyu-client-springmvc/src/main/java/org/apache/shenyu/client/springmvc/init/SpringMvcClientEventListener.java b/shenyu-client/shenyu-client-http/shenyu-client-springmvc/src/main/java/org/apache/shenyu/client/springmvc/init/SpringMvcClientEventListener.java index 788327271d88..ca4aeaec945f 100644 --- a/shenyu-client/shenyu-client-http/shenyu-client-springmvc/src/main/java/org/apache/shenyu/client/springmvc/init/SpringMvcClientEventListener.java +++ b/shenyu-client/shenyu-client-http/shenyu-client-springmvc/src/main/java/org/apache/shenyu/client/springmvc/init/SpringMvcClientEventListener.java @@ -194,10 +194,13 @@ protected String buildApiSuperPath(final Class clazz, @Nullable final ShenyuS protected List buildApiSuperPaths(final Class clazz, @Nullable final ShenyuSpringMvcClient beanShenyuClient) { final String rootPath = buildRootPath(); if (Objects.nonNull(beanShenyuClient) && ArrayUtils.isNotEmpty(beanShenyuClient.path())) { - return Arrays.stream(beanShenyuClient.path()) + List paths = Arrays.stream(beanShenyuClient.path()) .filter(StringUtils::isNotBlank) .map(p -> formatPath(String.format("%s/%s", rootPath, p))) .collect(Collectors.toList()); + if (!paths.isEmpty()) { + return paths; + } } RequestMapping requestMapping = AnnotationUtils.findAnnotation(clazz, RequestMapping.class); if (Objects.nonNull(requestMapping) && ArrayUtils.isNotEmpty(requestMapping.path())) { @@ -247,8 +250,13 @@ protected void handleMethod(final Object bean, final Class clazz, protected String buildApiPath(final Method method, final String superPath, @NonNull final ShenyuSpringMvcClient methodShenyuClient) { String contextPath = getContextPath(); - if (StringUtils.isNotBlank(methodShenyuClient.path()[0])) { - return pathJoin(contextPath, superPath, methodShenyuClient.path()[0]); + // Skip if any annotation path is already captured in superPath (class annotation used as method fallback) + final String annotationPath = methodShenyuClient.path()[0]; + boolean alreadyInSuperPath = Arrays.stream(methodShenyuClient.path()) + .filter(StringUtils::isNotBlank) + .anyMatch(p -> superPath.endsWith(formatPath(p))); + if (StringUtils.isNotBlank(annotationPath) && !alreadyInSuperPath) { + return pathJoin(contextPath, superPath, annotationPath); } final String path = getPathByMethod(method); if (StringUtils.isNotBlank(path)) { From 42eee2b5511ef3a2541376ed8900535ea818de20 Mon Sep 17 00:00:00 2001 From: wy471x Date: Sat, 23 May 2026 19:38:04 +0800 Subject: [PATCH 4/6] feat(client):add integrated test cases for SpringMvcClientEventListener. --- .../SpringMvcMappingPathControllerTest.java | 34 ++++++++++++++++--- 1 file changed, 30 insertions(+), 4 deletions(-) diff --git a/shenyu-integrated-test/shenyu-integrated-test-http/src/test/java/org/apache/shenyu/integrated/test/http/SpringMvcMappingPathControllerTest.java b/shenyu-integrated-test/shenyu-integrated-test-http/src/test/java/org/apache/shenyu/integrated/test/http/SpringMvcMappingPathControllerTest.java index 3b1debec6bf7..ea4d63281fd5 100644 --- a/shenyu-integrated-test/shenyu-integrated-test-http/src/test/java/org/apache/shenyu/integrated/test/http/SpringMvcMappingPathControllerTest.java +++ b/shenyu-integrated-test/shenyu-integrated-test-http/src/test/java/org/apache/shenyu/integrated/test/http/SpringMvcMappingPathControllerTest.java @@ -24,23 +24,49 @@ import java.io.IOException; -public class SpringMvcMappingPathControllerTest extends AbstractTest { +class SpringMvcMappingPathControllerTest extends AbstractTest { + + private static final String MULTI_PATH_SUFFIX = "I'm Shenyu-Gateway System. Welcome!"; @Test - public void testHello() throws IOException { + void testHello() throws IOException { String res = HttpHelper.INSTANCE.postGateway("/http/hello", java.lang.String.class); assertEquals("hello! I'm Shenyu-Gateway System. Welcome!", res); } @Test - public void testHi()throws IOException { + void testHi() throws IOException { String res = HttpHelper.INSTANCE.postGateway("/http/hi?name=tom", java.lang.String.class); assertEquals("hi! tom! I'm Shenyu-Gateway System. Welcome!", res); } @Test - public void testPost()throws IOException { + void testPost() throws IOException { String res = HttpHelper.INSTANCE.postGateway("/http/post/hi?name=tom", java.lang.String.class); assertEquals("[post method result]:hi! tom! I'm Shenyu-Gateway System. Welcome!", res); } + + @Test + void testMultiPathV1Greet() throws IOException { + String res = HttpHelper.INSTANCE.postGateway("/http/multipath/v1/greet", java.lang.String.class); + assertEquals("hello from multipath! " + MULTI_PATH_SUFFIX, res); + } + + @Test + void testMultiPathV2Greet() throws IOException { + String res = HttpHelper.INSTANCE.postGateway("/http/multipath/v2/greet", java.lang.String.class); + assertEquals("hello from multipath! " + MULTI_PATH_SUFFIX, res); + } + + @Test + void testMultiPathV1Echo() throws IOException { + String res = HttpHelper.INSTANCE.postGateway("/http/multipath/v1/echo?name=shenyu", java.lang.String.class); + assertEquals("echo: shenyu! " + MULTI_PATH_SUFFIX, res); + } + + @Test + void testMultiPathV2Echo() throws IOException { + String res = HttpHelper.INSTANCE.postGateway("/http/multipath/v2/echo?name=shenyu", java.lang.String.class); + assertEquals("echo: shenyu! " + MULTI_PATH_SUFFIX, res); + } } From eb5655ca35edb9710989f7509561f65342be62d9 Mon Sep 17 00:00:00 2001 From: wy471x Date: Sat, 23 May 2026 20:00:03 +0800 Subject: [PATCH 5/6] feat(client):add integrated test cases for SpringMvcClientEventListener. --- .../SpringMvcMappingPathControllerTest.java | 39 ++++++++++++++----- 1 file changed, 29 insertions(+), 10 deletions(-) diff --git a/shenyu-integrated-test/shenyu-integrated-test-http/src/test/java/org/apache/shenyu/integrated/test/http/SpringMvcMappingPathControllerTest.java b/shenyu-integrated-test/shenyu-integrated-test-http/src/test/java/org/apache/shenyu/integrated/test/http/SpringMvcMappingPathControllerTest.java index ea4d63281fd5..bfc372e08a30 100644 --- a/shenyu-integrated-test/shenyu-integrated-test-http/src/test/java/org/apache/shenyu/integrated/test/http/SpringMvcMappingPathControllerTest.java +++ b/shenyu-integrated-test/shenyu-integrated-test-http/src/test/java/org/apache/shenyu/integrated/test/http/SpringMvcMappingPathControllerTest.java @@ -17,56 +17,75 @@ package org.apache.shenyu.integrated.test.http; -import org.junit.jupiter.api.Test; -import org.apache.shenyu.integratedtest.common.helper.HttpHelper; import org.apache.shenyu.integratedtest.common.AbstractTest; -import static org.junit.jupiter.api.Assertions.assertEquals; +import org.apache.shenyu.integratedtest.common.helper.HttpHelper; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; import java.io.IOException; +import java.util.concurrent.TimeUnit; + +import static org.junit.jupiter.api.Assertions.assertEquals; class SpringMvcMappingPathControllerTest extends AbstractTest { private static final String MULTI_PATH_SUFFIX = "I'm Shenyu-Gateway System. Welcome!"; + @BeforeAll + static void waitForMultiPathRoutes() throws InterruptedException { + // Multi-path routes are registered asynchronously; poll until available + for (int i = 0; i < 30; i++) { + try { + String res = HttpHelper.INSTANCE.postGateway("/http/multipath/v1/greet", String.class); + if (("hello from multipath! " + MULTI_PATH_SUFFIX).equals(res)) { + return; + } + } catch (IOException e) { + // route not ready yet, keep waiting + } + Thread.sleep(TimeUnit.SECONDS.toMillis(2)); + } + } + @Test void testHello() throws IOException { - String res = HttpHelper.INSTANCE.postGateway("/http/hello", java.lang.String.class); + String res = HttpHelper.INSTANCE.postGateway("/http/hello", String.class); assertEquals("hello! I'm Shenyu-Gateway System. Welcome!", res); } @Test void testHi() throws IOException { - String res = HttpHelper.INSTANCE.postGateway("/http/hi?name=tom", java.lang.String.class); + String res = HttpHelper.INSTANCE.postGateway("/http/hi?name=tom", String.class); assertEquals("hi! tom! I'm Shenyu-Gateway System. Welcome!", res); } @Test void testPost() throws IOException { - String res = HttpHelper.INSTANCE.postGateway("/http/post/hi?name=tom", java.lang.String.class); + String res = HttpHelper.INSTANCE.postGateway("/http/post/hi?name=tom", String.class); assertEquals("[post method result]:hi! tom! I'm Shenyu-Gateway System. Welcome!", res); } @Test void testMultiPathV1Greet() throws IOException { - String res = HttpHelper.INSTANCE.postGateway("/http/multipath/v1/greet", java.lang.String.class); + String res = HttpHelper.INSTANCE.postGateway("/http/multipath/v1/greet", String.class); assertEquals("hello from multipath! " + MULTI_PATH_SUFFIX, res); } @Test void testMultiPathV2Greet() throws IOException { - String res = HttpHelper.INSTANCE.postGateway("/http/multipath/v2/greet", java.lang.String.class); + String res = HttpHelper.INSTANCE.postGateway("/http/multipath/v2/greet", String.class); assertEquals("hello from multipath! " + MULTI_PATH_SUFFIX, res); } @Test void testMultiPathV1Echo() throws IOException { - String res = HttpHelper.INSTANCE.postGateway("/http/multipath/v1/echo?name=shenyu", java.lang.String.class); + String res = HttpHelper.INSTANCE.postGateway("/http/multipath/v1/echo?name=shenyu", String.class); assertEquals("echo: shenyu! " + MULTI_PATH_SUFFIX, res); } @Test void testMultiPathV2Echo() throws IOException { - String res = HttpHelper.INSTANCE.postGateway("/http/multipath/v2/echo?name=shenyu", java.lang.String.class); + String res = HttpHelper.INSTANCE.postGateway("/http/multipath/v2/echo?name=shenyu", String.class); assertEquals("echo: shenyu! " + MULTI_PATH_SUFFIX, res); } } From 1fd46e7392c4fbb1c2256dbf8e94544cf1ca07e8 Mon Sep 17 00:00:00 2001 From: wy471x Date: Mon, 25 May 2026 22:16:09 +0800 Subject: [PATCH 6/6] feat(client):add multipath register test controller. --- .../SpringMvcMultiPathController.java | 60 +++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 shenyu-examples/shenyu-examples-http/src/main/java/org/apache/shenyu/examples/http/controller/SpringMvcMultiPathController.java diff --git a/shenyu-examples/shenyu-examples-http/src/main/java/org/apache/shenyu/examples/http/controller/SpringMvcMultiPathController.java b/shenyu-examples/shenyu-examples-http/src/main/java/org/apache/shenyu/examples/http/controller/SpringMvcMultiPathController.java new file mode 100644 index 000000000000..a95494e1c5eb --- /dev/null +++ b/shenyu-examples/shenyu-examples-http/src/main/java/org/apache/shenyu/examples/http/controller/SpringMvcMultiPathController.java @@ -0,0 +1,60 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 + * + * http://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.apache.shenyu.examples.http.controller; + +import org.apache.shenyu.client.apidocs.annotations.ApiDoc; +import org.apache.shenyu.client.apidocs.annotations.ApiModule; +import org.apache.shenyu.client.springmvc.annotation.ShenyuSpringMvcClient; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * SpringMvcMultiPathController — verifies multi-path class-level registration. + * Both /multipath/v1 and /multipath/v2 prefixes are registered via a single annotation. + */ +@RestController +@RequestMapping({"/multipath/v1", "/multipath/v2"}) +@ShenyuSpringMvcClient(path = {"/multipath/v1", "/multipath/v2"}, desc = "multi path register") +@ApiModule(value = "springMvcMultiPathController") +public class SpringMvcMultiPathController { + + private static final String SUFFIX = "I'm Shenyu-Gateway System. Welcome!"; + + /** + * greet. + * + * @return result + */ + @RequestMapping("/greet") + @ApiDoc(desc = "greet") + public String greet() { + return "hello from multipath! " + SUFFIX; + } + + /** + * echo. + * + * @param name name + * @return result + */ + @RequestMapping("/echo") + @ApiDoc(desc = "echo") + public String echo(final String name) { + return "echo: " + name + "! " + SUFFIX; + } +}