Skip to content

Commit 1e13d40

Browse files
committed
Avoid pre-computing base URI in PagedResourceAssemblerArgumentResolver.
We now avoid the pre-computation of the base URI in PagedResourceAssemblerArgumentResolver as the actual assembler will fall back to using the URI of the current request by default anyway. The latter makes sure that request parameters, that are contained in the original requests appear in links created. If a controller wants to deviate from that behavior, they can create a dedicated link themselves and hand that to the assembler explicitly. Fixes GH-2173, GH-452.
1 parent 3d7cabf commit 1e13d40

5 files changed

+37
-67
lines changed

src/main/java/org/springframework/data/web/PagedResourcesAssemblerArgumentResolver.java

+14-31
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@
2626
import org.springframework.core.MethodParameter;
2727
import org.springframework.core.log.LogMessage;
2828
import org.springframework.data.domain.Pageable;
29-
import org.springframework.hateoas.Link;
3029
import org.springframework.hateoas.server.MethodLinkBuilderFactory;
3130
import org.springframework.hateoas.server.core.MethodParameters;
3231
import org.springframework.hateoas.server.mvc.WebMvcLinkBuilderFactory;
@@ -35,8 +34,6 @@
3534
import org.springframework.web.context.request.NativeWebRequest;
3635
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
3736
import org.springframework.web.method.support.ModelAndViewContainer;
38-
import org.springframework.web.util.UriComponents;
39-
import org.springframework.web.util.UriComponentsBuilder;
4037

4138
/**
4239
* {@link HandlerMethodArgumentResolver} to allow injection of {@link PagedResourcesAssembler} into Spring MVC
@@ -55,20 +52,30 @@ public class PagedResourcesAssemblerArgumentResolver implements HandlerMethodArg
5552
private static final String PARAMETER_AMBIGUITY = "Discovered multiple parameters of type Pageable but no qualifier annotations to disambiguate!";
5653

5754
private final HateoasPageableHandlerMethodArgumentResolver resolver;
58-
private final MethodLinkBuilderFactory<?> linkBuilderFactory;
5955

6056
/**
6157
* Creates a new {@link PagedResourcesAssemblerArgumentResolver} using the given
6258
* {@link PageableHandlerMethodArgumentResolver} and {@link MethodLinkBuilderFactory}.
6359
*
6460
* @param resolver can be {@literal null}.
6561
* @param linkBuilderFactory can be {@literal null}, will be defaulted to a {@link WebMvcLinkBuilderFactory}.
62+
* @deprecated since 2.5, 2.4.4, 2.3.7, to be removed in 3.0
63+
* @use {@link #PagedResourcesAssemblerArgumentResolver(HateoasPageableHandlerMethodArgumentResolver)} instead.
6664
*/
65+
@Deprecated
6766
public PagedResourcesAssemblerArgumentResolver(HateoasPageableHandlerMethodArgumentResolver resolver,
6867
@Nullable MethodLinkBuilderFactory<?> linkBuilderFactory) {
68+
this(resolver);
69+
}
6970

71+
/**
72+
* Creates a new {@link PagedResourcesAssemblerArgumentResolver} using the given
73+
* {@link PageableHandlerMethodArgumentResolver}.
74+
*
75+
* @param resolver can be {@literal null}.
76+
*/
77+
public PagedResourcesAssemblerArgumentResolver(HateoasPageableHandlerMethodArgumentResolver resolver) {
7078
this.resolver = resolver;
71-
this.linkBuilderFactory = linkBuilderFactory == null ? new WebMvcLinkBuilderFactory() : linkBuilderFactory;
7279
}
7380

