Skip to content

Commit bf66f45

Browse files
committed
Support overriding, removing headers in ClientRequest
This commit adds two Consumer based methods to ClientRequest that allow for direct manipulation of the underlying headers or cookies map. As such, these methods can be used to override or remove existing entries. Issue: SPR-15635
1 parent e6f1950 commit bf66f45

File tree

4 files changed

+72
-21
lines changed

4 files changed

+72
-21
lines changed

spring-webflux/src/main/java/org/springframework/web/reactive/function/client/ClientRequest.java

Lines changed: 30 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
package org.springframework.web.reactive.function.client;
1818

1919
import java.net.URI;
20+
import java.util.function.Consumer;
2021

2122
import org.reactivestreams.Publisher;
2223
import reactor.core.publisher.Mono;
@@ -109,7 +110,7 @@ static Builder method(HttpMethod method, URI url) {
109110
interface Builder {
110111

111112
/**
112-
* Add the given, single header value under the given name.
113+
* Add the given header value(s) under the given name.
113114
* @param headerName the header name
114115
* @param headerValues the header value(s)
115116
* @return this builder
@@ -118,27 +119,49 @@ interface Builder {
118119
Builder header(String headerName, String... headerValues);
119120

120121
/**
121-
* Copy the given headers into the entity's headers map.
122-
* @param headers the existing HttpHeaders to copy from
122+
* Add the given headers into this request's headers map.
123+
* @param headers the existing HttpHeaders to add from
123124
* @return this builder
124125
*/
125126
Builder headers(HttpHeaders headers);
126127

127128
/**
128-
* Add a cookie with the given name and value.
129+
* Manipulate this request's headers with the given consumer. The
130+
* headers provided to the consumer are "live", so that the consumer can be used to
131+
* {@linkplain HttpHeaders#set(String, String) overwrite} existing header values,
132+
* {@linkplain HttpHeaders#remove(Object) remove} values, or use any of the other
133+
* {@link HttpHeaders} methods.
134+
* @param headersConsumer a function that consumes the {@code HttpHeaders}
135+
* @return this builder
136+
*/
137+
Builder headers(Consumer<HttpHeaders> headersConsumer);
138+
139+
/**
140+
* Add a cookie with the given name and value(s).
129141
* @param name the cookie name
130-
* @param value the cookie value
142+
* @param values the cookie value(s)
131143
* @return this builder
132144
*/
133-
Builder cookie(String name, String value);
145+
Builder cookie(String name, String... values);
134146

135147
/**
136-
* Copy the given cookies into the entity's cookies map.
148+
* Add the given cookies into this request's cookies map.
137149
* @param cookies the existing cookies to copy from
138150
* @return this builder
139151
*/
140152
Builder cookies(MultiValueMap<String, String> cookies);
141153

154+
/**
155+
* Manipulate this request's cookies with the given consumer. The
156+
* map provided to the consumer is "live", so that the consumer can be used to
157+
* {@linkplain MultiValueMap#set(Object, Object) overwrite} existing header values,
158+
* {@linkplain MultiValueMap#remove(Object) remove} values, or use any of the other
159+
* {@link MultiValueMap} methods.
160+
* @param cookiesConsumer a function that consumes the cookies map
161+
* @return this builder
162+
*/
163+
Builder cookies(Consumer<MultiValueMap<String, String>> cookiesConsumer);
164+
142165
/**
143166
* Set the body of the request to the given {@code BodyInserter}.
144167
* @param inserter the {@code BodyInserter} that writes to the request

spring-webflux/src/main/java/org/springframework/web/reactive/function/client/DefaultClientRequestBuilder.java

Lines changed: 31 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import java.util.List;
2222
import java.util.Map;
2323
import java.util.Optional;
24+
import java.util.function.Consumer;
2425

2526
import org.reactivestreams.Publisher;
2627
import reactor.core.publisher.Mono;
@@ -73,6 +74,7 @@ public ClientRequest.Builder header(String headerName, String... headerValues) {
7374

7475
@Override
7576
public ClientRequest.Builder headers(HttpHeaders headers) {
77+
Assert.notNull(headers, "'headers' must not be null");
7678
for (Map.Entry<String, List<String>> entry : headers.entrySet()) {
7779
String headerName = entry.getKey();
7880
for (String headerValue : entry.getValue()) {
@@ -83,14 +85,36 @@ public ClientRequest.Builder headers(HttpHeaders headers) {
8385
}
8486

8587
@Override
86-
public ClientRequest.Builder cookie(String name, String value) {
87-
this.cookies.add(name, value);
88+
public ClientRequest.Builder headers(Consumer<HttpHeaders> headersConsumer) {
89+
Assert.notNull(headersConsumer, "'headersConsumer' must not be null");
90+
headersConsumer.accept(this.headers);
91+
return this;
92+
}
93+
94+
@Override
95+
public ClientRequest.Builder cookie(String name, String... values) {
96+
for (String value : values) {
97+
this.cookies.add(name, value);
98+
}
8899
return this;
89100
}
90101

91102
@Override
92103
public ClientRequest.Builder cookies(MultiValueMap<String, String> cookies) {
93-
this.cookies.putAll(cookies);
104+
Assert.notNull(cookies, "'cookies' must not be null");
105+
for (Map.Entry<String, List<String>> entry : cookies.entrySet()) {
106+
String cookieName = entry.getKey();
107+
for (String cookieValue : entry.getValue()) {
108+
this.cookies.add(cookieName, cookieValue);
109+
}
110+
}
111+
return this;
112+
}
113+
114+
@Override
115+
public ClientRequest.Builder cookies(Consumer<MultiValueMap<String, String>> cookiesConsumer) {
116+
Assert.notNull(cookiesConsumer, "'cookiesConsumer' must not be null");
117+
cookiesConsumer.accept(this.cookies);
94118
return this;
95119
}
96120

@@ -175,13 +199,10 @@ public Mono<Void> writeTo(ClientHttpRequest request, ExchangeStrategies strategi
175199

176200
MultiValueMap<String, HttpCookie> requestCookies = request.getCookies();
177201
if (!this.cookies.isEmpty()) {
178-
this.cookies.entrySet().forEach(entry -> {
179-
String name = entry.getKey();
180-
entry.getValue().forEach(value -> {
181-
HttpCookie cookie = new HttpCookie(name, value);
182-
requestCookies.add(name, cookie);
183-
});
184-
});
202+
this.cookies.forEach((name, values) -> values.forEach(value -> {
203+
HttpCookie cookie = new HttpCookie(name, value);
204+
requestCookies.add(name, cookie);
205+
}));
185206
}
186207

187208
return this.inserter.insert(request, new BodyInserter.Context() {

spring-webflux/src/main/java/org/springframework/web/reactive/function/client/ExchangeFilterFunctions.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,9 @@ public static ExchangeFilterFunction basicAuthentication(String username, String
4848
clientRequest -> {
4949
String authorization = authorization(username, password);
5050
ClientRequest authorizedRequest = ClientRequest.from(clientRequest)
51-
.header(HttpHeaders.AUTHORIZATION, authorization)
51+
.headers(headers -> {
52+
headers.set(HttpHeaders.AUTHORIZATION, authorization);
53+
})
5254
.build();
5355
return Mono.just(authorizedRequest);
5456
});

spring-webflux/src/test/java/org/springframework/web/reactive/function/client/DefaultClientRequestBuilderTests.java

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -51,11 +51,16 @@ public void from() throws Exception {
5151
ClientRequest other = ClientRequest.method(GET, URI.create("http://example.com"))
5252
.header("foo", "bar")
5353
.cookie("baz", "qux").build();
54-
ClientRequest result = ClientRequest.from(other).build();
54+
ClientRequest result = ClientRequest.from(other)
55+
.headers(httpHeaders -> httpHeaders.set("foo", "baar"))
56+
.cookies(cookies -> cookies.set("baz", "quux"))
57+
.build();
5558
assertEquals(new URI("http://example.com"), result.url());
5659
assertEquals(GET, result.method());
57-
assertEquals("bar", result.headers().getFirst("foo"));
58-
assertEquals("qux", result.cookies().getFirst("baz"));
60+
assertEquals(1, result.headers().size());
61+
assertEquals("baar", result.headers().getFirst("foo"));
62+
assertEquals(1, result.cookies().size());
63+
assertEquals("quux", result.cookies().getFirst("baz"));
5964
}
6065

6166
@Test

0 commit comments

Comments
 (0)