Browse Source

Update contribution

Closes gh-33220
pull/33297/head
rstoyanchev 2 years ago
parent
commit
f967f6f9f0
  1. 4
      spring-web/src/main/java/org/springframework/web/service/invoker/AbstractNamedValueArgumentResolver.java
  2. 78
      spring-web/src/main/java/org/springframework/web/service/invoker/RequestParamArgumentResolver.java
  3. 35
      spring-web/src/test/java/org/springframework/web/service/invoker/RequestParamArgumentResolverTests.java

4
spring-web/src/main/java/org/springframework/web/service/invoker/AbstractNamedValueArgumentResolver.java

@ -129,7 +129,7 @@ public abstract class AbstractNamedValueArgumentResolver implements HttpServiceA @@ -129,7 +129,7 @@ public abstract class AbstractNamedValueArgumentResolver implements HttpServiceA
*/
@Nullable
protected NamedValueInfo createNamedValueInfo(
MethodParameter parameter, HttpRequestValues.Metadata requestValues) {
MethodParameter parameter, HttpRequestValues.Metadata metadata) {
return createNamedValueInfo(parameter);
}
@ -246,6 +246,8 @@ public abstract class AbstractNamedValueArgumentResolver implements HttpServiceA @@ -246,6 +246,8 @@ public abstract class AbstractNamedValueArgumentResolver implements HttpServiceA
* @param required whether it is marked as required
* @param defaultValue fallback value, possibly {@link ValueConstants#DEFAULT_NONE}
* @param label how it should appear in error messages, e.g. "path variable", "request header"
* @param multiValued whether this argument resolver supports sending multiple values;
* if not, then multiple values are formatted as a String value
*/
public NamedValueInfo(
String name, boolean required, @Nullable String defaultValue, String label, boolean multiValued) {

78
spring-web/src/main/java/org/springframework/web/service/invoker/RequestParamArgumentResolver.java

@ -55,61 +55,79 @@ import org.springframework.web.bind.annotation.RequestParam; @@ -55,61 +55,79 @@ import org.springframework.web.bind.annotation.RequestParam;
*/
public class RequestParamArgumentResolver extends AbstractNamedValueArgumentResolver {
private boolean formatAsSingleValue = true;
private boolean favorSingleValue;
public RequestParamArgumentResolver(ConversionService conversionService) {
super(conversionService);
}
public RequestParamArgumentResolver(ConversionService conversionService, boolean formatAsSingleValue) {
super(conversionService);
this.formatAsSingleValue = formatAsSingleValue;
}
@Override
@Nullable
protected NamedValueInfo createNamedValueInfo(MethodParameter parameter, HttpRequestValues.Metadata requestValues) {
MediaType contentType = requestValues.getContentType();
if (contentType != null && isMultiValueFormContentType(contentType)) {
this.formatAsSingleValue = true;
}
/**
* Whether to format multiple values (e.g. collection, array) as a single
* String value through the configured {@link ConversionService} unless the
* content type is form data, or it is a multipart request.
* <p>By default, this is {@code false} in which case formatting is not applied,
* and a separate parameter with the same name is created for each value.
* @since 6.2
*/
public void setFavorSingleValue(boolean favorSingleValue) {
this.favorSingleValue = favorSingleValue;
}
return createNamedValueInfo(parameter);
/**
* Return the setting for {@link #setFavorSingleValue favorSingleValue}.
* @since 6.2
*/
public boolean isFavorSingleValue() {
return this.favorSingleValue;
}
@Override
@Nullable
protected NamedValueInfo createNamedValueInfo(MethodParameter parameter) {
protected NamedValueInfo createNamedValueInfo(MethodParameter parameter, HttpRequestValues.Metadata metadata) {
RequestParam annot = parameter.getParameterAnnotation(RequestParam.class);
if (annot == null) {
return null;
}
return (annot == null ? null :
new NamedValueInfo(annot.name(), annot.required(), annot.defaultValue(),
"request parameter", this.formatAsSingleValue));
return new NamedValueInfo(
annot.name(), annot.required(), annot.defaultValue(), "request parameter",
supportsMultipleValues(parameter, metadata));
}
@Override
protected void addRequestValue(
String name, Object value, MethodParameter parameter, HttpRequestValues.Builder requestValues) {
requestValues.addRequestParameter(name, (String) value);
protected NamedValueInfo createNamedValueInfo(MethodParameter parameter) {
// Shouldn't be called since we override createNamedValueInfo with HttpRequestValues.Metadata
throw new UnsupportedOperationException();
}
protected boolean isFormatAsSingleValue() {
return this.formatAsSingleValue;
/**
* Determine whether the resolver should send multi-value request parameters
* as individual values. If not, they are formatted to a single String value.
* The default implementation uses {@link #isFavorSingleValue()} to decide
* unless the content type is form data, or it is a multipart request.
* @since 6.2
*/
protected boolean supportsMultipleValues(MethodParameter parameter, HttpRequestValues.Metadata metadata) {
return (!isFavorSingleValue() || isFormOrMultipartContent(metadata));
}
protected void setFormatAsSingleValue(boolean formatAsSingleValue) {
this.formatAsSingleValue = formatAsSingleValue;
/**
* Whether the content type is form data, or it is a multipart request.
* @since 6.2
*/
protected boolean isFormOrMultipartContent(HttpRequestValues.Metadata metadata) {
MediaType mediaType = metadata.getContentType();
return (mediaType != null && (mediaType.getType().equals("multipart") ||
mediaType.isCompatibleWith(MediaType.APPLICATION_FORM_URLENCODED)));
}
protected boolean isMultiValueFormContentType(MediaType contentType) {
return contentType.equals(MediaType.APPLICATION_FORM_URLENCODED)
|| contentType.getType().equals("multipart");
@Override
protected void addRequestValue(
String name, Object value, MethodParameter parameter, HttpRequestValues.Builder requestValues) {
requestValues.addRequestParameter(name, (String) value);
}
}

35
spring-web/src/test/java/org/springframework/web/service/invoker/RequestParamArgumentResolverTests.java

@ -16,17 +16,18 @@ @@ -16,17 +16,18 @@
package org.springframework.web.service.invoker;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.junit.jupiter.api.Test;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.support.DefaultConversionService;
import org.springframework.util.MultiValueMap;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.service.annotation.GetExchange;
import org.springframework.web.service.annotation.PostExchange;
import org.springframework.web.util.UriComponents;
import org.springframework.web.util.UriComponentsBuilder;
import static org.assertj.core.api.Assertions.assertThat;
@ -62,24 +63,18 @@ class RequestParamArgumentResolverTests { @@ -62,24 +63,18 @@ class RequestParamArgumentResolverTests {
}
@Test
@SuppressWarnings("unchecked")
void requestParamWithDisabledFormattingCollectionValue() {
ConversionService conversionService = new DefaultConversionService();
boolean formatAsSingleValue = false;
Service service = builder.customArgumentResolver(
new RequestParamArgumentResolver(conversionService, formatAsSingleValue))
.build()
.createClient(Service.class);
List<String> collectionParams = List.of("1", "2", "3");
service.getForm("value 1", collectionParams);
Object uriVariables = this.client.getRequestValues().getUriVariables();
assertThat(uriVariables).isNotInstanceOf(MultiValueMap.class).isInstanceOf(HashMap.class);
assertThat((HashMap<String, String>) uriVariables).hasSize(4)
.containsEntry("queryParam0", "param1")
.containsEntry("queryParam0[0]", "value 1")
.containsEntry("queryParam1", "param2")
.containsEntry("queryParam1[0]", String.join(",", collectionParams));
RequestParamArgumentResolver resolver = new RequestParamArgumentResolver(new DefaultConversionService());
resolver.setFavorSingleValue(true);
Service service = builder.customArgumentResolver(resolver).build().createClient(Service.class);
service.getWithParams("value 1", List.of("1", "2", "3"));
HttpRequestValues values = this.client.getRequestValues();
String uriTemplate = values.getUriTemplate();
Map<String, String> uriVariables = values.getUriVariables();
UriComponents uri = UriComponentsBuilder.fromUriString(uriTemplate).buildAndExpand(uriVariables).encode();
assertThat(uri.getQuery()).isEqualTo("param1=value%201&param2=1,2,3");
}
private interface Service {
@ -88,7 +83,7 @@ class RequestParamArgumentResolverTests { @@ -88,7 +83,7 @@ class RequestParamArgumentResolverTests {
void postForm(@RequestParam String param1, @RequestParam String param2);
@GetExchange
void getForm(@RequestParam String param1, @RequestParam List<String> param2);
void getWithParams(@RequestParam String param1, @RequestParam List<String> param2);
}
}

Loading…
Cancel
Save