7481
/*
@@ -89,36 +96,12 @@ public boolean supportsParameter(MethodParameter parameter) {
8996
public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
9097
NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) {
9198

92-
UriComponents fromUriString = resolveBaseUri(parameter);
9399
MethodParameter pageableParameter = findMatchingPageableParameter(parameter);
94100

95101
if (pageableParameter != null) {
96-
return new MethodParameterAwarePagedResourcesAssembler<>(pageableParameter, resolver, fromUriString);
102+
return new MethodParameterAwarePagedResourcesAssembler<>(pageableParameter, resolver, null);
97103
} else {
98-
return new PagedResourcesAssembler<>(resolver, fromUriString);
99-
}
100-
}
101-
102-
/**
103-
* Eagerly resolve a base URI for the given {@link MethodParameter} to be handed to the assembler.
104-
*
105-
* @param parameter must not be {@literal null}.
106-
* @return the {@link UriComponents} representing the base URI, or {@literal null} if it can't be resolved eagerly.
107-
*/
108-
@Nullable
109-
private UriComponents resolveBaseUri(MethodParameter parameter) {
110-
111-
Method method = parameter.getMethod();
112-
113-
if (method == null) {
114-
throw new IllegalArgumentException(String.format("Could not obtain method from parameter %s!", parameter));
115-
}
116-
117-
try {
118-
Link linkToMethod = linkBuilderFactory.linkTo(parameter.getDeclaringClass(), method).withSelfRel();
119-
return UriComponentsBuilder.fromUriString(linkToMethod.getHref()).build();
120-
} catch (IllegalArgumentException o_O) {
121-
return null;
104+
return new PagedResourcesAssembler<>(resolver, null);
122105
}
123106
}
124107

