Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add cookie support to RestClient #33697

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ RestClient customClient = RestClient.builder()
.baseUrl("https://example.com")
.defaultUriVariables(Map.of("variable", "foo"))
.defaultHeader("My-Header", "Foo")
.defaultCookie("My-Cookie", "Bar")
.requestInterceptor(myCustomInterceptor)
.requestInitializer(myCustomInitializer)
.build();
Expand All @@ -55,6 +56,7 @@ val customClient = RestClient.builder()
.baseUrl("https://example.com")
.defaultUriVariables(mapOf("variable" to "foo"))
.defaultHeader("My-Header", "Foo")
.defaultCookie("My-Cookie", "Bar")
.requestInterceptor(myCustomInterceptor)
.requestInitializer(myCustomInitializer)
.build()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@

import org.springframework.core.ParameterizedTypeReference;
import org.springframework.core.ResolvableType;
import org.springframework.http.HttpCookie;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpRequest;
Expand All @@ -64,6 +65,8 @@
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.util.UriBuilder;
import org.springframework.web.util.UriBuilderFactory;

Expand Down Expand Up @@ -103,6 +106,9 @@ final class DefaultRestClient implements RestClient {
@Nullable
private final HttpHeaders defaultHeaders;

@Nullable
private final MultiValueMap<String, String> defaultCookies;

@Nullable
private final Consumer<RequestHeadersSpec<?>> defaultRequest;

Expand All @@ -123,6 +129,7 @@ final class DefaultRestClient implements RestClient {
@Nullable List<ClientHttpRequestInitializer> initializers,
UriBuilderFactory uriBuilderFactory,
@Nullable HttpHeaders defaultHeaders,
@Nullable MultiValueMap<String, String> defaultCookies,
@Nullable Consumer<RequestHeadersSpec<?>> defaultRequest,
@Nullable List<StatusHandler> statusHandlers,
List<HttpMessageConverter<?>> messageConverters,
Expand All @@ -135,6 +142,7 @@ final class DefaultRestClient implements RestClient {
this.interceptors = interceptors;
this.uriBuilderFactory = uriBuilderFactory;
this.defaultHeaders = defaultHeaders;
this.defaultCookies = defaultCookies;
this.defaultRequest = defaultRequest;
this.defaultStatusHandlers = (statusHandlers != null ? new ArrayList<>(statusHandlers) : new ArrayList<>());
this.messageConverters = messageConverters;
Expand Down Expand Up @@ -293,6 +301,8 @@ private static <T> Class<T> bodyClass(Type type) {

private class DefaultRequestBodyUriSpec implements RequestBodyUriSpec {

private static final String COOKIE_DELIMITER = "; ";

private final HttpMethod httpMethod;

@Nullable
Expand All @@ -301,6 +311,9 @@ private class DefaultRequestBodyUriSpec implements RequestBodyUriSpec {
@Nullable
private HttpHeaders headers;

@Nullable
private MultiValueMap<String, String> cookies;

@Nullable
private InternalBody body;

Expand Down Expand Up @@ -356,6 +369,13 @@ private HttpHeaders getHeaders() {
return this.headers;
}

private MultiValueMap<String, String> getCookies() {
if (this.cookies == null) {
this.cookies = new LinkedMultiValueMap<>(3);
}
return this.cookies;
}

@Override
public DefaultRequestBodyUriSpec header(String headerName, String... headerValues) {
for (String headerValue : headerValues) {
Expand All @@ -382,6 +402,18 @@ public DefaultRequestBodyUriSpec acceptCharset(Charset... acceptableCharsets) {
return this;
}

@Override
public DefaultRequestBodyUriSpec cookie(String name, String value) {
getCookies().add(name, value);
return this;
}

@Override
public DefaultRequestBodyUriSpec cookies(Consumer<MultiValueMap<String, String>> cookiesConsumer) {
cookiesConsumer.accept(getCookies());
return this;
}

@Override
public DefaultRequestBodyUriSpec contentType(MediaType contentType) {
getHeaders().setContentType(contentType);
Expand Down Expand Up @@ -525,6 +557,12 @@ private <T> T exchangeInternal(ExchangeFunction<T> exchangeFunction, boolean clo
try {
uri = initUri();
HttpHeaders headers = initHeaders();

MultiValueMap<String, String> cookies = initCookies();
if (!CollectionUtils.isEmpty(cookies)) {
headers.put(HttpHeaders.COOKIE, List.of(cookiesToHeaderValue(cookies)));
}

ClientHttpRequest clientRequest = createRequest(uri);
clientRequest.getHeaders().addAll(headers);
Map<String, Object> attributes = getAttributes();
Expand Down Expand Up @@ -599,6 +637,28 @@ else if (CollectionUtils.isEmpty(defaultHeaders)) {
}
}

private MultiValueMap<String, String> initCookies() {
MultiValueMap<String, String> mergedCookies = new LinkedMultiValueMap<>();

if(!CollectionUtils.isEmpty(defaultCookies)) {
mergedCookies.putAll(defaultCookies);
}

if(!CollectionUtils.isEmpty(this.cookies)) {
mergedCookies.putAll(this.cookies);
}

return mergedCookies;
}

private String cookiesToHeaderValue(MultiValueMap<String, String> cookies) {
List<String> flatCookies = new ArrayList<>();
cookies.forEach((name, cookieValues) -> cookieValues.forEach(value ->
flatCookies.add(new HttpCookie(name, value).toString())
));
return String.join(COOKIE_DELIMITER, flatCookies);
}

private ClientHttpRequest createRequest(URI uri) throws IOException {
ClientHttpRequestFactory factory;
if (DefaultRestClient.this.interceptors != null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.CollectionUtils;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.util.DefaultUriBuilderFactory;
import org.springframework.web.util.UriBuilderFactory;
import org.springframework.web.util.UriTemplateHandler;
Expand Down Expand Up @@ -127,6 +129,9 @@ final class DefaultRestClientBuilder implements RestClient.Builder {
@Nullable
private HttpHeaders defaultHeaders;

@Nullable
private MultiValueMap<String, String> defaultCookies;

@Nullable
private Consumer<RestClient.RequestHeadersSpec<?>> defaultRequest;

Expand Down Expand Up @@ -169,6 +174,8 @@ public DefaultRestClientBuilder(DefaultRestClientBuilder other) {
else {
this.defaultHeaders = null;
}
this.defaultCookies = (other.defaultCookies != null ?
new LinkedMultiValueMap<>(other.defaultCookies) : null);
this.defaultRequest = other.defaultRequest;
this.statusHandlers = (other.statusHandlers != null ? new ArrayList<>(other.statusHandlers) : null);

Expand Down Expand Up @@ -289,6 +296,25 @@ private HttpHeaders initHeaders() {
return this.defaultHeaders;
}

@Override
public RestClient.Builder defaultCookie(String cookie, String... values) {
initCookies().addAll(cookie, Arrays.asList(values));
return this;
}

@Override
public RestClient.Builder defaultCookies(Consumer<MultiValueMap<String, String>> cookiesConsumer) {
cookiesConsumer.accept(initCookies());
return this;
}

private MultiValueMap<String, String> initCookies() {
if (this.defaultCookies == null) {
this.defaultCookies = new LinkedMultiValueMap<>(3);
}
return this.defaultCookies;
}

@Override
public RestClient.Builder defaultRequest(Consumer<RestClient.RequestHeadersSpec<?>> defaultRequest) {
this.defaultRequest = this.defaultRequest != null ?
Expand Down Expand Up @@ -443,11 +469,13 @@ public RestClient build() {
ClientHttpRequestFactory requestFactory = initRequestFactory();
UriBuilderFactory uriBuilderFactory = initUriBuilderFactory();
HttpHeaders defaultHeaders = copyDefaultHeaders();
MultiValueMap<String, String> defaultCookies = copyDefaultCookies();
List<HttpMessageConverter<?>> messageConverters = (this.messageConverters != null ?
this.messageConverters : initMessageConverters());
return new DefaultRestClient(requestFactory,
this.interceptors, this.initializers, uriBuilderFactory,
defaultHeaders,
defaultCookies,
this.defaultRequest,
this.statusHandlers,
messageConverters,
Expand Down Expand Up @@ -501,4 +529,16 @@ private HttpHeaders copyDefaultHeaders() {
}
}

@Nullable
private MultiValueMap<String, String> copyDefaultCookies() {
if (this.defaultCookies != null) {
MultiValueMap<String, String> copy = new LinkedMultiValueMap<>(this.defaultCookies.size());
this.defaultCookies.forEach((key, values) -> copy.put(key, new ArrayList<>(values)));
return CollectionUtils.unmodifiableMultiValueMap(copy);
}
else {
return null;
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
import org.springframework.http.client.observation.ClientRequestObservationConvention;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.lang.Nullable;
import org.springframework.util.MultiValueMap;
import org.springframework.web.util.DefaultUriBuilderFactory;
import org.springframework.web.util.UriBuilder;
import org.springframework.web.util.UriBuilderFactory;
Expand Down Expand Up @@ -312,6 +313,23 @@ interface Builder {
*/
Builder defaultHeaders(Consumer<HttpHeaders> headersConsumer);

/**
* Global option to specify a cookie to be added to every request,
* if the request does not already contain such a cookie.
* @param cookie the cookie name
* @param values the cookie values
* @since 6.2
*/
Builder defaultCookie(String cookie, String... values);

/**
* Provides access to every {@link #defaultCookie(String, String...)}
* declared so far with the possibility to add, replace, or remove.
* @param cookiesConsumer a function that consumes the cookies map
* @since 6.2
*/
Builder defaultCookies(Consumer<MultiValueMap<String, String>> cookiesConsumer);

/**
* Provide a consumer to customize every request being built.
* @param defaultRequest the consumer to use for modifying requests
Expand Down Expand Up @@ -519,6 +537,24 @@ interface RequestHeadersSpec<S extends RequestHeadersSpec<S>> {
*/
S acceptCharset(Charset... acceptableCharsets);

/**
* Add a cookie with the given name and value.
* @param name the cookie name
* @param value the cookie value
* @return this builder
* @since 6.2
*/
S cookie(String name, String value);

/**
* Provides access to every cookie declared so far with the possibility
* to add, replace, or remove values.
* @param cookiesConsumer the consumer to provide access to
* @return this builder
* @since 6.2
*/
S cookies(Consumer<MultiValueMap<String, String>> cookiesConsumer);

/**
* Set the value of the {@code If-Modified-Since} header.
* @param ifModifiedSince the new value of the header
Expand Down
Loading