Add ReactiveHttpRequestValues
Separate collection and handling of reactive request values into a subclass of HttpRequestValues. Closes gh-30117
This commit is contained in:
@@ -27,10 +27,10 @@ import org.reactivestreams.Publisher;
|
||||
|
||||
import org.springframework.core.ParameterizedTypeReference;
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.http.HttpEntity;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.client.MultipartBodyBuilder;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
@@ -48,7 +48,7 @@ import org.springframework.web.util.UriUtils;
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 6.0
|
||||
*/
|
||||
public final class HttpRequestValues {
|
||||
public class HttpRequestValues {
|
||||
|
||||
private static final MultiValueMap<String, String> EMPTY_COOKIES_MAP =
|
||||
CollectionUtils.toMultiValueMap(Collections.emptyMap());
|
||||
@@ -74,18 +74,11 @@ public final class HttpRequestValues {
|
||||
@Nullable
|
||||
private final Object bodyValue;
|
||||
|
||||
@Nullable
|
||||
private final Publisher<?> body;
|
||||
|
||||
@Nullable
|
||||
private final ParameterizedTypeReference<?> bodyElementType;
|
||||
|
||||
|
||||
private HttpRequestValues(@Nullable HttpMethod httpMethod,
|
||||
protected HttpRequestValues(@Nullable HttpMethod httpMethod,
|
||||
@Nullable URI uri, @Nullable String uriTemplate, Map<String, String> uriVariables,
|
||||
HttpHeaders headers, MultiValueMap<String, String> cookies, Map<String, Object> attributes,
|
||||
@Nullable Object bodyValue,
|
||||
@Nullable Publisher<?> body, @Nullable ParameterizedTypeReference<?> bodyElementType) {
|
||||
@Nullable Object bodyValue) {
|
||||
|
||||
Assert.isTrue(uri != null || uriTemplate != null, "Neither URI nor URI template");
|
||||
|
||||
@@ -97,8 +90,6 @@ public final class HttpRequestValues {
|
||||
this.cookies = cookies;
|
||||
this.attributes = attributes;
|
||||
this.bodyValue = bodyValue;
|
||||
this.body = body;
|
||||
this.bodyElementType = bodyElementType;
|
||||
}
|
||||
|
||||
|
||||
@@ -161,8 +152,6 @@ public final class HttpRequestValues {
|
||||
|
||||
/**
|
||||
* Return the request body as a value to be serialized, if set.
|
||||
* <p>This is mutually exclusive with {@link #getBody()}.
|
||||
* Only one of the two or neither is set.
|
||||
*/
|
||||
@Nullable
|
||||
public Object getBodyValue() {
|
||||
@@ -173,18 +162,24 @@ public final class HttpRequestValues {
|
||||
* Return the request body as a Publisher.
|
||||
* <p>This is mutually exclusive with {@link #getBodyValue()}.
|
||||
* Only one of the two or neither is set.
|
||||
* @deprecated in favor of {@link ReactiveHttpRequestValues#getBodyPublisher()};
|
||||
* to be removed in 6.2
|
||||
*/
|
||||
@Deprecated(since = "6.1", forRemoval = true)
|
||||
@Nullable
|
||||
public Publisher<?> getBody() {
|
||||
return this.body;
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the element type for a {@linkplain #getBody() Publisher body}.
|
||||
* Return the element type for a Publisher body.
|
||||
* @deprecated in favor of {@link ReactiveHttpRequestValues#getBodyPublisherElementType()};
|
||||
* to be removed in 6.2
|
||||
*/
|
||||
@Deprecated(since = "6.1", forRemoval = true)
|
||||
@Nullable
|
||||
public ParameterizedTypeReference<?> getBodyElementType() {
|
||||
return this.bodyElementType;
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
|
||||
@@ -196,7 +191,7 @@ public final class HttpRequestValues {
|
||||
/**
|
||||
* Builder for {@link HttpRequestValues}.
|
||||
*/
|
||||
public final static class Builder {
|
||||
public static class Builder {
|
||||
|
||||
@Nullable
|
||||
private HttpMethod httpMethod;
|
||||
@@ -220,7 +215,7 @@ public final class HttpRequestValues {
|
||||
private MultiValueMap<String, String> requestParams;
|
||||
|
||||
@Nullable
|
||||
private MultipartBodyBuilder multipartBuilder;
|
||||
private MultiValueMap<String, Object> parts;
|
||||
|
||||
@Nullable
|
||||
private Map<String, Object> attributes;
|
||||
@@ -228,12 +223,6 @@ public final class HttpRequestValues {
|
||||
@Nullable
|
||||
private Object bodyValue;
|
||||
|
||||
@Nullable
|
||||
private Publisher<?> body;
|
||||
|
||||
@Nullable
|
||||
private ParameterizedTypeReference<?> bodyElementType;
|
||||
|
||||
/**
|
||||
* Set the HTTP method for the request.
|
||||
*/
|
||||
@@ -327,23 +316,30 @@ public final class HttpRequestValues {
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a part to a multipart request. The part value may be as described
|
||||
* in {@link MultipartBodyBuilder#part(String, Object)}.
|
||||
* Add a part for a multipart request. The part may be:
|
||||
* <ul>
|
||||
* <li>String -- form field
|
||||
* <li>{@link org.springframework.core.io.Resource Resource} -- file part
|
||||
* <li>Object -- content to be encoded (e.g. to JSON)
|
||||
* <li>{@link HttpEntity} -- part content and headers although generally it's
|
||||
* easier to add headers through the returned builder
|
||||
* </ul>
|
||||
*/
|
||||
public Builder addRequestPart(String name, Object part) {
|
||||
this.multipartBuilder = (this.multipartBuilder != null ? this.multipartBuilder : new MultipartBodyBuilder());
|
||||
this.multipartBuilder.part(name, part);
|
||||
this.parts = (this.parts != null ? this.parts : new LinkedMultiValueMap<>());
|
||||
this.parts.add(name, part);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Variant of {@link #addRequestPart(String, Object)} that allows the
|
||||
* part value to be produced by a {@link Publisher}.
|
||||
* @deprecated in favor of {@link ReactiveHttpRequestValues.Builder#addRequestPartPublisher};
|
||||
* to be removed in 6.2
|
||||
*/
|
||||
@Deprecated(since = "6.1", forRemoval = true)
|
||||
public <T, P extends Publisher<T>> Builder addRequestPart(String name, P publisher, ResolvableType type) {
|
||||
this.multipartBuilder = (this.multipartBuilder != null ? this.multipartBuilder : new MultipartBodyBuilder());
|
||||
this.multipartBuilder.asyncPart(name, publisher, ParameterizedTypeReference.forType(type.getType()));
|
||||
return this;
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -358,25 +354,22 @@ public final class HttpRequestValues {
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the request body as a concrete value to be serialized.
|
||||
* <p>This is mutually exclusive with, and resets any previously set
|
||||
* {@linkplain #setBody(Publisher, ParameterizedTypeReference) body Publisher}.
|
||||
* Set the request body as an Object to be serialized.
|
||||
*/
|
||||
public void setBodyValue(Object bodyValue) {
|
||||
this.bodyValue = bodyValue;
|
||||
this.body = null;
|
||||
this.bodyElementType = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the request body as a concrete value to be serialized.
|
||||
* Set the request body as a Reactive Streams Publisher.
|
||||
* <p>This is mutually exclusive with, and resets any previously set
|
||||
* {@linkplain #setBodyValue(Object) body value}.
|
||||
* @deprecated in favor of {@link ReactiveHttpRequestValues.Builder#setBodyPublisher};
|
||||
* to be removed in 6.2
|
||||
*/
|
||||
@Deprecated(since = "6.1", forRemoval = true)
|
||||
public <T, P extends Publisher<T>> void setBody(P body, ParameterizedTypeReference<T> elementTye) {
|
||||
this.body = body;
|
||||
this.bodyElementType = elementTye;
|
||||
this.bodyValue = null;
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -389,15 +382,15 @@ public final class HttpRequestValues {
|
||||
Map<String, String> uriVars = (this.uriVars != null ? new HashMap<>(this.uriVars) : Collections.emptyMap());
|
||||
|
||||
Object bodyValue = this.bodyValue;
|
||||
if (this.multipartBuilder != null) {
|
||||
Assert.isTrue(bodyValue == null && this.body == null, "Expected body or request parts, not both");
|
||||
bodyValue = this.multipartBuilder.build();
|
||||
if (hasParts()) {
|
||||
Assert.isTrue(!hasBody(), "Expected body or request parts, not both");
|
||||
bodyValue = buildMultipartBody();
|
||||
}
|
||||
|
||||
if (!CollectionUtils.isEmpty(this.requestParams)) {
|
||||
if (hasContentType(MediaType.APPLICATION_FORM_URLENCODED)) {
|
||||
Assert.isTrue(this.multipartBuilder == null, "Cannot add parts to form data request");
|
||||
Assert.isTrue(bodyValue == null && this.body == null, "Cannot set body of form data request");
|
||||
if (hasFormDataContentType()) {
|
||||
Assert.isTrue(!hasParts(), "Request parts not expected for a form data request");
|
||||
Assert.isTrue(!hasBody(), "Body not expected for a form data request");
|
||||
bodyValue = new LinkedMultiValueMap<>(this.requestParams);
|
||||
}
|
||||
else if (uri != null) {
|
||||
@@ -426,13 +419,26 @@ public final class HttpRequestValues {
|
||||
Map<String, Object> attributes = (this.attributes != null ?
|
||||
new HashMap<>(this.attributes) : Collections.emptyMap());
|
||||
|
||||
return new HttpRequestValues(
|
||||
this.httpMethod, uri, uriTemplate, uriVars, headers, cookies, attributes,
|
||||
bodyValue, this.body, this.bodyElementType);
|
||||
return createRequestValues(
|
||||
this.httpMethod, uri, uriTemplate, uriVars, headers, cookies, attributes, bodyValue);
|
||||
}
|
||||
|
||||
private boolean hasContentType(MediaType mediaType) {
|
||||
return (this.headers != null && mediaType.equals(this.headers.getContentType()));
|
||||
protected boolean hasParts() {
|
||||
return (this.parts != null);
|
||||
}
|
||||
|
||||
protected boolean hasBody() {
|
||||
return (this.bodyValue != null);
|
||||
}
|
||||
|
||||
protected Object buildMultipartBody() {
|
||||
Assert.notNull(this.parts, "`parts` is null, was hasParts() not called?");
|
||||
return this.parts;
|
||||
}
|
||||
|
||||
private boolean hasFormDataContentType() {
|
||||
return (this.headers != null &&
|
||||
MediaType.APPLICATION_FORM_URLENCODED.equals(this.headers.getContentType()));
|
||||
}
|
||||
|
||||
private String appendQueryParams(
|
||||
@@ -453,6 +459,15 @@ public final class HttpRequestValues {
|
||||
return uriComponentsBuilder.build().toUriString();
|
||||
}
|
||||
|
||||
protected HttpRequestValues createRequestValues(
|
||||
@Nullable HttpMethod httpMethod,
|
||||
@Nullable URI uri, @Nullable String uriTemplate, Map<String, String> uriVars,
|
||||
HttpHeaders headers, MultiValueMap<String, String> cookies, Map<String, Object> attributes,
|
||||
@Nullable Object bodyValue) {
|
||||
|
||||
return new HttpRequestValues(
|
||||
this.httpMethod, uri, uriTemplate, uriVars, headers, cookies, attributes, bodyValue);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.reactivestreams.Publisher;
|
||||
import reactor.core.publisher.Flux;
|
||||
@@ -81,13 +82,16 @@ final class HttpServiceMethod {
|
||||
this.parameters = initMethodParameters(method);
|
||||
this.argumentResolvers = argumentResolvers;
|
||||
|
||||
this.requestValuesInitializer =
|
||||
HttpRequestValuesInitializer.create(method, containingClass, embeddedValueResolver);
|
||||
boolean isReactorAdapter = (REACTOR_PRESENT && adapter instanceof ReactorHttpExchangeAdapter);
|
||||
|
||||
this.responseFunction =
|
||||
(REACTOR_PRESENT && adapter instanceof ReactorHttpExchangeAdapter reactorAdapter ?
|
||||
ReactorExchangeResponseFunction.create(reactorAdapter, method) :
|
||||
ExchangeResponseFunction.create(adapter, method));
|
||||
this.requestValuesInitializer =
|
||||
HttpRequestValuesInitializer.create(
|
||||
method, containingClass, embeddedValueResolver,
|
||||
(isReactorAdapter ? ReactiveHttpRequestValues::builder : HttpRequestValues::builder));
|
||||
|
||||
this.responseFunction = (isReactorAdapter ?
|
||||
ReactorExchangeResponseFunction.create((ReactorHttpExchangeAdapter) adapter, method) :
|
||||
ExchangeResponseFunction.create(adapter, method));
|
||||
}
|
||||
|
||||
private static MethodParameter[] initMethodParameters(Method method) {
|
||||
@@ -147,20 +151,11 @@ final class HttpServiceMethod {
|
||||
*/
|
||||
private record HttpRequestValuesInitializer(
|
||||
@Nullable HttpMethod httpMethod, @Nullable String url,
|
||||
@Nullable MediaType contentType, @Nullable List<MediaType> acceptMediaTypes) {
|
||||
|
||||
private HttpRequestValuesInitializer(
|
||||
HttpMethod httpMethod, @Nullable String url,
|
||||
@Nullable MediaType contentType, @Nullable List<MediaType> acceptMediaTypes) {
|
||||
|
||||
this.url = url;
|
||||
this.httpMethod = httpMethod;
|
||||
this.contentType = contentType;
|
||||
this.acceptMediaTypes = acceptMediaTypes;
|
||||
}
|
||||
@Nullable MediaType contentType, @Nullable List<MediaType> acceptMediaTypes,
|
||||
Supplier<HttpRequestValues.Builder> requestValuesSupplier) {
|
||||
|
||||
public HttpRequestValues.Builder initializeRequestValuesBuilder() {
|
||||
HttpRequestValues.Builder requestValues = HttpRequestValues.builder();
|
||||
HttpRequestValues.Builder requestValues = this.requestValuesSupplier.get();
|
||||
if (this.httpMethod != null) {
|
||||
requestValues.setHttpMethod(this.httpMethod);
|
||||
}
|
||||
@@ -181,7 +176,8 @@ final class HttpServiceMethod {
|
||||
* Introspect the method and create the request factory for it.
|
||||
*/
|
||||
public static HttpRequestValuesInitializer create(
|
||||
Method method, Class<?> containingClass, @Nullable StringValueResolver embeddedValueResolver) {
|
||||
Method method, Class<?> containingClass, @Nullable StringValueResolver embeddedValueResolver,
|
||||
Supplier<HttpRequestValues.Builder> requestValuesSupplier) {
|
||||
|
||||
HttpExchange annot1 = AnnotatedElementUtils.findMergedAnnotation(containingClass, HttpExchange.class);
|
||||
HttpExchange annot2 = AnnotatedElementUtils.findMergedAnnotation(method, HttpExchange.class);
|
||||
@@ -193,7 +189,8 @@ final class HttpServiceMethod {
|
||||
MediaType contentType = initContentType(annot1, annot2);
|
||||
List<MediaType> acceptableMediaTypes = initAccept(annot1, annot2);
|
||||
|
||||
return new HttpRequestValuesInitializer(httpMethod, url, contentType, acceptableMediaTypes);
|
||||
return new HttpRequestValuesInitializer(
|
||||
httpMethod, url, contentType, acceptableMediaTypes, requestValuesSupplier);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
|
||||
@@ -0,0 +1,275 @@
|
||||
/*
|
||||
* Copyright 2002-2023 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
|
||||
*
|
||||
* https://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.service.invoker;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.reactivestreams.Publisher;
|
||||
|
||||
import org.springframework.core.ParameterizedTypeReference;
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.client.MultipartBodyBuilder;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
|
||||
/**
|
||||
* {@link HttpRequestValues} extension for use with {@link ReactorHttpExchangeAdapter}.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 6.1
|
||||
*/
|
||||
public final class ReactiveHttpRequestValues extends HttpRequestValues {
|
||||
|
||||
@Nullable
|
||||
private final Publisher<?> body;
|
||||
|
||||
@Nullable
|
||||
private final ParameterizedTypeReference<?> bodyElementType;
|
||||
|
||||
|
||||
private ReactiveHttpRequestValues(
|
||||
@Nullable HttpMethod httpMethod,
|
||||
@Nullable URI uri, @Nullable String uriTemplate, Map<String, String> uriVariables,
|
||||
HttpHeaders headers, MultiValueMap<String, String> cookies, Map<String, Object> attributes,
|
||||
@Nullable Object bodyValue, @Nullable Publisher<?> body, @Nullable ParameterizedTypeReference<?> elementType) {
|
||||
|
||||
super(httpMethod, uri, uriTemplate, uriVariables, headers, cookies, attributes, bodyValue);
|
||||
|
||||
this.body = body;
|
||||
this.bodyElementType = elementType;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return a {@link Publisher} that will produce for the request body.
|
||||
* <p>This is mutually exclusive with {@link #getBodyValue()}.
|
||||
* Only one of the two or neither is set.
|
||||
*/
|
||||
@Nullable
|
||||
public Publisher<?> getBodyPublisher() {
|
||||
return this.body;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the element type for a {@linkplain #getBodyPublisher() Publisher body}.
|
||||
*/
|
||||
@Nullable
|
||||
public ParameterizedTypeReference<?> getBodyPublisherElementType() {
|
||||
return this.bodyElementType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the request body as a Publisher.
|
||||
* <p>This is mutually exclusive with {@link #getBodyValue()}.
|
||||
* Only one of the two or neither is set.
|
||||
*/
|
||||
@SuppressWarnings("removal")
|
||||
@Nullable
|
||||
public Publisher<?> getBody() {
|
||||
return getBodyPublisher();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the element type for a {@linkplain #getBodyPublisher() Publisher body}.
|
||||
*/
|
||||
@SuppressWarnings("removal")
|
||||
@Nullable
|
||||
public ParameterizedTypeReference<?> getBodyElementType() {
|
||||
return getBodyPublisherElementType();
|
||||
}
|
||||
|
||||
|
||||
public static Builder builder() {
|
||||
return new Builder();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Builder for {@link ReactiveHttpRequestValues}.
|
||||
*/
|
||||
public final static class Builder extends HttpRequestValues.Builder {
|
||||
|
||||
@Nullable
|
||||
private MultipartBodyBuilder multipartBuilder;
|
||||
|
||||
@Nullable
|
||||
private Publisher<?> body;
|
||||
|
||||
@Nullable
|
||||
private ParameterizedTypeReference<?> bodyElementType;
|
||||
|
||||
@Override
|
||||
public Builder setHttpMethod(HttpMethod httpMethod) {
|
||||
super.setHttpMethod(httpMethod);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder setUri(URI uri) {
|
||||
super.setUri(uri);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder setUriTemplate(String uriTemplate) {
|
||||
super.setUriTemplate(uriTemplate);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder setUriVariable(String name, String value) {
|
||||
super.setUriVariable(name, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder setAccept(List<MediaType> acceptableMediaTypes) {
|
||||
super.setAccept(acceptableMediaTypes);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder setContentType(MediaType contentType) {
|
||||
super.setContentType(contentType);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder addHeader(String headerName, String... headerValues) {
|
||||
super.addHeader(headerName, headerValues);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder addCookie(String name, String... values) {
|
||||
super.addCookie(name, values);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder addRequestParameter(String name, String... values) {
|
||||
super.addRequestParameter(name, values);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder addAttribute(String name, Object value) {
|
||||
super.addAttribute(name, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a part to a multipart request. The part value may be as described
|
||||
* in {@link MultipartBodyBuilder#part(String, Object)}.
|
||||
*/
|
||||
@Override
|
||||
public Builder addRequestPart(String name, Object part) {
|
||||
this.multipartBuilder = (this.multipartBuilder != null ? this.multipartBuilder : new MultipartBodyBuilder());
|
||||
this.multipartBuilder.part(name, part);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Variant of {@link #addRequestPart(String, Object)} that allows the
|
||||
* part value to be produced by a {@link Publisher}.
|
||||
*/
|
||||
public <T, P extends Publisher<T>> Builder addRequestPartPublisher(
|
||||
String name, P publisher, ParameterizedTypeReference<T> elementTye) {
|
||||
|
||||
this.multipartBuilder = (this.multipartBuilder != null ? this.multipartBuilder : new MultipartBodyBuilder());
|
||||
this.multipartBuilder.asyncPart(name, publisher, elementTye);
|
||||
return this;
|
||||
}
|
||||
|
||||
@SuppressWarnings("removal")
|
||||
@Override
|
||||
public <T, P extends Publisher<T>> Builder addRequestPart(String name, P publisher, ResolvableType type) {
|
||||
return addRequestPartPublisher(name, publisher, ParameterizedTypeReference.forType(type.getType()));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* <p>This is mutually exclusive with, and resets any previously set
|
||||
* {@linkplain #setBodyPublisher(Publisher, ParameterizedTypeReference)}.
|
||||
*/
|
||||
@Override
|
||||
public void setBodyValue(Object bodyValue) {
|
||||
super.setBodyValue(bodyValue);
|
||||
this.body = null;
|
||||
this.bodyElementType = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the request body as a Reactive Streams Publisher.
|
||||
* <p>This is mutually exclusive with, and resets any previously set
|
||||
* {@linkplain #setBodyValue(Object) body value}.
|
||||
*/
|
||||
@SuppressWarnings("DataFlowIssue")
|
||||
public <T, P extends Publisher<T>> void setBodyPublisher(P body, ParameterizedTypeReference<T> elementTye) {
|
||||
this.body = body;
|
||||
this.bodyElementType = elementTye;
|
||||
super.setBodyValue(null);
|
||||
}
|
||||
|
||||
@SuppressWarnings("removal")
|
||||
@Override
|
||||
public <T, P extends Publisher<T>> void setBody(P body, ParameterizedTypeReference<T> elementTye) {
|
||||
setBodyPublisher(body, elementTye);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ReactiveHttpRequestValues build() {
|
||||
return (ReactiveHttpRequestValues) super.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean hasParts() {
|
||||
return (this.multipartBuilder != null);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean hasBody() {
|
||||
return (super.hasBody() || this.body != null);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object buildMultipartBody() {
|
||||
Assert.notNull(this.multipartBuilder, "`multipartBuilder` is null, was hasParts() not called?");
|
||||
return this.multipartBuilder.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ReactiveHttpRequestValues createRequestValues(
|
||||
@Nullable HttpMethod httpMethod,
|
||||
@Nullable URI uri, @Nullable String uriTemplate, Map<String, String> uriVars,
|
||||
HttpHeaders headers, MultiValueMap<String, String> cookies, Map<String, Object> attributes,
|
||||
@Nullable Object bodyValue) {
|
||||
|
||||
return new ReactiveHttpRequestValues(
|
||||
httpMethod, uri, uriTemplate, uriVars, headers, cookies, attributes,
|
||||
bodyValue, this.body, this.bodyElementType);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -78,9 +78,14 @@ public class RequestBodyArgumentResolver implements HttpServiceArgumentResolver
|
||||
Assert.isTrue(!adapter.isNoValue(), message);
|
||||
Assert.isTrue(nestedParameter.getNestedParameterType() != Void.class, message);
|
||||
|
||||
requestValues.setBody(
|
||||
adapter.toPublisher(argument),
|
||||
ParameterizedTypeReference.forType(nestedParameter.getNestedGenericParameterType()));
|
||||
if (requestValues instanceof ReactiveHttpRequestValues.Builder reactiveRequestValues) {
|
||||
reactiveRequestValues.setBodyPublisher(
|
||||
adapter.toPublisher(argument), asParameterizedTypeRef(nestedParameter));
|
||||
}
|
||||
else {
|
||||
throw new IllegalStateException(
|
||||
"RequestBody with a reactive type is only supported with reactive client");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -93,4 +98,8 @@ public class RequestBodyArgumentResolver implements HttpServiceArgumentResolver
|
||||
return true;
|
||||
}
|
||||
|
||||
private static ParameterizedTypeReference<Object> asParameterizedTypeRef(MethodParameter nestedParam) {
|
||||
return ParameterizedTypeReference.forType(nestedParam.getNestedGenericParameterType());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -19,9 +19,9 @@ package org.springframework.web.service.invoker;
|
||||
import org.reactivestreams.Publisher;
|
||||
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.core.ParameterizedTypeReference;
|
||||
import org.springframework.core.ReactiveAdapter;
|
||||
import org.springframework.core.ReactiveAdapterRegistry;
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.http.HttpEntity;
|
||||
import org.springframework.http.codec.multipart.Part;
|
||||
import org.springframework.lang.Nullable;
|
||||
@@ -89,9 +89,20 @@ public class RequestPartArgumentResolver extends AbstractNamedValueArgumentResol
|
||||
Class<?> type = parameter.getParameterType();
|
||||
ReactiveAdapter adapter = this.reactiveAdapterRegistry.getAdapter(type);
|
||||
if (adapter != null) {
|
||||
Assert.isTrue(!adapter.isNoValue(), "Expected publisher that produces a value");
|
||||
Publisher<?> publisher = adapter.toPublisher(value);
|
||||
requestValues.addRequestPart(name, publisher, ResolvableType.forMethodParameter(parameter.nested()));
|
||||
MethodParameter nestedParameter = parameter.nested();
|
||||
|
||||
String message = "Async type for @RequestPart should produce value(s)";
|
||||
Assert.isTrue(!adapter.isNoValue(), message);
|
||||
Assert.isTrue(nestedParameter.getNestedParameterType() != Void.class, message);
|
||||
|
||||
if (requestValues instanceof ReactiveHttpRequestValues.Builder reactiveValues) {
|
||||
reactiveValues.addRequestPartPublisher(
|
||||
name, adapter.toPublisher(value), asParameterizedTypeRef(nestedParameter));
|
||||
}
|
||||
else {
|
||||
throw new IllegalStateException(
|
||||
"RequestPart with a reactive type is only supported with reactive client");
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -99,4 +110,8 @@ public class RequestPartArgumentResolver extends AbstractNamedValueArgumentResol
|
||||
requestValues.addRequestPart(name, value);
|
||||
}
|
||||
|
||||
private static ParameterizedTypeReference<Object> asParameterizedTypeRef(MethodParameter nestedParam) {
|
||||
return ParameterizedTypeReference.forType(nestedParam.getNestedGenericParameterType());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user