src/main/java/org/springframework/data/web/config/HateoasAwareSpringDataWebConfiguration.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ public PagedResourcesAssembler<?> pagedResourcesAssembler() {
9999

100100
@Bean
101101
public PagedResourcesAssemblerArgumentResolver pagedResourcesAssemblerArgumentResolver() {
102-
return new PagedResourcesAssemblerArgumentResolver(pageableResolver.get(), null);
102+
return new PagedResourcesAssemblerArgumentResolver(pageableResolver.get());
103103
}
104104

105105
/*

src/test/java/org/springframework/data/web/PagedResourcesAssemblerArgumentResolverUnitTests.java

+1-34
Original file line numberDiff line numberDiff line change
@@ -18,17 +18,13 @@
1818
import static org.assertj.core.api.Assertions.*;
1919

2020
import java.lang.reflect.Method;
21-
import java.util.Optional;
2221

2322
import org.junit.jupiter.api.BeforeEach;
2423
import org.junit.jupiter.api.Test;
25-
2624
import org.springframework.beans.factory.annotation.Qualifier;
2725
import org.springframework.core.MethodParameter;
2826
import org.springframework.data.domain.Pageable;
29-
import org.springframework.test.util.ReflectionTestUtils;
3027
import org.springframework.web.bind.annotation.RequestMapping;
31-
import org.springframework.web.util.UriComponents;
3228

3329
/**
3430
* Unit tests for {@link PagedResourcesAssemblerArgumentResolver}.
@@ -46,7 +42,7 @@ void setUp() {
4642
WebTestUtils.initWebTest();
4743

4844
HateoasPageableHandlerMethodArgumentResolver hateoasPageableHandlerMethodArgumentResolver = new HateoasPageableHandlerMethodArgumentResolver();
49-
this.resolver = new PagedResourcesAssemblerArgumentResolver(hateoasPageableHandlerMethodArgumentResolver, null);
45+
this.resolver = new PagedResourcesAssemblerArgumentResolver(hateoasPageableHandlerMethodArgumentResolver);
5046
}
5147

5248
@Test // DATACMNS-418
@@ -112,30 +108,6 @@ void doesNotFailForTemplatedMethodMapping() throws Exception {
112108
assertThat(result).isNotNull();
113109
}
114110

115-
@Test // DATACMNS-513
116-
void detectsMappingOfInvokedSubType() throws Exception {
117-
118-
Method method = Controller.class.getMethod("methodWithMapping", PagedResourcesAssembler.class);
119-
120-
// Simulate HandlerMethod.HandlerMethodParameter.getDeclaringClass()
121-
// as it's returning the invoked class as the declared one
122-
MethodParameter methodParameter = new MethodParameter(method, 0) {
123-
public java.lang.Class<?> getDeclaringClass() {
124-
return SubController.class;
125-
}
126-
};
127-
128-
Object result = resolver.resolveArgument(methodParameter, null, null, null);
129-
130-
assertThat(result).isInstanceOf(PagedResourcesAssembler.class);
131-
132-
Optional<UriComponents> uriComponents = (Optional<UriComponents>) ReflectionTestUtils.getField(result, "baseUri");
133-
134-
assertThat(uriComponents).hasValueSatisfying(it -> {
135-
assertThat(it.getPath()).isEqualTo("/foo/mapping");
136-
});
137-
}
138-
139111
private void assertSelectsParameter(Method method, int expectedIndex) {
140112

141113
MethodParameter parameter = new MethodParameter(method, 0);
@@ -190,9 +162,4 @@ void noMatchingQualifiers(@Qualifier("qualified") PagedResourcesAssembler<Object
190162
@RequestMapping("/mapping")
191163
Object methodWithMapping(PagedResourcesAssembler<Object> pageable);
192164
}
193-
194-
@RequestMapping("/foo")
195-
interface SubController extends Controller {
196-
197-
}
198165
}

src/test/java/org/springframework/data/web/PagedResourcesAssemblerUnitTests.java

+12
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
import org.springframework.hateoas.RepresentationModel;
3939
import org.springframework.hateoas.server.RepresentationModelAssembler;
4040
import org.springframework.hateoas.server.core.EmbeddedWrapper;
41+
import org.springframework.mock.web.MockHttpServletRequest;
4142
import org.springframework.web.util.UriComponents;
4243
import org.springframework.web.util.UriComponentsBuilder;
4344

@@ -257,6 +258,17 @@ void selfLinkContainsCoordinatesForCurrentPage() {
257258
assertThat(resource.getRequiredLink(IanaLinkRelations.SELF).getHref()).endsWith("?page=0&size=1");
258259
}
259260

261+
@Test // #2173
262+
void keepsRequestParametersOfOriginalRequestUri() {
263+
264+
WebTestUtils.initWebTest(new MockHttpServletRequest("GET", "/sample?foo=bar"));
265+
266+
PagedModel<EntityModel<Person>> model = assembler.toModel(createPage(1));
267+
268+
assertThat(model.getRequiredLink(IanaLinkRelations.FIRST).getHref())
269+
.isEqualTo("http://localhost/sample?foo=bar&page=0&size=1");
270+
}
271+
260272
private static Page<Person> createPage(int index) {
261273

262274
Pageable request = PageRequest.of(index, 1);

src/test/java/org/springframework/data/web/WebTestUtils.java

+9-1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
*/
1616
package org.springframework.data.web;
1717

18+
import javax.servlet.http.HttpServletRequest;
19+
1820
import org.springframework.mock.web.MockHttpServletRequest;
1921
import org.springframework.mock.web.MockServletContext;
2022
import org.springframework.web.context.WebApplicationContext;
@@ -33,8 +35,14 @@ public class WebTestUtils {
3335
* Initializes web tests. Will register a {@link MockHttpServletRequest} for the current thread.
3436
*/
3537
public static void initWebTest() {
38+
initWebTest(new MockHttpServletRequest());
39+
}
40+
41+
/**
42+
* Initializes web tests for the given {@link HttpServletRequest} which will registered for the current thread.
43+
*/
44+
public static void initWebTest(HttpServletRequest request) {
3645

37-
MockHttpServletRequest request = new MockHttpServletRequest();
3846
ServletRequestAttributes requestAttributes = new ServletRequestAttributes(request);
3947
RequestContextHolder.setRequestAttributes(requestAttributes);
4048
}

0 commit comments

Comments
 (0)