#467 - AnnotationMappingDiscoverer now considers @RequestMapping(params = …)

We now inspect the static request parameters declared in the actual request mapping and apply those values when building URIs pointing to those methods.

Original pull request: #1576.
This commit is contained in:
Réda Housni Alaoui
2021-07-12 18:36:48 +02:00
committed by Oliver Drotbohm
parent 8c4824245e
commit 445d5fd4d8
7 changed files with 60 additions and 2 deletions

View File

@@ -15,6 +15,7 @@
*/
package org.springframework.hateoas.server.core;
import static java.util.Optional.ofNullable;
import static org.springframework.core.annotation.AnnotatedElementUtils.*;
import static org.springframework.core.annotation.AnnotationUtils.*;
@@ -40,6 +41,7 @@ import org.springframework.web.bind.annotation.RequestMethod;
* @author Oliver Gierke
* @author Mark Paluch
* @author Greg Turnquist
* @author Réda Housni Alaoui
* @deprecated since 1.2, not for removal but for hiding within the package in 1.3
*/
@Deprecated
@@ -167,6 +169,14 @@ public class AnnotationMappingDiscoverer implements MappingDiscoverer {
: Arrays.stream(mediaTypes).map(MediaType::parseMediaType).collect(Collectors.toList());
}
@Override
public String[] getParams(Method method) {
Annotation annotation = findMergedAnnotation(method, annotationType);
String[] params = (String[]) getValue(annotation, "params");
return ofNullable(params).orElseGet(() -> new String[0]);
}
private String[] getMappingFrom(@Nullable Annotation annotation) {
if (annotation == null) {

View File

@@ -31,6 +31,7 @@ import org.springframework.util.StringUtils;
*
* @author Michal Stochmialek
* @author Oliver Drotbohm
* @author Réda Housni Alaoui
*/
public class CachingMappingDiscoverer implements MappingDiscoverer {
@@ -104,6 +105,11 @@ public class CachingMappingDiscoverer implements MappingDiscoverer {
return delegate.getConsumes(method);
}
@Override
public String[] getParams(Method method) {
return delegate.getParams(method);
}
private static String key(Class<?> type, @Nullable Method method) {
StringBuilder builder = new StringBuilder(type.getName());

View File

@@ -29,6 +29,7 @@ import org.springframework.lang.Nullable;
*
* @author Oliver Gierke
* @author Greg Turnquist
* @author Réda Housni Alaoui
*/
public interface MappingDiscoverer {
@@ -79,4 +80,10 @@ public interface MappingDiscoverer {
* @since 1.3
*/
List<MediaType> getConsumes(Method method);
/**
* @param method - must not be null.
* @return the parameters of the mapped request, narrowing the primary mapping.
*/
String[] getParams(Method method);
}

View File

@@ -31,6 +31,7 @@ import org.springframework.web.context.WebApplicationContext;
*
* @author Lars Michele
* @author Oliver Drotbohm
* @author Réda Housni Alaoui
*/
class PropertyResolvingMappingDiscoverer implements MappingDiscoverer {
@@ -91,6 +92,11 @@ class PropertyResolvingMappingDiscoverer implements MappingDiscoverer {
return delegate.getConsumes(method);
}
@Override
public String[] getParams(Method method) {
return delegate.getParams(method);
}
@Nullable
private static String resolveProperties(@Nullable String mapping) {

View File

@@ -24,9 +24,7 @@ import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.function.Supplier;
import javax.servlet.ServletContext;
import org.springframework.core.MethodParameter;
import org.springframework.core.convert.ConversionService;
import org.springframework.format.support.DefaultFormattingConversionService;
@@ -34,12 +32,15 @@ import org.springframework.hateoas.Link;
import org.springframework.hateoas.server.MethodLinkBuilderFactory;
import org.springframework.hateoas.server.core.LinkBuilderSupport;
import org.springframework.hateoas.server.core.MethodParameters;
import org.springframework.hateoas.server.core.SpringAffordanceBuilder;
import org.springframework.hateoas.server.core.WebHandler;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.context.support.WebApplicationContextUtils;
import org.springframework.web.servlet.mvc.condition.NameValueExpression;
import org.springframework.web.servlet.mvc.condition.ParamsRequestCondition;
import org.springframework.web.util.UriComponentsBuilder;
/**
@@ -54,6 +55,7 @@ import org.springframework.web.util.UriComponentsBuilder;
* @author Kevin Conaway
* @author Andrew Naydyonock
* @author Greg Turnquist
* @author Réda Housni Alaoui
*/
public class WebMvcLinkBuilderFactory implements MethodLinkBuilderFactory<WebMvcLinkBuilder> {
@@ -120,6 +122,19 @@ public class WebMvcLinkBuilderFactory implements MethodLinkBuilderFactory<WebMvc
return WebHandler.linkTo(invocationValue, WebMvcLinkBuilder::new, (builder, invocation) -> {
String[] primaryParams = SpringAffordanceBuilder.DISCOVERER.getParams(invocation.getMethod());
ParamsRequestCondition paramsRequestCondition = new ParamsRequestCondition(primaryParams);
for (NameValueExpression<String> expression: paramsRequestCondition.getExpressions()) {
if (expression.isNegated()) {
continue;
}
String value = expression.getValue();
if (value == null){
continue;
}
builder.queryParam(expression.getName(), value);
}
MethodParameters parameters = MethodParameters.of(invocation.getMethod());
Iterator<Object> parameterValues = Arrays.asList(invocation.getArguments()).iterator();

View File

@@ -50,6 +50,7 @@ import org.springframework.web.util.UriComponentsBuilder;
* @author Oliver Gierke
* @author Kamill Sokol
* @author Ross Turner
* @author Réda Housni Alaoui
*/
class WebMvcLinkBuilderFactoryUnitTest extends TestUtils {
@@ -113,6 +114,13 @@ class WebMvcLinkBuilderFactoryUnitTest extends TestUtils {
assertThat(link.getHref()).endsWith("/something/with%20blank/foo");
}
@Test
void linksToMethodWithPrimaryParam(){
Link link = linkTo(methodOn(ControllerWithMethods.class).methodWithPrimaryParams()).withSelfRel();
assertThat(link.getRel()).isEqualTo(IanaLinkRelations.SELF);
assertThat(link.getHref()).endsWith("/something/foo?a=1&b=2");
}
/**
* @see #96
*/

View File

@@ -58,6 +58,7 @@ import org.springframework.web.util.UriComponentsBuilder;
* @author Kevin Conaway
* @author Oliver Trosien
* @author Greg Turnquist
* @author Réda Housni Alaoui
*/
class WebMvcLinkBuilderUnitTest extends TestUtils {
@@ -666,6 +667,11 @@ class WebMvcLinkBuilderUnitTest extends TestUtils {
return null;
}
@RequestMapping(path = "/foo", params = {"a=1", "b=2", "c!=4", "!d"})
HttpEntity<Void> methodWithPrimaryParams(){
return null;
}
@RequestMapping(value = "/{id}/foo")
HttpEntity<Void> methodForNextPage(@PathVariable String id, @RequestParam(required = false) Integer offset,
@RequestParam Integer limit) {