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 78f906342641..67692b801d56 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 @@ -40,6 +40,7 @@ import org.springframework.context.ApplicationContext; import org.springframework.core.annotation.AnnotatedElementUtils; import org.springframework.core.annotation.AnnotationUtils; +import org.springframework.core.env.Environment; import org.springframework.lang.NonNull; import org.springframework.lang.Nullable; import org.springframework.stereotype.Controller; @@ -65,7 +66,7 @@ public class SpringMvcClientEventListener extends AbstractContextRefreshedEventListener { private static final Logger LOG = LoggerFactory.getLogger(SpringMvcClientEventListener.class); - + private final ShenyuClientRegisterEventPublisher publisher = ShenyuClientRegisterEventPublisher.getInstance(); private final List> mappingAnnotation = new ArrayList<>(3); @@ -76,15 +77,20 @@ public class SpringMvcClientEventListener extends AbstractContextRefreshedEventL private final boolean addPrefixed; + private final Environment env; + /** * Instantiates a new context refreshed event listener. * * @param clientConfig the shenyu client config * @param shenyuClientRegisterRepository the shenyuClientRegisterRepository + * @param env the env */ public SpringMvcClientEventListener(final PropertiesConfig clientConfig, - final ShenyuClientRegisterRepository shenyuClientRegisterRepository) { + final ShenyuClientRegisterRepository shenyuClientRegisterRepository, + final Environment env) { super(clientConfig, shenyuClientRegisterRepository); + this.env = env; Properties props = clientConfig.getProps(); this.isFull = Boolean.parseBoolean(props.getProperty(ShenyuClientConstants.IS_FULL, Boolean.FALSE.toString())); this.protocol = props.getProperty(ShenyuClientConstants.PROTOCOL, ShenyuClientConstants.HTTP); @@ -150,15 +156,18 @@ protected URIRegisterDTO buildURIRegisterDTO(final ApplicationContext context, @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())) { - return beanShenyuClient.path(); + return formatPath(String.format("%s/%s", rootPath, beanShenyuClient.path())); } 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 requestMapping.path()[0]; + return formatPath(String.format("%s/%s", rootPath, requestMapping.path()[0])); } - return ""; + return formatPath(rootPath); } @Override @@ -197,6 +206,10 @@ protected String buildApiPath(final Method method, final String superPath, return pathJoin(contextPath, superPath); } + private String formatPath(final String path) { + return path.replaceAll("/+", "/").replaceFirst("/$", ""); + } + private String getPathByMethod(@NonNull final Method method) { for (Class mapping : mappingAnnotation) { final String pathByAnnotation = getPathByAnnotation(AnnotatedElementUtils.findMergedAnnotation(method, mapping)); @@ -246,14 +259,14 @@ protected MetaDataRegisterDTO buildMetaDataDTO(final Object bean, .registerMetaData(shenyuClient.registerMetaData()) .build(); } - + @Override protected ApiDocRegisterDTO.ApiExt customApiDocExt(final ApiDocRegisterDTO.ApiExt ext) { ext.setProtocol(protocol); ext.setAddPrefixed(addPrefixed); return ext; } - + @Override public String getPort() { final int port = Integer.parseInt(Optional.ofNullable(super.getPort()).orElseGet(() -> "-1")); 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 89df6b2d95d5..09681b3b4373 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 @@ -36,6 +36,8 @@ import org.springframework.beans.factory.config.AutowireCapableBeanFactory; import org.springframework.context.ApplicationContext; import org.springframework.context.event.ContextRefreshedEvent; +import org.springframework.core.annotation.AnnotatedElementUtils; +import org.springframework.core.env.Environment; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; @@ -72,10 +74,13 @@ public class SpringMvcClientEventListenerTest { @Mock private ApplicationContext applicationContext; - + @Mock private AutowireCapableBeanFactory beanFactory; + @Mock + private Environment env; + private ContextRefreshedEvent contextRefreshedEvent; private void init() { @@ -90,7 +95,7 @@ private void init() { PropertiesConfig clientConfig = mock(PropertiesConfig.class); when(clientConfig.getProps()).thenReturn(properties); - Assert.assertThrows(ShenyuClientIllegalArgumentException.class, () -> new SpringMvcClientEventListener(clientConfig, mock(ShenyuClientRegisterRepository.class))); + Assert.assertThrows(ShenyuClientIllegalArgumentException.class, () -> new SpringMvcClientEventListener(clientConfig, mock(ShenyuClientRegisterRepository.class), env)); } @Test @@ -141,9 +146,9 @@ private SpringMvcClientEventListener buildSpringMvcClientEventListener(final boo mockRegisterCenter.setServerLists("http://127.0.0.1:9095"); mockRegisterCenter.setRegisterType("http"); mockRegisterCenter.setProps(properties); - return new SpringMvcClientEventListener(config, ShenyuClientRegisterRepositoryFactory.newInstance(mockRegisterCenter)); + return new SpringMvcClientEventListener(config, ShenyuClientRegisterRepositoryFactory.newInstance(mockRegisterCenter), env); } - + @Test public void testOnApplicationEvent() { init(); @@ -152,13 +157,13 @@ public void testOnApplicationEvent() { MockedStatic portUtilsMockedStatic = mockStatic(PortUtils.class); portUtilsMockedStatic.when(() -> PortUtils.findPort(beanFactory)).thenReturn(8080); springMvcClientEventListener.onApplicationEvent(contextRefreshedEvent); - + // hit `!registered.compareAndSet(false, true)` springMvcClientEventListener.onApplicationEvent(contextRefreshedEvent); portUtilsMockedStatic.close(); registerUtilsMockedStatic.close(); } - + @Test public void testOnApplicationEventError() { init(); @@ -166,7 +171,21 @@ public void testOnApplicationEventError() { Assert.assertThrows(ShenyuException.class, () -> springMvcClientEventListener.onApplicationEvent(contextRefreshedEvent)); registerUtilsMockedStatic.close(); } - + + @Test + public void testOnBuildApiSuperPath() { + SpringMvcClientEventListener springMvcClientEventListener = buildSpringMvcClientEventListener(false, false); + + Assert.assertEquals("super-path", "/order", springMvcClientEventListener.buildApiSuperPath( + SpringMvcClientTestBean.class, AnnotatedElementUtils.findMergedAnnotation(SpringMvcClientTestBean.class, ShenyuSpringMvcClient.class))); + + when(env.getProperty("spring.mvc.servlet.path")).thenReturn("/servlet-path"); + when(env.getProperty("server.servlet.context-path")).thenReturn("/servlet-context-path"); + Assert.assertEquals("super-path", "/servlet-context-path/servlet-path/order", springMvcClientEventListener.buildApiSuperPath( + SpringMvcClientTestBean.class, AnnotatedElementUtils.findMergedAnnotation(SpringMvcClientTestBean.class, ShenyuSpringMvcClient.class))); + registerUtilsMockedStatic.close(); + } + @RestController @RequestMapping("/order") @ShenyuSpringMvcClient(path = "/order") diff --git a/shenyu-spring-boot-starter/shenyu-spring-boot-starter-client/shenyu-spring-boot-starter-client-springmvc/src/main/java/org/apache/shenyu/springboot/starter/client/springmvc/ShenyuSpringMvcClientConfiguration.java b/shenyu-spring-boot-starter/shenyu-spring-boot-starter-client/shenyu-spring-boot-starter-client-springmvc/src/main/java/org/apache/shenyu/springboot/starter/client/springmvc/ShenyuSpringMvcClientConfiguration.java index 4a5dbdedd090..7a7f8fdc6f86 100644 --- a/shenyu-spring-boot-starter/shenyu-spring-boot-starter-client/shenyu-spring-boot-starter-client-springmvc/src/main/java/org/apache/shenyu/springboot/starter/client/springmvc/ShenyuSpringMvcClientConfiguration.java +++ b/shenyu-spring-boot-starter/shenyu-spring-boot-starter-client/shenyu-spring-boot-starter-client-springmvc/src/main/java/org/apache/shenyu/springboot/starter/client/springmvc/ShenyuSpringMvcClientConfiguration.java @@ -33,7 +33,6 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.env.Environment; - import java.util.Properties; /** @@ -74,6 +73,6 @@ public SpringMvcClientEventListener springHttpClientEventListener(final ShenyuCl props.setProperty(ShenyuClientConstants.CONTEXT_PATH, String.format("/%s", applicationName)); } } - return new SpringMvcClientEventListener(clientPropertiesConfig, shenyuClientRegisterRepository); + return new SpringMvcClientEventListener(clientPropertiesConfig, shenyuClientRegisterRepository, env); } }