Integrate suggested support for creating MVC URLs

The key contract is MvcUrls. An instance is automatically created with
the Spring MVC namespace and the MVC Java config but can also be easily
created in any configuration.

Some example tests can be found in DefaultMvcUrlsTests.

Issue: SPR-10665, SPR-8826
This commit is contained in:
Rossen Stoyanchev
2013-10-20 10:01:54 -04:00
parent 4fd27b12fc
commit bafc73f147
33 changed files with 1313 additions and 2088 deletions

View File

@@ -56,6 +56,8 @@ import org.springframework.web.accept.ContentNegotiationManager;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.context.request.ServletWebRequest;
import org.springframework.web.context.request.async.CallableProcessingInterceptor;
import org.springframework.web.context.request.async.CallableProcessingInterceptorAdapter;
@@ -76,11 +78,14 @@ import org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter;
import org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
import org.springframework.web.servlet.mvc.support.MvcUrls;
import org.springframework.web.servlet.resource.DefaultServletHttpRequestHandler;
import org.springframework.web.servlet.resource.ResourceHttpRequestHandler;
import org.springframework.web.servlet.theme.ThemeChangeInterceptor;
import org.springframework.web.util.UriComponents;
import static org.junit.Assert.*;
import static org.springframework.web.servlet.mvc.support.MvcUrlUtils.*;
/**
* @author Keith Donald
@@ -108,7 +113,7 @@ public class MvcNamespaceTests {
@Test
public void testDefaultConfig() throws Exception {
loadBeanDefinitions("mvc-config.xml", 12);
loadBeanDefinitions("mvc-config.xml", 13);
RequestMappingHandlerMapping mapping = appContext.getBean(RequestMappingHandlerMapping.class);
assertNotNull(mapping);
@@ -147,11 +152,26 @@ public class MvcNamespaceTests {
adapter.handle(request, response, handlerMethod);
assertTrue(handler.recordedValidationError);
// MvcUrls
RequestContextHolder.setRequestAttributes(new ServletRequestAttributes(new MockHttpServletRequest()));
try {
Date now = new Date();
TestController testController = controller(TestController.class);
testController.testBind(now, null, null);
MvcUrls mvcUrls = this.appContext.getBean(MvcUrls.class);
UriComponents uriComponents = mvcUrls.linkToMethodOn(testController);
assertEquals("http://localhost/?date=2013-10-21", uriComponents.toUriString());
}
finally {
RequestContextHolder.resetRequestAttributes();
}
}
@Test(expected=TypeMismatchException.class)
public void testCustomConversionService() throws Exception {
loadBeanDefinitions("mvc-config-custom-conversion-service.xml", 12);
loadBeanDefinitions("mvc-config-custom-conversion-service.xml", 13);
RequestMappingHandlerMapping mapping = appContext.getBean(RequestMappingHandlerMapping.class);
assertNotNull(mapping);
@@ -177,7 +197,7 @@ public class MvcNamespaceTests {
@Test
public void testCustomValidator() throws Exception {
loadBeanDefinitions("mvc-config-custom-validator.xml", 12);
loadBeanDefinitions("mvc-config-custom-validator.xml", 13);
RequestMappingHandlerMapping mapping = appContext.getBean(RequestMappingHandlerMapping.class);
assertNotNull(mapping);
@@ -199,7 +219,7 @@ public class MvcNamespaceTests {
@Test
public void testInterceptors() throws Exception {
loadBeanDefinitions("mvc-config-interceptors.xml", 17);
loadBeanDefinitions("mvc-config-interceptors.xml", 18);
RequestMappingHandlerMapping mapping = appContext.getBean(RequestMappingHandlerMapping.class);
assertNotNull(mapping);
@@ -328,7 +348,7 @@ public class MvcNamespaceTests {
@Test
public void testBeanDecoration() throws Exception {
loadBeanDefinitions("mvc-config-bean-decoration.xml", 14);
loadBeanDefinitions("mvc-config-bean-decoration.xml", 15);
RequestMappingHandlerMapping mapping = appContext.getBean(RequestMappingHandlerMapping.class);
assertNotNull(mapping);
@@ -349,7 +369,7 @@ public class MvcNamespaceTests {
@Test
public void testViewControllers() throws Exception {
loadBeanDefinitions("mvc-config-view-controllers.xml", 15);
loadBeanDefinitions("mvc-config-view-controllers.xml", 16);
RequestMappingHandlerMapping mapping = appContext.getBean(RequestMappingHandlerMapping.class);
assertNotNull(mapping);
@@ -409,7 +429,7 @@ public class MvcNamespaceTests {
/** WebSphere gives trailing servlet path slashes by default!! */
@Test
public void testViewControllersOnWebSphere() throws Exception {
loadBeanDefinitions("mvc-config-view-controllers.xml", 15);
loadBeanDefinitions("mvc-config-view-controllers.xml", 16);
SimpleUrlHandlerMapping mapping2 = appContext.getBean(SimpleUrlHandlerMapping.class);
SimpleControllerHandlerAdapter adapter = appContext.getBean(SimpleControllerHandlerAdapter.class);
@@ -462,7 +482,7 @@ public class MvcNamespaceTests {
@Test
public void testContentNegotiationManager() throws Exception {
loadBeanDefinitions("mvc-config-content-negotiation-manager.xml", 12);
loadBeanDefinitions("mvc-config-content-negotiation-manager.xml", 13);
RequestMappingHandlerMapping mapping = appContext.getBean(RequestMappingHandlerMapping.class);
ContentNegotiationManager manager = mapping.getContentNegotiationManager();
@@ -474,7 +494,7 @@ public class MvcNamespaceTests {
@Test
public void testAsyncSupportOptions() throws Exception {
loadBeanDefinitions("mvc-config-async-support.xml", 13);
loadBeanDefinitions("mvc-config-async-support.xml", 14);
RequestMappingHandlerAdapter adapter = appContext.getBean(RequestMappingHandlerAdapter.class);
assertNotNull(adapter);

View File

@@ -16,28 +16,33 @@
package org.springframework.web.servlet.config.annotation;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import java.util.ArrayList;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import org.joda.time.DateTime;
import org.joda.time.format.ISODateTimeFormat;
import org.junit.Before;
import org.junit.Test;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.convert.ConversionService;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.format.annotation.DateTimeFormat.ISO;
import org.springframework.format.support.FormattingConversionService;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.HttpEntity;
import org.springframework.mock.web.test.MockHttpServletRequest;
import org.springframework.mock.web.test.MockServletContext;
import org.springframework.stereotype.Controller;
import org.springframework.validation.Validator;
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.support.ConfigurableWebBindingInitializer;
import org.springframework.web.context.support.StaticWebApplicationContext;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.HandlerExecutionChain;
import org.springframework.web.servlet.handler.AbstractHandlerMapping;
@@ -49,6 +54,11 @@ import org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExc
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
import org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver;
import org.springframework.web.servlet.mvc.support.MvcUrls;
import org.springframework.web.util.UriComponents;
import static org.junit.Assert.*;
import static org.springframework.web.servlet.mvc.support.MvcUrlUtils.*;
/**
* A test fixture with an {@link WebMvcConfigurationSupport} instance.
@@ -57,27 +67,26 @@ import org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolv
*/
public class WebMvcConfigurationSupportTests {
private WebMvcConfigurationSupport mvcConfiguration;
private StaticWebApplicationContext wac;
private WebApplicationContext wac;
@Before
public void setUp() {
this.wac = new StaticWebApplicationContext();
this.mvcConfiguration = new WebMvcConfigurationSupport();
this.mvcConfiguration.setApplicationContext(wac);
AnnotationConfigWebApplicationContext cxt = new AnnotationConfigWebApplicationContext();
cxt.setServletContext(new MockServletContext());
cxt.register(TestConfig.class);
cxt.refresh();
this.wac = cxt;
}
@Test
public void requestMappingHandlerMapping() throws Exception {
this.wac.registerSingleton("controller", TestController.class);
RequestMappingHandlerMapping handlerMapping = mvcConfiguration.requestMappingHandlerMapping();
RequestMappingHandlerMapping handlerMapping = this.wac.getBean(RequestMappingHandlerMapping.class);
assertEquals(0, handlerMapping.getOrder());
handlerMapping.setApplicationContext(this.wac);
handlerMapping.afterPropertiesSet();
HandlerExecutionChain chain = handlerMapping.getHandler(new MockHttpServletRequest("GET", "/"));
assertNotNull(chain.getInterceptors());
assertEquals(ConversionServiceExposingInterceptor.class, chain.getInterceptors()[0].getClass());
@@ -85,7 +94,10 @@ public class WebMvcConfigurationSupportTests {
@Test
public void emptyViewControllerHandlerMapping() {
AbstractHandlerMapping handlerMapping = (AbstractHandlerMapping) mvcConfiguration.viewControllerHandlerMapping();
AbstractHandlerMapping handlerMapping = this.wac.getBean(
"viewControllerHandlerMapping", AbstractHandlerMapping.class);
assertNotNull(handlerMapping);
assertEquals(Integer.MAX_VALUE, handlerMapping.getOrder());
assertTrue(handlerMapping.getClass().getName().endsWith("EmptyHandlerMapping"));
@@ -93,16 +105,13 @@ public class WebMvcConfigurationSupportTests {
@Test
public void beanNameHandlerMapping() throws Exception {
StaticWebApplicationContext cxt = new StaticWebApplicationContext();
cxt.registerSingleton("/controller", TestController.class);
HttpServletRequest request = new MockHttpServletRequest("GET", "/controller");
BeanNameUrlHandlerMapping handlerMapping = mvcConfiguration.beanNameHandlerMapping();
BeanNameUrlHandlerMapping handlerMapping = this.wac.getBean(BeanNameUrlHandlerMapping.class);
assertEquals(2, handlerMapping.getOrder());
handlerMapping.setApplicationContext(cxt);
HttpServletRequest request = new MockHttpServletRequest("GET", "/testController");
HandlerExecutionChain chain = handlerMapping.getHandler(request);
assertNotNull(chain.getInterceptors());
assertEquals(2, chain.getInterceptors().length);
assertEquals(ConversionServiceExposingInterceptor.class, chain.getInterceptors()[1].getClass());
@@ -110,8 +119,10 @@ public class WebMvcConfigurationSupportTests {
@Test
public void emptyResourceHandlerMapping() {
mvcConfiguration.setApplicationContext(new StaticWebApplicationContext());
AbstractHandlerMapping handlerMapping = (AbstractHandlerMapping) mvcConfiguration.resourceHandlerMapping();
AbstractHandlerMapping handlerMapping = this.wac.getBean(
"resourceHandlerMapping", AbstractHandlerMapping.class);
assertNotNull(handlerMapping);
assertEquals(Integer.MAX_VALUE, handlerMapping.getOrder());
assertTrue(handlerMapping.getClass().getName().endsWith("EmptyHandlerMapping"));
@@ -119,8 +130,10 @@ public class WebMvcConfigurationSupportTests {
@Test
public void emptyDefaultServletHandlerMapping() {
mvcConfiguration.setServletContext(new MockServletContext());
AbstractHandlerMapping handlerMapping = (AbstractHandlerMapping) mvcConfiguration.defaultServletHandlerMapping();
AbstractHandlerMapping handlerMapping = this.wac.getBean(
"defaultServletHandlerMapping", AbstractHandlerMapping.class);
assertNotNull(handlerMapping);
assertEquals(Integer.MAX_VALUE, handlerMapping.getOrder());
assertTrue(handlerMapping.getClass().getName().endsWith("EmptyHandlerMapping"));
@@ -128,11 +141,10 @@ public class WebMvcConfigurationSupportTests {
@Test
public void requestMappingHandlerAdapter() throws Exception {
RequestMappingHandlerAdapter adapter = mvcConfiguration.requestMappingHandlerAdapter();
List<HttpMessageConverter<?>> expectedConverters = new ArrayList<HttpMessageConverter<?>>();
mvcConfiguration.addDefaultHttpMessageConverters(expectedConverters);
assertEquals(expectedConverters.size(), adapter.getMessageConverters().size());
RequestMappingHandlerAdapter adapter = this.wac.getBean(RequestMappingHandlerAdapter.class);
assertEquals(9, adapter.getMessageConverters().size());
ConfigurableWebBindingInitializer initializer = (ConfigurableWebBindingInitializer) adapter.getWebBindingInitializer();
assertNotNull(initializer);
@@ -146,10 +158,27 @@ public class WebMvcConfigurationSupportTests {
assertTrue(validator instanceof LocalValidatorFactoryBean);
}
@Test
public void mvcUrls() throws Exception {
RequestContextHolder.setRequestAttributes(new ServletRequestAttributes(new MockHttpServletRequest()));
try {
DateTime now = DateTime.now();
MvcUrls mvcUrls = this.wac.getBean(MvcUrls.class);
UriComponents uriComponents = mvcUrls.linkToMethodOn(controller(
TestController.class).methodWithTwoPathVariables(1, now));
assertEquals("/foo/1/bar/" + ISODateTimeFormat.date().print(now), uriComponents.getPath());
}
finally {
RequestContextHolder.resetRequestAttributes();
}
}
@Test
public void handlerExceptionResolver() throws Exception {
HandlerExceptionResolverComposite compositeResolver =
(HandlerExceptionResolverComposite) mvcConfiguration.handlerExceptionResolver();
this.wac.getBean("handlerExceptionResolver", HandlerExceptionResolverComposite.class);
assertEquals(0, compositeResolver.getOrder());
@@ -164,12 +193,28 @@ public class WebMvcConfigurationSupportTests {
}
@EnableWebMvc
@Configuration
public static class TestConfig {
@Bean(name={"/testController"})
public TestController testController() {
return new TestController();
}
}
@Controller
private static class TestController {
@RequestMapping("/")
public void handle() {
}
@RequestMapping("/foo/{id}/bar/{date}")
public HttpEntity<Void> methodWithTwoPathVariables(@PathVariable Integer id,
@DateTimeFormat(iso = ISO.DATE) @PathVariable DateTime date) {
return null;
}
}
}

View File

@@ -1,112 +0,0 @@
/*
* Copyright 2012-2013 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
*
* 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.springframework.web.servlet.hypermedia;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import org.junit.Test;
import org.springframework.core.AnnotationAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;
/**
* Unit tests for {@link AnnotationMappingDiscoverer}.
*
* @author Oliver Gierke
*/
public class AnnotationMappingDiscovererUnitTests {
AnnotationMappingDiscoverer discoverer = new AnnotationMappingDiscoverer(
RequestMapping.class);
@Test(expected = IllegalArgumentException.class)
public void rejectsNullAnnotationType() {
new AnnotationMappingDiscoverer((Class<? extends Annotation>) null);
}
@Test(expected = IllegalArgumentException.class)
public void rejectsNullAnnotationAttribute() {
new AnnotationMappingDiscoverer((AnnotationAttribute) null);
}
@Test
public void discoversTypeLevelMapping() {
assertThat(discoverer.getMapping(MyController.class), is("/type"));
}
@Test
public void discoversMethodLevelMapping() throws Exception {
Method method = MyController.class.getMethod("method");
assertThat(discoverer.getMapping(method), is("/type/method"));
}
@Test
public void returnsNullForNonExistentTypeLevelMapping() {
assertThat(discoverer.getMapping(ControllerWithoutTypeLevelMapping.class),
is(nullValue()));
}
@Test
public void resolvesMethodLevelMappingWithoutTypeLevelMapping() throws Exception {
Method method = ControllerWithoutTypeLevelMapping.class.getMethod("method");
assertThat(discoverer.getMapping(method), is("/method"));
}
@Test
public void resolvesMethodLevelMappingWithSlashRootMapping() throws Exception {
Method method = SlashRootMapping.class.getMethod("method");
assertThat(discoverer.getMapping(method), is("/method"));
}
/**
* @see #46
*/
@Test
public void treatsMissingMethodMappingAsEmptyMapping() throws Exception {
Method method = MyController.class.getMethod("noMethodMapping");
assertThat(discoverer.getMapping(method), is("/type"));
}
@RequestMapping("/type")
interface MyController {
@RequestMapping("/method")
void method();
@RequestMapping
void noMethodMapping();
}
interface ControllerWithoutTypeLevelMapping {
@RequestMapping("/method")
void method();
}
@RequestMapping("/")
interface SlashRootMapping {
@RequestMapping("/method")
void method();
}
}

View File

@@ -1,131 +0,0 @@
/*
* Copyright 2012-2013 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
*
* 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.springframework.web.servlet.hypermedia;
import java.net.URI;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import org.joda.time.DateTime;
import org.joda.time.format.ISODateTimeFormat;
import org.junit.Test;
import org.springframework.core.MethodParameter;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.format.annotation.DateTimeFormat.ISO;
import org.springframework.http.HttpEntity;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.hypermedia.MvcUriComponentsBuilderUnitTests.PersonControllerImpl;
import org.springframework.web.servlet.hypermedia.MvcUriComponentsBuilderUnitTests.PersonsAddressesController;
import org.springframework.web.util.UriComponentsBuilder;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
import static org.springframework.web.servlet.hypermedia.MvcUriComponentsBuilder.*;
/**
* Unit tests for {@link MvcUriComponentsBuilderFactory}.
*
* @author Ricardo Gladwell
* @author Oliver Gierke
*/
public class MvcUriComponentsBuilderFactoryUnitTests extends TestUtils {
List<UriComponentsContributor> contributors = Collections.emptyList();
MvcUriComponentsBuilderFactory factory = new MvcUriComponentsBuilderFactory(
contributors);
@Test
public void createsLinkToControllerRoot() {
URI link = factory.from(PersonControllerImpl.class).build().toUri();
assertPointsToMockServer(link);
assertThat(link.toString(), endsWith("/people"));
}
@Test
public void createsLinkToParameterizedControllerRoot() {
URI link = factory.from(PersonsAddressesController.class, 15).build().toUri();
assertPointsToMockServer(link);
assertThat(link.toString(), endsWith("/people/15/addresses"));
}
@Test
public void appliesParameterValueIfContributorConfigured() {
List<? extends UriComponentsContributor> contributors = Arrays.asList(new SampleUriComponentsContributor());
MvcUriComponentsBuilderFactory factory = new MvcUriComponentsBuilderFactory(
contributors);
SpecialType specialType = new SpecialType();
specialType.parameterValue = "value";
URI link = factory.from(
methodOn(SampleController.class).sampleMethod(1L, specialType)).build().toUri();
assertPointsToMockServer(link);
assertThat(link.toString(), endsWith("/sample/1?foo=value"));
}
/**
* @see #57
*/
@Test
public void usesDateTimeFormatForUriBinding() {
DateTime now = DateTime.now();
MvcUriComponentsBuilderFactory factory = new MvcUriComponentsBuilderFactory(
contributors);
URI link = factory.from(methodOn(SampleController.class).sampleMethod(now)).build().toUri();
assertThat(link.toString(),
endsWith("/sample/" + ISODateTimeFormat.date().print(now)));
}
static interface SampleController {
@RequestMapping("/sample/{id}")
HttpEntity<?> sampleMethod(@PathVariable("id") Long id, SpecialType parameter);
@RequestMapping("/sample/{time}")
HttpEntity<?> sampleMethod(
@PathVariable("time") @DateTimeFormat(iso = ISO.DATE) DateTime time);
}
static class SampleUriComponentsContributor implements UriComponentsContributor {
@Override
public boolean supportsParameter(MethodParameter parameter) {
return SpecialType.class.equals(parameter.getParameterType());
}
@Override
public void enhance(UriComponentsBuilder builder, MethodParameter parameter,
Object value) {
builder.queryParam("foo", ((SpecialType) value).parameterValue);
}
}
static class SpecialType {
String parameterValue;
}
}

View File

@@ -1,250 +0,0 @@
/*
* Copyright 2012-2013 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
*
* 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.springframework.web.servlet.hypermedia;
import java.net.URI;
import java.util.Arrays;
import java.util.List;
import org.hamcrest.Matchers;
import org.junit.Test;
import org.springframework.http.HttpEntity;
import org.springframework.util.MultiValueMap;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.util.UriComponents;
import org.springframework.web.util.UriComponentsBuilder;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
import static org.springframework.web.servlet.hypermedia.MvcUriComponentsBuilder.*;
/**
* Unit tests for {@link MvcUriComponentsBuilder}.
*
* @author Oliver Gierke
* @author Dietrich Schulten
*/
public class MvcUriComponentsBuilderUnitTests extends TestUtils {
@Test
public void createsLinkToControllerRoot() {
URI link = from(PersonControllerImpl.class).build().toUri();
assertThat(link.toString(), Matchers.endsWith("/people"));
}
@Test
public void createsLinkToParameterizedControllerRoot() {
URI link = from(PersonsAddressesController.class, 15).build().toUri();
assertThat(link.toString(), endsWith("/people/15/addresses"));
}
/**
* @see #70
*/
@Test
public void createsLinkToMethodOnParameterizedControllerRoot() {
URI link = from(
methodOn(PersonsAddressesController.class, 15).getAddressesForCountry(
"DE")).build().toUri();
assertThat(link.toString(), endsWith("/people/15/addresses/DE"));
}
@Test
public void createsLinkToSubResource() {
URI link = from(PersonControllerImpl.class).pathSegment("something").build().toUri();
assertThat(link.toString(), endsWith("/people/something"));
}
@Test(expected = IllegalStateException.class)
public void rejectsControllerWithMultipleMappings() {
from(InvalidController.class);
}
@Test
public void createsLinkToUnmappedController() {
URI link = from(UnmappedController.class).build().toUri();
assertThat(link.toString(), is("http://localhost/"));
}
@Test
public void appendingNullIsANoOp() {
URI link = from(PersonControllerImpl.class).path(null).build().toUri();
assertThat(link.toString(), endsWith("/people"));
}
@Test
public void linksToMethod() {
URI link = from(methodOn(ControllerWithMethods.class).myMethod(null)).build().toUri();
assertPointsToMockServer(link);
assertThat(link.toString(), endsWith("/something/else"));
}
@Test
public void linksToMethodWithPathVariable() {
URI link = from(methodOn(ControllerWithMethods.class).methodWithPathVariable("1")).build().toUri();
assertPointsToMockServer(link);
assertThat(link.toString(), endsWith("/something/1/foo"));
}
/**
* @see #33
*/
@Test
public void usesForwardedHostAsHostIfHeaderIsSet() {
request.addHeader("X-Forwarded-Host", "somethingDifferent");
URI link = from(PersonControllerImpl.class).build().toUri();
assertThat(link.toString(), startsWith("http://somethingDifferent"));
}
/**
* @see #26, #39
*/
@Test
public void linksToMethodWithPathVariableAndRequestParams() {
URI link = from(
methodOn(ControllerWithMethods.class).methodForNextPage("1", 10, 5)).build().toUri();
UriComponents components = toComponents(link);
assertThat(components.getPath(), is("/something/1/foo"));
MultiValueMap<String, String> queryParams = components.getQueryParams();
assertThat(queryParams.get("limit"), contains("5"));
assertThat(queryParams.get("offset"), contains("10"));
}
/**
* @see #26, #39
*/
@Test
public void linksToMethodWithPathVariableAndMultiValueRequestParams() {
URI link = from(
methodOn(ControllerWithMethods.class).methodWithMultiValueRequestParams(
"1", Arrays.asList(3, 7), 5)).build().toUri();
UriComponents components = toComponents(link);
assertThat(components.getPath(), is("/something/1/foo"));
MultiValueMap<String, String> queryParams = components.getQueryParams();
assertThat(queryParams.get("limit"), contains("5"));
assertThat(queryParams.get("items"), containsInAnyOrder("3", "7"));
}
/**
* @see #90
*/
@Test
public void usesForwardedHostAndPortFromHeader() {
request.addHeader("X-Forwarded-Host", "foobar:8088");
URI link = from(PersonControllerImpl.class).build().toUri();
assertThat(link.toString(), startsWith("http://foobar:8088"));
}
/**
* @see #90
*/
@Test
public void usesFirstHostOfXForwardedHost() {
request.addHeader("X-Forwarded-Host", "barfoo:8888, localhost:8088");
URI link = from(PersonControllerImpl.class).build().toUri();
assertThat(link.toString(), startsWith("http://barfoo:8888"));
}
private static UriComponents toComponents(URI link) {
return UriComponentsBuilder.fromUri(link).build();
}
static class Person {
Long id;
public Long getId() {
return id;
}
}
@RequestMapping("/people")
interface PersonController {
}
class PersonControllerImpl implements PersonController {
}
@RequestMapping("/people/{id}/addresses")
static class PersonsAddressesController {
@RequestMapping("/{country}")
public HttpEntity<Void> getAddressesForCountry(@PathVariable String country) {
return null;
}
}
@RequestMapping({ "/persons", "/people" })
class InvalidController {
}
class UnmappedController {
}
@RequestMapping("/something")
static class ControllerWithMethods {
@RequestMapping("/else")
HttpEntity<Void> myMethod(@RequestBody Object payload) {
return null;
}
@RequestMapping("/{id}/foo")
HttpEntity<Void> methodWithPathVariable(@PathVariable String id) {
return null;
}
@RequestMapping(value = "/{id}/foo")
HttpEntity<Void> methodForNextPage(@PathVariable String id,
@RequestParam Integer offset, @RequestParam Integer limit) {
return null;
}
@RequestMapping(value = "/{id}/foo")
HttpEntity<Void> methodWithMultiValueRequestParams(@PathVariable String id,
@RequestParam List<Integer> items, @RequestParam Integer limit) {
return null;
}
}
}

View File

@@ -1,47 +0,0 @@
/*
* Copyright 2012 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
*
* 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.springframework.web.servlet.hypermedia;
import org.junit.Test;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
/**
* @author Oliver Gierke
*/
public class RecordedInvocationUtilsUnitTests extends TestUtils {
@Test
public void test() {
MvcUriComponentsBuilder.from(RecordedInvocationUtils.methodOn(SampleController.class).someMethod(
1L));
}
@RequestMapping("/sample")
static class SampleController {
@RequestMapping("/{id}/foo")
HttpEntity<Void> someMethod(@PathVariable("id") Long id) {
return new ResponseEntity<Void>(HttpStatus.OK);
}
}
}

View File

@@ -1,49 +0,0 @@
/*
* Copyright 2012-2013 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
*
* 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.springframework.web.servlet.hypermedia;
import java.net.URI;
import org.junit.Before;
import org.springframework.mock.web.test.MockHttpServletRequest;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
/**
* Utility class to ease tesing.
*
* @author Oliver Gierke
*/
public class TestUtils {
protected MockHttpServletRequest request;
@Before
public void setUp() {
request = new MockHttpServletRequest();
ServletRequestAttributes requestAttributes = new ServletRequestAttributes(request);
RequestContextHolder.setRequestAttributes(requestAttributes);
}
protected void assertPointsToMockServer(URI link) {
assertThat(link.toString(), startsWith("http://localhost"));
}
}

View File

@@ -0,0 +1,290 @@
/*
* Copyright 2012-2013 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
*
* 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.springframework.web.servlet.mvc.support;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.hamcrest.Matchers;
import org.joda.time.DateTime;
import org.joda.time.format.ISODateTimeFormat;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.format.annotation.DateTimeFormat.ISO;
import org.springframework.http.HttpEntity;
import org.springframework.mock.web.test.MockHttpServletRequest;
import org.springframework.util.MultiValueMap;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.method.annotation.RequestParamMethodArgumentResolver;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.servlet.mvc.method.annotation.PathVariableMethodArgumentResolver;
import org.springframework.web.util.UriComponents;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
import static org.springframework.web.servlet.mvc.support.MvcUrlUtils.*;
/**
* Unit tests for {@link DefaultMvcUrls}.
*
* @author Oliver Gierke
* @author Dietrich Schulten
* @author Rossen Stoyanchev
*/
public class DefaultMvcUrlsTests {
private MockHttpServletRequest request;
private MvcUrls mvcUrls;
@Before
public void setUp() {
this.request = new MockHttpServletRequest();
ServletRequestAttributes requestAttributes = new ServletRequestAttributes(request);
RequestContextHolder.setRequestAttributes(requestAttributes);
List<HandlerMethodArgumentResolver> resolvers = new ArrayList<>();
resolvers.add(new PathVariableMethodArgumentResolver());
resolvers.add(new RequestParamMethodArgumentResolver(null, false));
this.mvcUrls = new DefaultMvcUrls(resolvers, null);
}
@After
public void teardown() {
RequestContextHolder.resetRequestAttributes();
}
@Test
public void linkToControllerRoot() {
UriComponents uriComponents = this.mvcUrls.linkToController(PersonControllerImpl.class).build();
assertThat(uriComponents.toUriString(), Matchers.endsWith("/people"));
}
@Test
public void linkToParameterizedControllerRoot() {
UriComponents uriComponents = this.mvcUrls.linkToController(
PersonsAddressesController.class).buildAndExpand(15);
assertThat(uriComponents.toUriString(), endsWith("/people/15/addresses"));
}
@Test
public void linkToMethodOnParameterizedControllerRoot() {
UriComponents uriComponents = this.mvcUrls.linkToMethodOn(
controller(PersonsAddressesController.class, 15).getAddressesForCountry("DE"));
assertThat(uriComponents.toUriString(), endsWith("/people/15/addresses/DE"));
}
@Test
public void linkToSubResource() {
UriComponents uriComponents =
this.mvcUrls.linkToController(PersonControllerImpl.class).pathSegment("something").build();
assertThat(uriComponents.toUriString(), endsWith("/people/something"));
}
@Test
public void linkToControllerWithMultipleMappings() {
UriComponents uriComponents = this.mvcUrls.linkToController(InvalidController.class).build();
assertThat(uriComponents.toUriString(), is("http://localhost/persons"));
}
@Test
public void linkToControllerNotMapped() {
UriComponents uriComponents = this.mvcUrls.linkToController(UnmappedController.class).build();
assertThat(uriComponents.toUriString(), is("http://localhost/"));
}
@Test
public void linkToMethodRefWithPathVar() throws Exception {
Method method = ControllerWithMethods.class.getDeclaredMethod("methodWithPathVariable", String.class);
UriComponents uriComponents = this.mvcUrls.linkToMethod(method, new Object[] { "1" });
assertThat(uriComponents.toUriString(), is("http://localhost/something/1/foo"));
}
@Test
public void linkToMethodRefWithTwoPathVars() throws Exception {
DateTime now = DateTime.now();
Method method = ControllerWithMethods.class.getDeclaredMethod(
"methodWithTwoPathVariables", Integer.class, DateTime.class);
UriComponents uriComponents = this.mvcUrls.linkToMethod(method, new Object[] { 1, now });
assertThat(uriComponents.getPath(), is("/something/1/foo/" + ISODateTimeFormat.date().print(now)));
}
@Test
public void linkToMethodRefWithPathVarAndRequestParam() throws Exception {
Method method = ControllerWithMethods.class.getDeclaredMethod("methodForNextPage", String.class, Integer.class, Integer.class);
UriComponents uriComponents = this.mvcUrls.linkToMethod(method, new Object[] {"1", 10, 5});
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 linkToMethod() {
UriComponents uriComponents = this.mvcUrls.linkToMethodOn(
controller(ControllerWithMethods.class).myMethod(null));
assertThat(uriComponents.toUriString(), startsWith("http://localhost"));
assertThat(uriComponents.toUriString(), endsWith("/something/else"));
}
@Test
public void linkToMethodWithPathVar() {
UriComponents uriComponents = this.mvcUrls.linkToMethodOn(
controller(ControllerWithMethods.class).methodWithPathVariable("1"));
assertThat(uriComponents.toUriString(), startsWith("http://localhost"));
assertThat(uriComponents.toUriString(), endsWith("/something/1/foo"));
}
@Test
public void linkToMethodWithPathVarAndRequestParams() {
UriComponents uriComponents = this.mvcUrls.linkToMethodOn(
controller(ControllerWithMethods.class).methodForNextPage("1", 10, 5));
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 linkToMethodWithPathVarAndMultiValueRequestParams() {
UriComponents uriComponents = this.mvcUrls.linkToMethodOn(
controller(ControllerWithMethods.class).methodWithMultiValueRequestParams(
"1", Arrays.asList(3, 7), 5));
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 usesForwardedHostAsHostIfHeaderIsSet() {
this.request.addHeader("X-Forwarded-Host", "somethingDifferent");
UriComponents uriComponents = this.mvcUrls.linkToController(PersonControllerImpl.class).build();
assertThat(uriComponents.toUriString(), startsWith("http://somethingDifferent"));
}
@Test
public void usesForwardedHostAndPortFromHeader() {
request.addHeader("X-Forwarded-Host", "foobar:8088");
UriComponents uriComponents = this.mvcUrls.linkToController(PersonControllerImpl.class).build();
assertThat(uriComponents.toUriString(), startsWith("http://foobar:8088"));
}
@Test
public void usesFirstHostOfXForwardedHost() {
request.addHeader("X-Forwarded-Host", "barfoo:8888, localhost:8088");
UriComponents uriComponents = this.mvcUrls.linkToController(PersonControllerImpl.class).build();
assertThat(uriComponents.toUriString(), startsWith("http://barfoo:8888"));
}
static class Person {
Long id;
public Long getId() {
return id;
}
}
@RequestMapping("/people")
interface PersonController {
}
class PersonControllerImpl implements PersonController {
}
@RequestMapping("/people/{id}/addresses")
static class PersonsAddressesController {
@RequestMapping("/{country}")
public HttpEntity<Void> getAddressesForCountry(@PathVariable String country) {
return null;
}
}
@RequestMapping({ "/persons", "/people" })
class InvalidController {
}
class UnmappedController {
}
@RequestMapping("/something")
static class ControllerWithMethods {
@RequestMapping("/else")
HttpEntity<Void> myMethod(@RequestBody Object payload) {
return null;
}
@RequestMapping("/{id}/foo")
HttpEntity<Void> methodWithPathVariable(@PathVariable String id) {
return null;
}
@RequestMapping("/{id}/foo/{date}")
HttpEntity<Void> methodWithTwoPathVariables(
@PathVariable Integer id, @DateTimeFormat(iso = ISO.DATE) @PathVariable DateTime date) {
return null;
}
@RequestMapping(value = "/{id}/foo")
HttpEntity<Void> methodForNextPage(@PathVariable String id,
@RequestParam Integer offset, @RequestParam Integer limit) {
return null;
}
@RequestMapping(value = "/{id}/foo")
HttpEntity<Void> methodWithMultiValueRequestParams(@PathVariable String id,
@RequestParam List<Integer> items, @RequestParam Integer limit) {
return null;
}
}
}

View File

@@ -0,0 +1,122 @@
/*
* Copyright 2012-2013 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
*
* 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.springframework.web.servlet.mvc.support;
import java.lang.reflect.Method;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.mock.web.test.MockHttpServletRequest;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.servlet.mvc.support.MvcUrlUtils.ControllerMethodValues;
import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;
/**
* Test fixture for {@link MvcUrlUtils}.
*
* @author Oliver Gierke
* @author Rossen Stoyanchev
*/
public class MvcUrlUtilsTests {
private MockHttpServletRequest request;
@Before
public void setUp() {
this.request = new MockHttpServletRequest();
ServletRequestAttributes requestAttributes = new ServletRequestAttributes(request);
RequestContextHolder.setRequestAttributes(requestAttributes);
}
@After
public void teardown() {
RequestContextHolder.resetRequestAttributes();
}
@Test
public void methodOn() {
HttpEntity<Void> result = MvcUrlUtils.controller(SampleController.class).someMethod(1L);
assertTrue(result instanceof ControllerMethodValues);
assertEquals("someMethod", ((ControllerMethodValues) result).getControllerMethod().getName());
}
@Test
public void typeLevelMapping() {
assertThat(MvcUrlUtils.getTypeLevelMapping(MyController.class), is("/type"));
}
@Test
public void typeLevelMappingNone() {
assertThat(MvcUrlUtils.getTypeLevelMapping(ControllerWithoutTypeLevelMapping.class), is("/"));
}
@Test
public void methodLevelMapping() throws Exception {
Method method = MyController.class.getMethod("method");
assertThat(MvcUrlUtils.getMethodMapping(method), is("/type/method"));
}
@Test
public void methodLevelMappingWithoutTypeLevelMapping() throws Exception {
Method method = ControllerWithoutTypeLevelMapping.class.getMethod("method");
assertThat(MvcUrlUtils.getMethodMapping(method), is("/method"));
}
@Test
public void methodMappingWithControllerMappingOnly() throws Exception {
Method method = MyController.class.getMethod("noMethodMapping");
assertThat(MvcUrlUtils.getMethodMapping(method), is("/type"));
}
@RequestMapping("/sample")
static class SampleController {
@RequestMapping("/{id}/foo")
HttpEntity<Void> someMethod(@PathVariable("id") Long id) {
return new ResponseEntity<Void>(HttpStatus.OK);
}
}
@RequestMapping("/type")
interface MyController {
@RequestMapping("/method")
void method();
@RequestMapping
void noMethodMapping();
}
interface ControllerWithoutTypeLevelMapping {
@RequestMapping("/method")
void method();
}
}