Revised reference example for linkable controller method signature

Issue: SPR-16710
This commit is contained in:
Juergen Hoeller 2018-04-12 14:45:11 +02:00
parent 97ee94f4ca
commit 7ee6130680
2 changed files with 275 additions and 216 deletions

View File

@ -50,6 +50,7 @@ import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes; import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext; import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import org.springframework.web.servlet.DispatcherServlet; import org.springframework.web.servlet.DispatcherServlet;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.config.annotation.EnableWebMvc; import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.util.UriComponents; import org.springframework.web.util.UriComponents;
@ -85,38 +86,38 @@ public class MvcUriComponentsBuilderTests {
@Test @Test
public void testFromController() { public void fromControllerPlain() {
UriComponents uriComponents = fromController(PersonControllerImpl.class).build(); UriComponents uriComponents = fromController(PersonControllerImpl.class).build();
assertThat(uriComponents.toUriString(), Matchers.endsWith("/people")); assertThat(uriComponents.toUriString(), Matchers.endsWith("/people"));
} }
@Test @Test
public void testFromControllerUriTemplate() { public void fromControllerUriTemplate() {
UriComponents uriComponents = fromController(PersonsAddressesController.class).buildAndExpand(15); UriComponents uriComponents = fromController(PersonsAddressesController.class).buildAndExpand(15);
assertThat(uriComponents.toUriString(), endsWith("/people/15/addresses")); assertThat(uriComponents.toUriString(), endsWith("/people/15/addresses"));
} }
@Test @Test
public void testFromControllerSubResource() { public void fromControllerSubResource() {
UriComponents uriComponents = fromController(PersonControllerImpl.class).pathSegment("something").build(); UriComponents uriComponents = fromController(PersonControllerImpl.class).pathSegment("something").build();
assertThat(uriComponents.toUriString(), endsWith("/people/something")); assertThat(uriComponents.toUriString(), endsWith("/people/something"));
} }
@Test @Test
public void testFromControllerTwoTypeLevelMappings() { public void fromControllerTwoTypeLevelMappings() {
UriComponents uriComponents = fromController(InvalidController.class).build(); UriComponents uriComponents = fromController(InvalidController.class).build();
assertThat(uriComponents.toUriString(), is("http://localhost/persons")); assertThat(uriComponents.toUriString(), is("http://localhost/persons"));
} }
@Test @Test
public void testFromControllerNotMapped() { public void fromControllerNotMapped() {
UriComponents uriComponents = fromController(UnmappedController.class).build(); UriComponents uriComponents = fromController(UnmappedController.class).build();
assertThat(uriComponents.toUriString(), is("http://localhost/")); assertThat(uriComponents.toUriString(), is("http://localhost/"));
} }
@Test @Test
public void testFromControllerWithCustomBaseUrlViaStaticCall() { public void fromControllerWithCustomBaseUrlViaStaticCall() {
UriComponentsBuilder builder = UriComponentsBuilder.fromUriString("http://example.org:9090/base"); UriComponentsBuilder builder = UriComponentsBuilder.fromUriString("http://example.org:9090/base");
UriComponents uriComponents = fromController(builder, PersonControllerImpl.class).build(); UriComponents uriComponents = fromController(builder, PersonControllerImpl.class).build();
@ -125,217 +126,15 @@ public class MvcUriComponentsBuilderTests {
} }
@Test @Test
public void testFromControllerWithCustomBaseUrlViaInstance() { public void fromControllerWithCustomBaseUrlViaInstance() {
UriComponentsBuilder builder = UriComponentsBuilder.fromUriString("http://example.org:9090/base"); UriComponentsBuilder builder = UriComponentsBuilder.fromUriString("http://example.org:9090/base");
MvcUriComponentsBuilder mvcBuilder = MvcUriComponentsBuilder.relativeTo(builder); MvcUriComponentsBuilder mvcBuilder = relativeTo(builder);
UriComponents uriComponents = mvcBuilder.withController(PersonControllerImpl.class).build(); UriComponents uriComponents = mvcBuilder.withController(PersonControllerImpl.class).build();
assertEquals("http://example.org:9090/base/people", uriComponents.toString()); assertEquals("http://example.org:9090/base/people", uriComponents.toString());
assertEquals("http://example.org:9090/base", builder.toUriString()); assertEquals("http://example.org:9090/base", builder.toUriString());
} }
@Test
public void testFromMethodNamePathVariable() {
UriComponents uriComponents = fromMethodName(ControllerWithMethods.class,
"methodWithPathVariable", "1").build();
assertThat(uriComponents.toUriString(), is("http://localhost/something/1/foo"));
}
@Test
public void testFromMethodNameTypeLevelPathVariable() {
this.request.setContextPath("/myapp");
UriComponents uriComponents = fromMethodName(
PersonsAddressesController.class, "getAddressesForCountry", "DE").buildAndExpand("1");
assertThat(uriComponents.toUriString(), is("http://localhost/myapp/people/1/addresses/DE"));
}
@Test
public void testFromMethodNameTwoPathVariables() {
DateTime now = DateTime.now();
UriComponents uriComponents = fromMethodName(
ControllerWithMethods.class, "methodWithTwoPathVariables", 1, now).build();
assertThat(uriComponents.getPath(), is("/something/1/foo/" + ISODateTimeFormat.date().print(now)));
}
@Test
public void testFromMethodNameWithPathVarAndRequestParam() {
UriComponents uriComponents = fromMethodName(
ControllerWithMethods.class, "methodForNextPage", "1", 10, 5).build();
assertThat(uriComponents.getPath(), is("/something/1/foo"));
MultiValueMap<String, String> queryParams = uriComponents.getQueryParams();
assertThat(queryParams.get("limit"), contains("5"));
assertThat(queryParams.get("offset"), contains("10"));
}
@Test // SPR-12977
public void fromMethodNameWithBridgedMethod() {
UriComponents uriComponents = fromMethodName(PersonCrudController.class, "get", (long) 42).build();
assertThat(uriComponents.toUriString(), is("http://localhost/42"));
}
@Test // SPR-11391
public void testFromMethodNameTypeLevelPathVariableWithoutArgumentValue() {
UriComponents uriComponents = fromMethodName(UserContactController.class, "showCreate", 123).build();
assertThat(uriComponents.getPath(), is("/user/123/contacts/create"));
}
@Test
public void testFromMethodNameNotMapped() {
UriComponents uriComponents = fromMethodName(UnmappedController.class, "unmappedMethod").build();
assertThat(uriComponents.toUriString(), is("http://localhost/"));
}
@Test
public void testFromMethodNameWithCustomBaseUrlViaStaticCall() {
UriComponentsBuilder builder = UriComponentsBuilder.fromUriString("http://example.org:9090/base");
UriComponents uriComponents = fromMethodName(builder, ControllerWithMethods.class,
"methodWithPathVariable", "1").build();
assertEquals("http://example.org:9090/base/something/1/foo", uriComponents.toString());
assertEquals("http://example.org:9090/base", builder.toUriString());
}
@Test
public void testFromMethodNameWithCustomBaseUrlViaInstance() {
UriComponentsBuilder builder = UriComponentsBuilder.fromUriString("http://example.org:9090/base");
MvcUriComponentsBuilder mvcBuilder = MvcUriComponentsBuilder.relativeTo(builder);
UriComponents uriComponents = mvcBuilder.withMethodName(ControllerWithMethods.class,
"methodWithPathVariable", "1").build();
assertEquals("http://example.org:9090/base/something/1/foo", uriComponents.toString());
assertEquals("http://example.org:9090/base", builder.toUriString());
}
@Test
public void testFromMethodNameWithMetaAnnotation() {
UriComponents uriComponents = fromMethodName(MetaAnnotationController.class, "handleInput").build();
assertThat(uriComponents.toUriString(), is("http://localhost/input"));
}
@Test // SPR-14405
public void testFromMappingNameWithOptionalParam() {
UriComponents uriComponents = fromMethodName(ControllerWithMethods.class,
"methodWithOptionalParam", new Object[] {null}).build();
assertThat(uriComponents.toUriString(), is("http://localhost/something/optional-param"));
}
@Test
public void testFromMethodCall() {
UriComponents uriComponents = fromMethodCall(on(ControllerWithMethods.class).myMethod(null)).build();
assertThat(uriComponents.toUriString(), startsWith("http://localhost"));
assertThat(uriComponents.toUriString(), endsWith("/something/else"));
}
@Test
public void testFromMethodCallOnSubclass() {
UriComponents uriComponents = fromMethodCall(on(ExtendedController.class).myMethod(null)).build();
assertThat(uriComponents.toUriString(), startsWith("http://localhost"));
assertThat(uriComponents.toUriString(), endsWith("/extended/else"));
}
@Test
public void testFromMethodCallWithTypeLevelUriVars() {
UriComponents uriComponents = fromMethodCall(
on(PersonsAddressesController.class).getAddressesForCountry("DE")).buildAndExpand(15);
assertThat(uriComponents.toUriString(), endsWith("/people/15/addresses/DE"));
}
@Test
public void testFromMethodCallWithPathVar() {
UriComponents uriComponents = fromMethodCall(
on(ControllerWithMethods.class).methodWithPathVariable("1")).build();
assertThat(uriComponents.toUriString(), startsWith("http://localhost"));
assertThat(uriComponents.toUriString(), endsWith("/something/1/foo"));
}
@Test
public void testFromMethodCallWithPathVarAndRequestParams() {
UriComponents uriComponents = fromMethodCall(
on(ControllerWithMethods.class).methodForNextPage("1", 10, 5)).build();
assertThat(uriComponents.getPath(), is("/something/1/foo"));
MultiValueMap<String, String> queryParams = uriComponents.getQueryParams();
assertThat(queryParams.get("limit"), contains("5"));
assertThat(queryParams.get("offset"), contains("10"));
}
@Test
public void testFromMethodCallWithPathVarAndMultiValueRequestParams() {
UriComponents uriComponents = fromMethodCall(
on(ControllerWithMethods.class).methodWithMultiValueRequestParams("1", Arrays.asList(3, 7), 5)).build();
assertThat(uriComponents.getPath(), is("/something/1/foo"));
MultiValueMap<String, String> queryParams = uriComponents.getQueryParams();
assertThat(queryParams.get("limit"), contains("5"));
assertThat(queryParams.get("items"), containsInAnyOrder("3", "7"));
}
@Test
public void testFromMethodCallWithCustomBaseUrlViaStaticCall() {
UriComponentsBuilder builder = UriComponentsBuilder.fromUriString("http://example.org:9090/base");
UriComponents uriComponents = fromMethodCall(builder, on(ControllerWithMethods.class).myMethod(null)).build();
assertEquals("http://example.org:9090/base/something/else", uriComponents.toString());
assertEquals("http://example.org:9090/base", builder.toUriString());
}
@Test
public void testFromMethodCallWithCustomBaseUrlViaInstance() {
UriComponentsBuilder builder = UriComponentsBuilder.fromUriString("http://example.org:9090/base");
MvcUriComponentsBuilder mvcBuilder = MvcUriComponentsBuilder.relativeTo(builder);
UriComponents result = mvcBuilder.withMethodCall(on(ControllerWithMethods.class).myMethod(null)).build();
assertEquals("http://example.org:9090/base/something/else", result.toString());
assertEquals("http://example.org:9090/base", builder.toUriString());
}
@Test
public void testFromMappingName() {
AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
context.setServletContext(new MockServletContext());
context.register(WebConfig.class);
context.refresh();
this.request.setAttribute(DispatcherServlet.WEB_APPLICATION_CONTEXT_ATTRIBUTE, context);
this.request.setServerName("example.org");
this.request.setServerPort(9999);
this.request.setContextPath("/base");
String mappingName = "PAC#getAddressesForCountry";
String url = MvcUriComponentsBuilder.fromMappingName(mappingName).arg(0, "DE").buildAndExpand(123);
assertEquals("/base/people/123/addresses/DE", url);
}
@Test
public void testFromMappingNameWithCustomBaseUrl() {
AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
context.setServletContext(new MockServletContext());
context.register(WebConfig.class);
context.refresh();
this.request.setAttribute(DispatcherServlet.WEB_APPLICATION_CONTEXT_ATTRIBUTE, context);
UriComponentsBuilder baseUrl = UriComponentsBuilder.fromUriString("http://example.org:9999/base");
MvcUriComponentsBuilder mvcBuilder = MvcUriComponentsBuilder.relativeTo(baseUrl);
String url = mvcBuilder.withMappingName("PAC#getAddressesForCountry").arg(0, "DE").buildAndExpand(123);
assertEquals("http://example.org:9999/base/people/123/addresses/DE", url);
}
@Test @Test
public void usesForwardedHostAsHostIfHeaderIsSet() { public void usesForwardedHostAsHostIfHeaderIsSet() {
this.request.addHeader("X-Forwarded-Host", "somethingDifferent"); this.request.addHeader("X-Forwarded-Host", "somethingDifferent");
@ -360,13 +159,240 @@ public class MvcUriComponentsBuilderTests {
assertThat(uriComponents.toUriString(), startsWith("http://barfoo:8888")); assertThat(uriComponents.toUriString(), startsWith("http://barfoo:8888"));
} }
@Test
public void fromMethodNamePathVariable() {
UriComponents uriComponents = fromMethodName(ControllerWithMethods.class,
"methodWithPathVariable", "1").build();
assertThat(uriComponents.toUriString(), is("http://localhost/something/1/foo"));
}
@Test
public void fromMethodNameTypeLevelPathVariable() {
this.request.setContextPath("/myapp");
UriComponents uriComponents = fromMethodName(
PersonsAddressesController.class, "getAddressesForCountry", "DE").buildAndExpand("1");
assertThat(uriComponents.toUriString(), is("http://localhost/myapp/people/1/addresses/DE"));
}
@Test
public void fromMethodNameTwoPathVariables() {
DateTime now = DateTime.now();
UriComponents uriComponents = fromMethodName(
ControllerWithMethods.class, "methodWithTwoPathVariables", 1, now).build();
assertThat(uriComponents.getPath(), is("/something/1/foo/" + ISODateTimeFormat.date().print(now)));
}
@Test
public void fromMethodNameWithPathVarAndRequestParam() {
UriComponents uriComponents = fromMethodName(
ControllerWithMethods.class, "methodForNextPage", "1", 10, 5).build();
assertThat(uriComponents.getPath(), is("/something/1/foo"));
MultiValueMap<String, String> queryParams = uriComponents.getQueryParams();
assertThat(queryParams.get("limit"), contains("5"));
assertThat(queryParams.get("offset"), contains("10"));
}
@Test // SPR-12977
public void fromMethodNameWithBridgedMethod() {
UriComponents uriComponents = fromMethodName(PersonCrudController.class, "get", (long) 42).build();
assertThat(uriComponents.toUriString(), is("http://localhost/42"));
}
@Test // SPR-11391
public void fromMethodNameTypeLevelPathVariableWithoutArgumentValue() {
UriComponents uriComponents = fromMethodName(UserContactController.class, "showCreate", 123).build();
assertThat(uriComponents.getPath(), is("/user/123/contacts/create"));
}
@Test
public void fromMethodNameNotMapped() {
UriComponents uriComponents = fromMethodName(UnmappedController.class, "unmappedMethod").build();
assertThat(uriComponents.toUriString(), is("http://localhost/"));
}
@Test
public void fromMethodNameWithCustomBaseUrlViaStaticCall() {
UriComponentsBuilder builder = UriComponentsBuilder.fromUriString("http://example.org:9090/base");
UriComponents uriComponents = fromMethodName(builder, ControllerWithMethods.class,
"methodWithPathVariable", "1").build();
assertEquals("http://example.org:9090/base/something/1/foo", uriComponents.toString());
assertEquals("http://example.org:9090/base", builder.toUriString());
}
@Test
public void fromMethodNameWithCustomBaseUrlViaInstance() {
UriComponentsBuilder builder = UriComponentsBuilder.fromUriString("http://example.org:9090/base");
MvcUriComponentsBuilder mvcBuilder = relativeTo(builder);
UriComponents uriComponents = mvcBuilder.withMethodName(ControllerWithMethods.class,
"methodWithPathVariable", "1").build();
assertEquals("http://example.org:9090/base/something/1/foo", uriComponents.toString());
assertEquals("http://example.org:9090/base", builder.toUriString());
}
@Test // SPR-14405
public void fromMethodNameWithOptionalParam() {
UriComponents uriComponents = fromMethodName(ControllerWithMethods.class,
"methodWithOptionalParam", new Object[] {null}).build();
assertThat(uriComponents.toUriString(), is("http://localhost/something/optional-param"));
}
@Test
public void fromMethodNameWithMetaAnnotation() {
UriComponents uriComponents = fromMethodName(MetaAnnotationController.class, "handleInput").build();
assertThat(uriComponents.toUriString(), is("http://localhost/input"));
}
@Test
public void fromMethodCallPlain() {
UriComponents uriComponents = fromMethodCall(on(ControllerWithMethods.class).myMethod(null)).build();
assertThat(uriComponents.toUriString(), startsWith("http://localhost"));
assertThat(uriComponents.toUriString(), endsWith("/something/else"));
}
@Test
public void fromMethodCallOnSubclass() {
UriComponents uriComponents = fromMethodCall(on(ExtendedController.class).myMethod(null)).build();
assertThat(uriComponents.toUriString(), startsWith("http://localhost"));
assertThat(uriComponents.toUriString(), endsWith("/extended/else"));
}
@Test
public void fromMethodCallWithTypeLevelUriVars() {
UriComponents uriComponents = fromMethodCall(
on(PersonsAddressesController.class).getAddressesForCountry("DE")).buildAndExpand(15);
assertThat(uriComponents.toUriString(), endsWith("/people/15/addresses/DE"));
}
@Test
public void fromMethodCallWithPathVariable() {
UriComponents uriComponents = fromMethodCall(
on(ControllerWithMethods.class).methodWithPathVariable("1")).build();
assertThat(uriComponents.toUriString(), startsWith("http://localhost"));
assertThat(uriComponents.toUriString(), endsWith("/something/1/foo"));
}
@Test
public void fromMethodCallWithPathVariableAndRequestParams() {
UriComponents uriComponents = fromMethodCall(
on(ControllerWithMethods.class).methodForNextPage("1", 10, 5)).build();
assertThat(uriComponents.getPath(), is("/something/1/foo"));
MultiValueMap<String, String> queryParams = uriComponents.getQueryParams();
assertThat(queryParams.get("limit"), contains("5"));
assertThat(queryParams.get("offset"), contains("10"));
}
@Test
public void fromMethodCallWithPathVariableAndMultiValueRequestParams() {
UriComponents uriComponents = fromMethodCall(
on(ControllerWithMethods.class).methodWithMultiValueRequestParams("1", Arrays.asList(3, 7), 5)).build();
assertThat(uriComponents.getPath(), is("/something/1/foo"));
MultiValueMap<String, String> queryParams = uriComponents.getQueryParams();
assertThat(queryParams.get("limit"), contains("5"));
assertThat(queryParams.get("items"), containsInAnyOrder("3", "7"));
}
@Test
public void fromMethodCallWithCustomBaseUrlViaStaticCall() {
UriComponentsBuilder builder = UriComponentsBuilder.fromUriString("http://example.org:9090/base");
UriComponents uriComponents = fromMethodCall(builder, on(ControllerWithMethods.class).myMethod(null)).build();
assertEquals("http://example.org:9090/base/something/else", uriComponents.toString());
assertEquals("http://example.org:9090/base", builder.toUriString());
}
@Test
public void fromMethodCallWithCustomBaseUrlViaInstance() {
UriComponentsBuilder builder = UriComponentsBuilder.fromUriString("http://example.org:9090/base");
MvcUriComponentsBuilder mvcBuilder = relativeTo(builder);
UriComponents result = mvcBuilder.withMethodCall(on(ControllerWithMethods.class).myMethod(null)).build();
assertEquals("http://example.org:9090/base/something/else", result.toString());
assertEquals("http://example.org:9090/base", builder.toUriString());
}
@Test // SPR-16710 @Test // SPR-16710
public void withStringReturnType() { public void fromMethodCallWithModelAndViewReturnType() {
UriComponents uriComponents = MvcUriComponentsBuilder.fromMethodCall( UriComponents uriComponents = fromMethodCall(
on(BookingController.class).getBooking(21L)).buildAndExpand(42); on(BookingControllerWithModelAndView.class).getBooking(21L)).buildAndExpand(42);
assertEquals("http://localhost/hotels/42/bookings/21", uriComponents.encode().toUri().toString()); assertEquals("http://localhost/hotels/42/bookings/21", uriComponents.encode().toUri().toString());
} }
@Test // SPR-16710
public void fromMethodCallWithObjectReturnType() {
UriComponents uriComponents = fromMethodCall(
on(BookingControllerWithObject.class).getBooking(21L)).buildAndExpand(42);
assertEquals("http://localhost/hotels/42/bookings/21", uriComponents.encode().toUri().toString());
}
@Test(expected = IllegalStateException.class) // SPR-16710
public void fromMethodCallWithStringReturnType() {
UriComponents uriComponents = fromMethodCall(
on(BookingControllerWithString.class).getBooking(21L)).buildAndExpand(42);
assertEquals("http://localhost/hotels/42/bookings/21", uriComponents.encode().toUri().toString());
}
@Test // SPR-16710
public void fromMethodNameWithStringReturnType() {
UriComponents uriComponents = fromMethodName(
BookingControllerWithString.class, "getBooking", 21L).buildAndExpand(42);
assertEquals("http://localhost/hotels/42/bookings/21", uriComponents.encode().toUri().toString());
}
@Test
public void fromMappingNamePlain() {
AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
context.setServletContext(new MockServletContext());
context.register(WebConfig.class);
context.refresh();
this.request.setAttribute(DispatcherServlet.WEB_APPLICATION_CONTEXT_ATTRIBUTE, context);
this.request.setServerName("example.org");
this.request.setServerPort(9999);
this.request.setContextPath("/base");
String mappingName = "PAC#getAddressesForCountry";
String url = fromMappingName(mappingName).arg(0, "DE").buildAndExpand(123);
assertEquals("/base/people/123/addresses/DE", url);
}
@Test
public void fromMappingNameWithCustomBaseUrl() {
AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
context.setServletContext(new MockServletContext());
context.register(WebConfig.class);
context.refresh();
this.request.setAttribute(DispatcherServlet.WEB_APPLICATION_CONTEXT_ATTRIBUTE, context);
UriComponentsBuilder baseUrl = UriComponentsBuilder.fromUriString("http://example.org:9999/base");
MvcUriComponentsBuilder mvcBuilder = relativeTo(baseUrl);
String url = mvcBuilder.withMappingName("PAC#getAddressesForCountry").arg(0, "DE").buildAndExpand(123);
assertEquals("http://example.org:9999/base/people/123/addresses/DE", url);
}
static class Person { static class Person {
@ -516,7 +542,18 @@ public class MvcUriComponentsBuilderTests {
@Controller @Controller
@RequestMapping("/hotels/{hotel}") @RequestMapping("/hotels/{hotel}")
public class BookingController { static class BookingControllerWithModelAndView {
@GetMapping("/bookings/{booking}")
public ModelAndView getBooking(@PathVariable Long booking) {
return new ModelAndView("url");
}
}
@Controller
@RequestMapping("/hotels/{hotel}")
static class BookingControllerWithObject {
@GetMapping("/bookings/{booking}") @GetMapping("/bookings/{booking}")
public Object getBooking(@PathVariable Long booking) { public Object getBooking(@PathVariable Long booking) {
@ -524,4 +561,15 @@ public class MvcUriComponentsBuilderTests {
} }
} }
@Controller
@RequestMapping("/hotels/{hotel}")
static class BookingControllerWithString {
@GetMapping("/bookings/{booking}")
public String getBooking(@PathVariable Long booking) {
return "url";
}
}
} }

View File

@ -3098,7 +3098,8 @@ per request, and also provides an option to remove and ignore such headers.
[[mvc-links-to-controllers]] [[mvc-links-to-controllers]]
=== Links to controllers === Links to controllers
Spring MVC provides a mechanism to prepare links to controller methods. For example: Spring MVC provides a mechanism to prepare links to controller methods. For example,
the following MVC controller easily allows for link creation:
[source,java,indent=0] [source,java,indent=0]
[subs="verbatim,quotes"] [subs="verbatim,quotes"]
@ -3108,7 +3109,7 @@ Spring MVC provides a mechanism to prepare links to controller methods. For exam
public class BookingController { public class BookingController {
@GetMapping("/bookings/{booking}") @GetMapping("/bookings/{booking}")
public String getBooking(@PathVariable Long booking) { public ModelAndView getBooking(@PathVariable Long booking) {
// ... // ...
} }
} }
@ -3145,6 +3146,16 @@ akin to mock testing through proxies to avoid referring to the controller method
URI uri = uriComponents.encode().toUri(); URI uri = uriComponents.encode().toUri();
---- ----
[NOTE]
====
Controller method signatures are limited in their design when supposed to be usable for
link creation with `fromMethodCall`. Aside from needing a proper parameter signature,
there is a technical limitation on the return type: namely generating a runtime proxy
for link builder invocations, so the return type must not be `final`. In particular,
the common `String` return type for view names does not work here; use `ModelAndView`
or even plain `Object` (with a `String` return value) instead.
====
The above examples use static methods in `MvcUriComponentsBuilder`. Internally they rely The above examples use static methods in `MvcUriComponentsBuilder`. Internally they rely
on `ServletUriComponentsBuilder` to prepare a base URL from the scheme, host, port, on `ServletUriComponentsBuilder` to prepare a base URL from the scheme, host, port,
context path and servlet path of the current request. This works well in most cases, context path and servlet path of the current request. This works well in most cases,