diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/MvcUriComponentsBuilder.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/MvcUriComponentsBuilder.java index b3ab0b2f0aef..5e1ee52d85a4 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/MvcUriComponentsBuilder.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/MvcUriComponentsBuilder.java @@ -61,6 +61,7 @@ import org.springframework.web.method.HandlerMethod; import org.springframework.web.method.annotation.RequestParamMethodArgumentResolver; import org.springframework.web.method.support.CompositeUriComponentsContributor; +import org.springframework.web.service.annotation.HttpExchange; import org.springframework.web.servlet.DispatcherServlet; import org.springframework.web.servlet.mvc.method.RequestMappingInfoHandlerMapping; import org.springframework.web.servlet.support.ServletUriComponentsBuilder; @@ -69,7 +70,7 @@ /** * Creates instances of {@link org.springframework.web.util.UriComponentsBuilder} - * by pointing to {@code @RequestMapping} methods on Spring MVC controllers. + * by pointing to {@code @RequestMapping} or {@code @HttpExchange} methods on Spring MVC controllers. * *
There are several groups of methods: *
Note that this is a shorthand version of {@link #controller(Class)} intended * for inline use (with a static import), for example: *
@@ -342,10 +344,10 @@ public staticT on(Class controllerType) { } /** - * Return a "mock" controller instance. When an {@code @RequestMapping} method - * on the controller is invoked, the supplied argument values are remembered - * and the result can then be used to create {@code UriComponentsBuilder} via - * {@link #fromMethodCall(Object)}. + * Return a "mock" controller instance. When an {@code @RequestMapping} or + * {@code @HttpExchange} method on the controller is invoked, the supplied + * argument values are remembered and the result can then be used to create + * {@code UriComponentsBuilder} via {@link #fromMethodCall(Object)}. * This is a longer version of {@link #on(Class)}. It is needed with controller * methods returning void as well for repeated invocations. *
@@ -543,18 +545,27 @@ private static String getPathPrefix(Class> controllerType) { private static String getClassMapping(Class> controllerType) { Assert.notNull(controllerType, "'controllerType' must not be null"); RequestMapping mapping = AnnotatedElementUtils.findMergedAnnotation(controllerType, RequestMapping.class); - if (mapping == null) { - return ""; + if (mapping != null) { + return getPathMapping(mapping, controllerType.getName()); + } + HttpExchange httpExchange = AnnotatedElementUtils.findMergedAnnotation(controllerType, HttpExchange.class); + if (httpExchange != null) { + String url = httpExchange.url(); + return StringUtils.hasText(url) ? url : ""; } - return getPathMapping(mapping, controllerType.getName()); + return ""; } private static String getMethodMapping(AnnotatedMethod annotatedMethod) { RequestMapping requestMapping = annotatedMethod.getMethodAnnotation(RequestMapping.class); - if (requestMapping == null) { - throw new IllegalArgumentException("No @RequestMapping on: " + annotatedMethod.getMethod().toGenericString()); + if (requestMapping != null) { + return getPathMapping(requestMapping, annotatedMethod.getMethod().toGenericString()); + } + HttpExchange httpExchange = annotatedMethod.getMethodAnnotation(HttpExchange.class); + if (httpExchange != null) { + return httpExchange.url(); } - return getPathMapping(requestMapping, annotatedMethod.getMethod().toGenericString()); + throw new IllegalArgumentException("No @RequestMapping or @HttpExchange on: " + annotatedMethod.getMethod().toGenericString()); } private static String getPathMapping(RequestMapping requestMapping, String source) { diff --git a/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/MvcUriComponentsBuilderTests.java b/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/MvcUriComponentsBuilderTests.java index e7582d619a2d..018f5e4031fc 100644 --- a/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/MvcUriComponentsBuilderTests.java +++ b/spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/MvcUriComponentsBuilderTests.java @@ -56,6 +56,8 @@ import org.springframework.web.context.request.ServletRequestAttributes; import org.springframework.web.context.support.AnnotationConfigWebApplicationContext; import org.springframework.web.filter.ForwardedHeaderFilter; +import org.springframework.web.service.annotation.GetExchange; +import org.springframework.web.service.annotation.HttpExchange; import org.springframework.web.servlet.DispatcherServlet; import org.springframework.web.servlet.ModelAndView; import org.springframework.web.servlet.config.annotation.EnableWebMvc; @@ -84,6 +86,7 @@ * @author Dietrich Schulten * @author Rossen Stoyanchev * @author Sam Brannen + * @author Daeho Kwon */ @SuppressWarnings("unused") class MvcUriComponentsBuilderTests { @@ -357,6 +360,42 @@ void fromMethodNameWithAnnotationsOnInterface() { assertThat(uriComponents.toString()).isEqualTo("http://localhost/hello/test"); } + @Test + void fromControllerWithHttpExchange() { + UriComponents uriComponents = fromController(PersonHttpExchangeController.class).build(); + assertThat(uriComponents.toUriString()).endsWith("/exchange/people"); + } + + @Test + void fromMethodNameWithHttpExchange() { + UriComponents uriComponents = fromMethodName(PersonHttpExchangeController.class, "getPerson", 123L).build(); + assertThat(uriComponents.toUriString()).endsWith("/exchange/people/123"); + } + + @Test + void fromMethodCallWithHttpExchange() { + UriComponents uriComponents = fromMethodCall(on(PersonHttpExchangeController.class).getPerson(123L)).build(); + assertThat(uriComponents.toUriString()).endsWith("/exchange/people/123"); + } + + @Test + void fromControllerWithHttpExchangeOnInterface() { + UriComponents uriComponents = fromController(PersonHttpExchangeControllerImpl.class).build(); + assertThat(uriComponents.toUriString()).endsWith("/exchange/persons"); + } + + @Test + void fromMethodNameWithHttpExchangeOnInterface() { + UriComponents uriComponents = fromMethodName(PersonHttpExchangeControllerImpl.class, "getPerson", 123L).build(); + assertThat(uriComponents.toUriString()).endsWith("/exchange/persons/123"); + } + + @Test + void fromMethodCallWithHttpExchangeOnInterface() { + UriComponents uriComponents = fromMethodCall(on(PersonHttpExchangeControllerImpl.class).getPerson(123L)).build(); + assertThat(uriComponents.toUriString()).endsWith("/exchange/persons/123"); + } + @Test void fromMethodCallOnSubclass() { UriComponents uriComponents = fromMethodCall(on(ExtendedController.class).myMethod(null)).build(); @@ -900,4 +939,33 @@ public ResponseEntityget(String name) { } } + + @Controller + @HttpExchange("/exchange/people") + static class PersonHttpExchangeController { + + @GetExchange("/{id}") + HttpEntity getPerson(@PathVariable Long id) { + return null; + } + } + + + @HttpExchange("/exchange/persons") + interface PersonHttpExchangeInterface { + + @GetExchange("/{id}") + HttpEntity getPerson(@PathVariable Long id); + } + + + @Controller + static class PersonHttpExchangeControllerImpl implements PersonHttpExchangeInterface { + + @Override + public HttpEntity getPerson(Long id) { + return null; + } + } + }