Add HttpRequestValues.Processor
Closes gh-34699
This commit is contained in:
@@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
package org.springframework.web.service.invoker;
|
package org.springframework.web.service.invoker;
|
||||||
|
|
||||||
|
import java.lang.reflect.Method;
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
@@ -176,6 +177,9 @@ public class HttpRequestValues {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a builder for {@link HttpRequestValues}.
|
||||||
|
*/
|
||||||
public static Builder builder() {
|
public static Builder builder() {
|
||||||
return new Builder();
|
return new Builder();
|
||||||
}
|
}
|
||||||
@@ -209,6 +213,28 @@ public class HttpRequestValues {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A contract that allows further customization of {@link HttpRequestValues}
|
||||||
|
* in addition to those added by argument resolvers.
|
||||||
|
* <p>Use {@link HttpServiceProxyFactory.Builder#httpRequestValuesProcessor(Processor)}
|
||||||
|
* to add such a processor.
|
||||||
|
* @since 7.0
|
||||||
|
*/
|
||||||
|
public interface Processor {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invoked after argument resolvers have been called, and before the
|
||||||
|
* {@link HttpRequestValues} is built.
|
||||||
|
* @param method the {@code @HttpExchange} method
|
||||||
|
* @param arguments the raw argument values to the method
|
||||||
|
* @param builder the builder to add request values too; the builder
|
||||||
|
* also exposes method {@link Metadata} from the {@code HttpExchange} method.
|
||||||
|
*/
|
||||||
|
void process(Method method, @Nullable Object[] arguments, Builder builder);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Builder for {@link HttpRequestValues}.
|
* Builder for {@link HttpRequestValues}.
|
||||||
*/
|
*/
|
||||||
@@ -238,6 +264,9 @@ public class HttpRequestValues {
|
|||||||
|
|
||||||
private @Nullable Object bodyValue;
|
private @Nullable Object bodyValue;
|
||||||
|
|
||||||
|
protected Builder() {
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the HTTP method for the request.
|
* Set the HTTP method for the request.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -77,6 +77,8 @@ final class HttpServiceMethod {
|
|||||||
|
|
||||||
private final List<HttpServiceArgumentResolver> argumentResolvers;
|
private final List<HttpServiceArgumentResolver> argumentResolvers;
|
||||||
|
|
||||||
|
private final HttpRequestValues.Processor requestValuesProcessor;
|
||||||
|
|
||||||
private final HttpRequestValuesInitializer requestValuesInitializer;
|
private final HttpRequestValuesInitializer requestValuesInitializer;
|
||||||
|
|
||||||
private final ResponseFunction responseFunction;
|
private final ResponseFunction responseFunction;
|
||||||
@@ -84,11 +86,13 @@ final class HttpServiceMethod {
|
|||||||
|
|
||||||
HttpServiceMethod(
|
HttpServiceMethod(
|
||||||
Method method, Class<?> containingClass, List<HttpServiceArgumentResolver> argumentResolvers,
|
Method method, Class<?> containingClass, List<HttpServiceArgumentResolver> argumentResolvers,
|
||||||
HttpExchangeAdapter adapter, @Nullable StringValueResolver embeddedValueResolver) {
|
HttpRequestValues.Processor valuesProcessor, HttpExchangeAdapter adapter,
|
||||||
|
@Nullable StringValueResolver embeddedValueResolver) {
|
||||||
|
|
||||||
this.method = method;
|
this.method = method;
|
||||||
this.parameters = initMethodParameters(method);
|
this.parameters = initMethodParameters(method);
|
||||||
this.argumentResolvers = argumentResolvers;
|
this.argumentResolvers = argumentResolvers;
|
||||||
|
this.requestValuesProcessor = valuesProcessor;
|
||||||
|
|
||||||
boolean isReactorAdapter = (REACTOR_PRESENT && adapter instanceof ReactorHttpExchangeAdapter);
|
boolean isReactorAdapter = (REACTOR_PRESENT && adapter instanceof ReactorHttpExchangeAdapter);
|
||||||
|
|
||||||
@@ -129,6 +133,7 @@ final class HttpServiceMethod {
|
|||||||
public @Nullable Object invoke(@Nullable Object[] arguments) {
|
public @Nullable Object invoke(@Nullable Object[] arguments) {
|
||||||
HttpRequestValues.Builder requestValues = this.requestValuesInitializer.initializeRequestValuesBuilder();
|
HttpRequestValues.Builder requestValues = this.requestValuesInitializer.initializeRequestValuesBuilder();
|
||||||
applyArguments(requestValues, arguments);
|
applyArguments(requestValues, arguments);
|
||||||
|
this.requestValuesProcessor.process(this.method, arguments, requestValues);
|
||||||
return this.responseFunction.execute(requestValues.build());
|
return this.responseFunction.execute(requestValues.build());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -58,15 +58,19 @@ public final class HttpServiceProxyFactory {
|
|||||||
|
|
||||||
private final List<HttpServiceArgumentResolver> argumentResolvers;
|
private final List<HttpServiceArgumentResolver> argumentResolvers;
|
||||||
|
|
||||||
|
private final HttpRequestValues.Processor requestValuesProcessor;
|
||||||
|
|
||||||
private final @Nullable StringValueResolver embeddedValueResolver;
|
private final @Nullable StringValueResolver embeddedValueResolver;
|
||||||
|
|
||||||
|
|
||||||
private HttpServiceProxyFactory(
|
private HttpServiceProxyFactory(
|
||||||
HttpExchangeAdapter exchangeAdapter, List<HttpServiceArgumentResolver> argumentResolvers,
|
HttpExchangeAdapter exchangeAdapter, List<HttpServiceArgumentResolver> argumentResolvers,
|
||||||
|
List<HttpRequestValues.Processor> requestValuesProcessor,
|
||||||
@Nullable StringValueResolver embeddedValueResolver) {
|
@Nullable StringValueResolver embeddedValueResolver) {
|
||||||
|
|
||||||
this.exchangeAdapter = exchangeAdapter;
|
this.exchangeAdapter = exchangeAdapter;
|
||||||
this.argumentResolvers = argumentResolvers;
|
this.argumentResolvers = argumentResolvers;
|
||||||
|
this.requestValuesProcessor = new CompositeHttpRequestValuesProcessor(requestValuesProcessor);
|
||||||
this.embeddedValueResolver = embeddedValueResolver;
|
this.embeddedValueResolver = embeddedValueResolver;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -97,7 +101,8 @@ public final class HttpServiceProxyFactory {
|
|||||||
"No argument resolvers: afterPropertiesSet was not called");
|
"No argument resolvers: afterPropertiesSet was not called");
|
||||||
|
|
||||||
return new HttpServiceMethod(
|
return new HttpServiceMethod(
|
||||||
method, serviceType, this.argumentResolvers, this.exchangeAdapter, this.embeddedValueResolver);
|
method, serviceType, this.argumentResolvers, this.requestValuesProcessor,
|
||||||
|
this.exchangeAdapter, this.embeddedValueResolver);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -126,6 +131,8 @@ public final class HttpServiceProxyFactory {
|
|||||||
|
|
||||||
private final List<HttpServiceArgumentResolver> customArgumentResolvers = new ArrayList<>();
|
private final List<HttpServiceArgumentResolver> customArgumentResolvers = new ArrayList<>();
|
||||||
|
|
||||||
|
private final List<HttpRequestValues.Processor> requestValuesProcessors = new ArrayList<>();
|
||||||
|
|
||||||
private @Nullable ConversionService conversionService;
|
private @Nullable ConversionService conversionService;
|
||||||
|
|
||||||
private @Nullable StringValueResolver embeddedValueResolver;
|
private @Nullable StringValueResolver embeddedValueResolver;
|
||||||
@@ -154,6 +161,18 @@ public final class HttpServiceProxyFactory {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register an {@link HttpRequestValues} processor that can further
|
||||||
|
* customize request values based on the method and all arguments.
|
||||||
|
* @param processor the processor to add
|
||||||
|
* @return this same builder instance
|
||||||
|
* @since 7.0
|
||||||
|
*/
|
||||||
|
public Builder httpRequestValuesProcessor(HttpRequestValues.Processor processor) {
|
||||||
|
this.requestValuesProcessors.add(processor);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the {@link ConversionService} to use where input values need to
|
* Set the {@link ConversionService} to use where input values need to
|
||||||
* be formatted as Strings.
|
* be formatted as Strings.
|
||||||
@@ -183,7 +202,8 @@ public final class HttpServiceProxyFactory {
|
|||||||
Assert.notNull(this.exchangeAdapter, "HttpClientAdapter is required");
|
Assert.notNull(this.exchangeAdapter, "HttpClientAdapter is required");
|
||||||
|
|
||||||
return new HttpServiceProxyFactory(
|
return new HttpServiceProxyFactory(
|
||||||
this.exchangeAdapter, initArgumentResolvers(), this.embeddedValueResolver);
|
this.exchangeAdapter, initArgumentResolvers(), this.requestValuesProcessors,
|
||||||
|
this.embeddedValueResolver);
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings({"DataFlowIssue", "NullAway"})
|
@SuppressWarnings({"DataFlowIssue", "NullAway"})
|
||||||
@@ -251,7 +271,21 @@ public final class HttpServiceProxyFactory {
|
|||||||
System.arraycopy(args, 0, functionArgs, 0, args.length - 1);
|
System.arraycopy(args, 0, functionArgs, 0, args.length - 1);
|
||||||
return functionArgs;
|
return functionArgs;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Processor that delegates to a list of other processors.
|
||||||
|
*/
|
||||||
|
private record CompositeHttpRequestValuesProcessor(List<HttpRequestValues.Processor> processors)
|
||||||
|
implements HttpRequestValues.Processor {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void process(Method method, @Nullable Object[] arguments, HttpRequestValues.Builder builder) {
|
||||||
|
for (HttpRequestValues.Processor processor : this.processors) {
|
||||||
|
processor.process(method, arguments, builder);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -94,6 +94,9 @@ public final class ReactiveHttpRequestValues extends HttpRequestValues {
|
|||||||
|
|
||||||
private @Nullable ParameterizedTypeReference<?> bodyElementType;
|
private @Nullable ParameterizedTypeReference<?> bodyElementType;
|
||||||
|
|
||||||
|
private Builder() {
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Builder setHttpMethod(HttpMethod httpMethod) {
|
public Builder setHttpMethod(HttpMethod httpMethod) {
|
||||||
super.setHttpMethod(httpMethod);
|
super.setHttpMethod(httpMethod);
|
||||||
|
|||||||
@@ -198,12 +198,12 @@ class HttpServiceMethodTests {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
void typeAndMethodAnnotatedService() {
|
void typeAndMethodAnnotatedService() {
|
||||||
HttpServiceProxyFactory proxyFactory = HttpServiceProxyFactory.builder()
|
|
||||||
.exchangeAdapter(this.client)
|
|
||||||
.embeddedValueResolver(value -> (value.equals("${baseUrl}") ? "/base" : value))
|
|
||||||
.build();
|
|
||||||
|
|
||||||
MethodLevelAnnotatedService service = proxyFactory.createClient(TypeAndMethodLevelAnnotatedService.class);
|
MethodLevelAnnotatedService service = HttpServiceProxyFactory.builder()
|
||||||
|
.exchangeAdapter(this.client)
|
||||||
|
.embeddedValueResolver(value -> (value.equals("${baseUrl}") ? "/base" : value))
|
||||||
|
.build()
|
||||||
|
.createClient(TypeAndMethodLevelAnnotatedService.class);
|
||||||
|
|
||||||
service.performGet();
|
service.performGet();
|
||||||
|
|
||||||
@@ -222,6 +222,19 @@ class HttpServiceMethodTests {
|
|||||||
assertThat(requestValues.getHeaders().getAccept()).containsOnly(MediaType.APPLICATION_JSON);
|
assertThat(requestValues.getHeaders().getAccept()).containsOnly(MediaType.APPLICATION_JSON);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void httpRequestValuesProcessor() {
|
||||||
|
|
||||||
|
HttpServiceProxyFactory.builder()
|
||||||
|
.exchangeAdapter(this.client)
|
||||||
|
.httpRequestValuesProcessor((m, a, builder) -> builder.addAttribute("foo", "a"))
|
||||||
|
.build()
|
||||||
|
.createClient(Service.class)
|
||||||
|
.execute();
|
||||||
|
|
||||||
|
assertThat(this.client.getRequestValues().getAttributes().get("foo")).isEqualTo("a");
|
||||||
|
}
|
||||||
|
|
||||||
@Test // gh-32049
|
@Test // gh-32049
|
||||||
void multipleAnnotationsAtClassLevel() {
|
void multipleAnnotationsAtClassLevel() {
|
||||||
Class<?> serviceInterface = MultipleClassLevelAnnotationsService.class;
|
Class<?> serviceInterface = MultipleClassLevelAnnotationsService.class;
|
||||||
|
|||||||
Reference in New Issue
Block a user