From db0066919727f6299f673682b3417648f71fd219 Mon Sep 17 00:00:00 2001 From: Arjen Poutsma Date: Tue, 7 Nov 2017 14:05:27 +0100 Subject: [PATCH] Allow to set cookies in ServerResponse Issue: SPR-16121 --- .../server/DefaultEntityResponseBuilder.java | 30 +++++++-- .../DefaultRenderingResponseBuilder.java | 30 +++++++-- .../server/DefaultServerResponseBuilder.java | 66 +++++++++++++++---- .../function/server/EntityResponse.java | 21 ++++++ .../function/server/RenderingResponse.java | 21 ++++++ .../function/server/ServerResponse.java | 25 +++++++ .../DefaultEntityResponseBuilderTests.java | 18 ++++- .../server/DefaultRenderingResponseTests.java | 16 +++++ .../DefaultServerResponseBuilderTests.java | 21 +++++- .../function/server/RouterFunctionsTests.java | 13 ++++ 10 files changed, 238 insertions(+), 23 deletions(-) diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/function/server/DefaultEntityResponseBuilder.java b/spring-webflux/src/main/java/org/springframework/web/reactive/function/server/DefaultEntityResponseBuilder.java index 0c6aed5b0a5..232094f0f95 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/function/server/DefaultEntityResponseBuilder.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/function/server/DefaultEntityResponseBuilder.java @@ -27,6 +27,7 @@ import java.util.List; import java.util.Map; import java.util.Optional; import java.util.Set; +import java.util.function.Consumer; import reactor.core.publisher.Mono; @@ -35,10 +36,13 @@ import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; +import org.springframework.http.ResponseCookie; import org.springframework.http.codec.HttpMessageWriter; import org.springframework.http.server.reactive.ServerHttpRequest; import org.springframework.http.server.reactive.ServerHttpResponse; import org.springframework.util.Assert; +import org.springframework.util.LinkedMultiValueMap; +import org.springframework.util.MultiValueMap; import org.springframework.web.reactive.function.BodyInserter; import org.springframework.web.server.ServerWebExchange; @@ -58,6 +62,8 @@ class DefaultEntityResponseBuilder implements EntityResponse.Builder { private final HttpHeaders headers = new HttpHeaders(); + private final MultiValueMap cookies = new LinkedMultiValueMap<>(); + private final Map hints = new HashMap<>(); @@ -74,6 +80,21 @@ class DefaultEntityResponseBuilder implements EntityResponse.Builder { return this; } + @Override + public EntityResponse.Builder cookie(ResponseCookie cookie) { + Assert.notNull(cookie, "'cookie' must not be null"); + this.cookies.add(cookie.getName(), cookie); + return this; + } + + @Override + public EntityResponse.Builder cookies( + Consumer> cookiesConsumer) { + Assert.notNull(cookiesConsumer, "'cookiesConsumer' must not be null"); + cookiesConsumer.accept(this.cookies); + return this; + } + @Override public EntityResponse.Builder header(String headerName, String... headerValues) { for (String headerValue : headerValues) { @@ -161,8 +182,8 @@ class DefaultEntityResponseBuilder implements EntityResponse.Builder { @Override public Mono> build() { - return Mono.just(new DefaultEntityResponse(this.status, this.headers, this.entity, - this.inserter, this.hints)); + return Mono.just(new DefaultEntityResponse(this.status, this.headers, this.cookies, + this.entity, this.inserter, this.hints)); } @@ -177,10 +198,11 @@ class DefaultEntityResponseBuilder implements EntityResponse.Builder { private final Map hints; - public DefaultEntityResponse(HttpStatus statusCode, HttpHeaders headers, T entity, + public DefaultEntityResponse(HttpStatus statusCode, HttpHeaders headers, + MultiValueMap cookies, T entity, BodyInserter inserter, Map hints) { - super(statusCode, headers); + super(statusCode, headers, cookies); this.entity = entity; this.inserter = inserter; this.hints = hints; diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/function/server/DefaultRenderingResponseBuilder.java b/spring-webflux/src/main/java/org/springframework/web/reactive/function/server/DefaultRenderingResponseBuilder.java index 6633bc8c43b..ce9d1f92e0c 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/function/server/DefaultRenderingResponseBuilder.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/function/server/DefaultRenderingResponseBuilder.java @@ -22,6 +22,7 @@ import java.util.Collections; import java.util.LinkedHashMap; import java.util.Locale; import java.util.Map; +import java.util.function.Consumer; import java.util.stream.Stream; import reactor.core.publisher.Flux; @@ -32,9 +33,12 @@ import org.springframework.core.Conventions; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; +import org.springframework.http.ResponseCookie; import org.springframework.http.server.reactive.ServerHttpResponse; import org.springframework.lang.Nullable; import org.springframework.util.Assert; +import org.springframework.util.LinkedMultiValueMap; +import org.springframework.util.MultiValueMap; import org.springframework.web.reactive.result.view.ViewResolver; import org.springframework.web.server.ServerWebExchange; @@ -52,6 +56,8 @@ class DefaultRenderingResponseBuilder implements RenderingResponse.Builder { private final HttpHeaders headers = new HttpHeaders(); + private final MultiValueMap cookies = new LinkedMultiValueMap<>(); + private final Map model = new LinkedHashMap<>(); @@ -67,6 +73,20 @@ class DefaultRenderingResponseBuilder implements RenderingResponse.Builder { return this; } + @Override + public RenderingResponse.Builder cookie(ResponseCookie cookie) { + Assert.notNull(cookie, "'cookie' must not be null"); + this.cookies.add(cookie.getName(), cookie); + return this; + } + + @Override + public RenderingResponse.Builder cookies(Consumer> cookiesConsumer) { + Assert.notNull(cookiesConsumer, "'cookiesConsumer' must not be null"); + cookiesConsumer.accept(this.cookies); + return this; + } + @Override public RenderingResponse.Builder modelAttribute(Object attribute) { Assert.notNull(attribute, "'value' must not be null"); @@ -117,7 +137,8 @@ class DefaultRenderingResponseBuilder implements RenderingResponse.Builder { @Override public Mono build() { - return Mono.just(new DefaultRenderingResponse(this.status, this.headers, this.name, this.model)); + return Mono.just(new DefaultRenderingResponse(this.status, this.headers, this.cookies, + this.name, this.model)); } @@ -129,9 +150,10 @@ class DefaultRenderingResponseBuilder implements RenderingResponse.Builder { private final Map model; - public DefaultRenderingResponse(HttpStatus statusCode, HttpHeaders headers, String name, - Map model) { - super(statusCode, headers); + public DefaultRenderingResponse(HttpStatus statusCode, HttpHeaders headers, + MultiValueMap cookies, + String name, Map model) { + super(statusCode, headers, cookies); this.name = name; this.model = unmodifiableCopy(model); } diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/function/server/DefaultServerResponseBuilder.java b/spring-webflux/src/main/java/org/springframework/web/reactive/function/server/DefaultServerResponseBuilder.java index 0f3d1dc3a0f..be61fb5f0b9 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/function/server/DefaultServerResponseBuilder.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/function/server/DefaultServerResponseBuilder.java @@ -37,10 +37,14 @@ import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; +import org.springframework.http.ResponseCookie; import org.springframework.http.codec.HttpMessageWriter; import org.springframework.http.server.reactive.ServerHttpRequest; import org.springframework.http.server.reactive.ServerHttpResponse; import org.springframework.util.Assert; +import org.springframework.util.CollectionUtils; +import org.springframework.util.LinkedMultiValueMap; +import org.springframework.util.MultiValueMap; import org.springframework.web.reactive.function.BodyInserter; import org.springframework.web.reactive.function.BodyInserters; import org.springframework.web.server.ServerWebExchange; @@ -57,6 +61,8 @@ class DefaultServerResponseBuilder implements ServerResponse.BodyBuilder { private final HttpHeaders headers = new HttpHeaders(); + private final MultiValueMap cookies = new LinkedMultiValueMap<>(); + private final Map hints = new HashMap<>(); @@ -80,6 +86,21 @@ class DefaultServerResponseBuilder implements ServerResponse.BodyBuilder { return this; } + @Override + public ServerResponse.BodyBuilder cookie(ResponseCookie cookie) { + Assert.notNull(cookie, "'cookie' must not be null"); + this.cookies.add(cookie.getName(), cookie); + return this; + } + + @Override + public ServerResponse.BodyBuilder cookies( + Consumer> cookiesConsumer) { + Assert.notNull(cookiesConsumer, "'cookiesConsumer' must not be null"); + cookiesConsumer.accept(this.cookies); + return this; + } + @Override public ServerResponse.BodyBuilder allow(HttpMethod... allowedMethods) { this.headers.setAllow(new LinkedHashSet<>(Arrays.asList(allowedMethods))); @@ -166,7 +187,8 @@ class DefaultServerResponseBuilder implements ServerResponse.BodyBuilder { BiFunction> writeFunction) { Assert.notNull(writeFunction, "'writeFunction' must not be null"); - return Mono.just(new WriterFunctionServerResponse(this.statusCode, this.headers, writeFunction)); + return Mono.just(new WriterFunctionServerResponse(this.statusCode, this.headers, + this.cookies, writeFunction)); } @Override @@ -214,7 +236,8 @@ class DefaultServerResponseBuilder implements ServerResponse.BodyBuilder { @Override public Mono body(BodyInserter inserter) { Assert.notNull(inserter, "'inserter' must not be null"); - return Mono.just(new BodyInserterServerResponse<>(this.statusCode, this.headers, inserter, this.hints)); + return Mono.just(new BodyInserterServerResponse<>(this.statusCode, this.headers, + this.cookies, inserter, this.hints)); } @Override @@ -248,9 +271,13 @@ class DefaultServerResponseBuilder implements ServerResponse.BodyBuilder { private final HttpHeaders headers; - protected AbstractServerResponse(HttpStatus statusCode, HttpHeaders headers) { + private final MultiValueMap cookies; + + protected AbstractServerResponse(HttpStatus statusCode, HttpHeaders headers, + MultiValueMap cookies) { this.statusCode = statusCode; this.headers = readOnlyCopy(headers); + this.cookies = readOnlyCopy(cookies); } private static HttpHeaders readOnlyCopy(HttpHeaders headers) { @@ -259,6 +286,12 @@ class DefaultServerResponseBuilder implements ServerResponse.BodyBuilder { return HttpHeaders.readOnlyHttpHeaders(copy); } + private static MultiValueMap readOnlyCopy(MultiValueMap map) { + MultiValueMap copy = new LinkedMultiValueMap<>(); + copy.putAll(map); + return CollectionUtils.unmodifiableMultiValueMap(copy); + } + @Override public final HttpStatus statusCode() { return this.statusCode; @@ -269,16 +302,23 @@ class DefaultServerResponseBuilder implements ServerResponse.BodyBuilder { return this.headers; } + @Override + public MultiValueMap cookies() { + return this.cookies; + } + protected void writeStatusAndHeaders(ServerHttpResponse response) { response.setStatusCode(this.statusCode); - HttpHeaders responseHeaders = response.getHeaders(); - HttpHeaders headers = headers(); - if (!headers.isEmpty()) { - headers.entrySet().stream() - .filter(entry -> !responseHeaders.containsKey(entry.getKey())) - .forEach(entry -> responseHeaders - .put(entry.getKey(), entry.getValue())); + copy(this.headers, response.getHeaders()); + copy(this.cookies, response.getCookies()); + } + + private static void copy(MultiValueMap src, MultiValueMap dst) { + if (!src.isEmpty()) { + src.entrySet().stream() + .filter(entry -> !dst.containsKey(entry.getKey())) + .forEach(entry -> dst.put(entry.getKey(), entry.getValue())); } } } @@ -289,9 +329,10 @@ class DefaultServerResponseBuilder implements ServerResponse.BodyBuilder { private final BiFunction> writeFunction; public WriterFunctionServerResponse(HttpStatus statusCode, HttpHeaders headers, + MultiValueMap cookies, BiFunction> writeFunction) { - super(statusCode, headers); + super(statusCode, headers, cookies); this.writeFunction = writeFunction; } @@ -310,9 +351,10 @@ class DefaultServerResponseBuilder implements ServerResponse.BodyBuilder { private final Map hints; public BodyInserterServerResponse(HttpStatus statusCode, HttpHeaders headers, + MultiValueMap cookies, BodyInserter inserter, Map hints) { - super(statusCode, headers); + super(statusCode, headers, cookies); this.inserter = inserter; this.hints = hints; } diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/function/server/EntityResponse.java b/spring-webflux/src/main/java/org/springframework/web/reactive/function/server/EntityResponse.java index a01b8dd857d..7b209310ad6 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/function/server/EntityResponse.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/function/server/EntityResponse.java @@ -19,6 +19,7 @@ package org.springframework.web.reactive.function.server; import java.net.URI; import java.time.ZonedDateTime; import java.util.Set; +import java.util.function.Consumer; import org.reactivestreams.Publisher; import reactor.core.publisher.Mono; @@ -29,8 +30,10 @@ import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; +import org.springframework.http.ResponseCookie; import org.springframework.http.codec.json.Jackson2CodecSupport; import org.springframework.http.server.reactive.ServerHttpResponse; +import org.springframework.util.MultiValueMap; import org.springframework.web.reactive.function.BodyInserter; import org.springframework.web.reactive.function.BodyInserters; @@ -123,6 +126,24 @@ public interface EntityResponse extends ServerResponse { */ Builder status(HttpStatus status); + /** + * Add the given cookie to the response. + * @param cookie the cookie to add + * @return this builder + */ + Builder cookie(ResponseCookie cookie); + + /** + * Manipulate this response's cookies with the given consumer. The + * cookies provided to the consumer are "live", so that the consumer can be used to + * {@linkplain MultiValueMap#set(Object, Object) overwrite} existing cookies, + * {@linkplain MultiValueMap#remove(Object) remove} cookies, or use any of the other + * {@link MultiValueMap} methods. + * @param cookiesConsumer a function that consumes the cookies + * @return this builder + */ + Builder cookies(Consumer> cookiesConsumer); + /** * Set the set of allowed {@link HttpMethod HTTP methods}, as specified * by the {@code Allow} header. diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/function/server/RenderingResponse.java b/spring-webflux/src/main/java/org/springframework/web/reactive/function/server/RenderingResponse.java index 56773870e8b..f1212559d85 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/function/server/RenderingResponse.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/function/server/RenderingResponse.java @@ -18,13 +18,16 @@ package org.springframework.web.reactive.function.server; import java.util.Collection; import java.util.Map; +import java.util.function.Consumer; import reactor.core.publisher.Mono; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseCookie; import org.springframework.lang.Nullable; import org.springframework.util.Assert; +import org.springframework.util.MultiValueMap; /** * Rendering-specific subtype of {@link ServerResponse} that exposes model and template data. @@ -139,6 +142,24 @@ public interface RenderingResponse extends ServerResponse { */ Builder status(HttpStatus status); + /** + * Add the given cookie to the response. + * @param cookie the cookie to add + * @return this builder + */ + Builder cookie(ResponseCookie cookie); + + /** + * Manipulate this response's cookies with the given consumer. The + * cookies provided to the consumer are "live", so that the consumer can be used to + * {@linkplain MultiValueMap#set(Object, Object) overwrite} existing cookies, + * {@linkplain MultiValueMap#remove(Object) remove} cookies, or use any of the other + * {@link MultiValueMap} methods. + * @param cookiesConsumer a function that consumes the cookies + * @return this builder + */ + Builder cookies(Consumer> cookiesConsumer); + /** * Build the response. * @return the built response diff --git a/spring-webflux/src/main/java/org/springframework/web/reactive/function/server/ServerResponse.java b/spring-webflux/src/main/java/org/springframework/web/reactive/function/server/ServerResponse.java index 22b2dbb27c4..445e86328dd 100644 --- a/spring-webflux/src/main/java/org/springframework/web/reactive/function/server/ServerResponse.java +++ b/spring-webflux/src/main/java/org/springframework/web/reactive/function/server/ServerResponse.java @@ -34,10 +34,12 @@ import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; +import org.springframework.http.ResponseCookie; import org.springframework.http.codec.HttpMessageWriter; import org.springframework.http.codec.json.Jackson2CodecSupport; import org.springframework.http.server.reactive.ServerHttpResponse; import org.springframework.util.Assert; +import org.springframework.util.MultiValueMap; import org.springframework.web.reactive.function.BodyInserter; import org.springframework.web.reactive.function.BodyInserters; import org.springframework.web.reactive.result.view.ViewResolver; @@ -66,6 +68,11 @@ public interface ServerResponse { */ HttpHeaders headers(); + /** + * Return the cookies of this response. + */ + MultiValueMap cookies(); + /** * Write this response to the given web exchange. * @param exchange the web exchange to write to @@ -219,6 +226,24 @@ public interface ServerResponse { */ B headers(Consumer headersConsumer); + /** + * Add the given cookie to the response. + * @param cookie the cookie to add + * @return this builder + */ + B cookie(ResponseCookie cookie); + + /** + * Manipulate this response's cookies with the given consumer. The + * cookies provided to the consumer are "live", so that the consumer can be used to + * {@linkplain MultiValueMap#set(Object, Object) overwrite} existing cookies, + * {@linkplain MultiValueMap#remove(Object) remove} cookies, or use any of the other + * {@link MultiValueMap} methods. + * @param cookiesConsumer a function that consumes the cookies + * @return this builder + */ + B cookies(Consumer> cookiesConsumer); + /** * Set the set of allowed {@link HttpMethod HTTP methods}, as specified * by the {@code Allow} header. diff --git a/spring-webflux/src/test/java/org/springframework/web/reactive/function/server/DefaultEntityResponseBuilderTests.java b/spring-webflux/src/test/java/org/springframework/web/reactive/function/server/DefaultEntityResponseBuilderTests.java index 40b552acc06..e889329fdfd 100644 --- a/spring-webflux/src/test/java/org/springframework/web/reactive/function/server/DefaultEntityResponseBuilderTests.java +++ b/spring-webflux/src/test/java/org/springframework/web/reactive/function/server/DefaultEntityResponseBuilderTests.java @@ -39,17 +39,19 @@ import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; +import org.springframework.http.ResponseCookie; import org.springframework.http.codec.EncoderHttpMessageWriter; import org.springframework.http.codec.HttpMessageWriter; import org.springframework.http.server.reactive.ServerHttpResponse; import org.springframework.mock.http.server.reactive.test.MockServerHttpRequest; import org.springframework.mock.web.test.server.MockServerWebExchange; +import org.springframework.util.LinkedMultiValueMap; +import org.springframework.util.MultiValueMap; import org.springframework.web.reactive.function.BodyInserter; import org.springframework.web.reactive.result.view.ViewResolver; import static java.nio.charset.StandardCharsets.UTF_8; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertSame; +import static org.junit.Assert.*; /** * @author Arjen Poutsma @@ -176,6 +178,18 @@ public class DefaultEntityResponseBuilderTests { .verify(); } + @Test + public void cookies() throws Exception { + MultiValueMap newCookies = new LinkedMultiValueMap<>(); + newCookies.add("name", ResponseCookie.from("name", "value").build()); + Mono> result = + EntityResponse.fromObject("foo").cookies(cookies -> cookies.addAll(newCookies)).build(); + StepVerifier.create(result) + .expectNextMatches(response -> newCookies.equals(response.cookies())) + .expectComplete() + .verify(); + } + @Test public void bodyInserter() throws Exception { String body = "foo"; diff --git a/spring-webflux/src/test/java/org/springframework/web/reactive/function/server/DefaultRenderingResponseTests.java b/spring-webflux/src/test/java/org/springframework/web/reactive/function/server/DefaultRenderingResponseTests.java index 25bdccf8bbc..801d67a46c5 100644 --- a/spring-webflux/src/test/java/org/springframework/web/reactive/function/server/DefaultRenderingResponseTests.java +++ b/spring-webflux/src/test/java/org/springframework/web/reactive/function/server/DefaultRenderingResponseTests.java @@ -28,8 +28,11 @@ import reactor.core.publisher.Mono; import reactor.test.StepVerifier; import org.springframework.http.HttpHeaders; +import org.springframework.http.ResponseCookie; import org.springframework.mock.http.server.reactive.test.MockServerHttpRequest; import org.springframework.mock.web.test.server.MockServerWebExchange; +import org.springframework.util.LinkedMultiValueMap; +import org.springframework.util.MultiValueMap; import org.springframework.web.reactive.result.view.View; import org.springframework.web.reactive.result.view.ViewResolver; @@ -103,6 +106,19 @@ public class DefaultRenderingResponseTests { .verify(); } + @Test + public void cookies() throws Exception { + MultiValueMap newCookies = new LinkedMultiValueMap<>(); + newCookies.add("name", ResponseCookie.from("name", "value").build()); + Mono result = + RenderingResponse.create("foo").cookies(cookies -> cookies.addAll(newCookies)).build(); + StepVerifier.create(result) + .expectNextMatches(response -> newCookies.equals(response.cookies())) + .expectComplete() + .verify(); + } + + @Test public void render() throws Exception { Map model = Collections.singletonMap("foo", "bar"); diff --git a/spring-webflux/src/test/java/org/springframework/web/reactive/function/server/DefaultServerResponseBuilderTests.java b/spring-webflux/src/test/java/org/springframework/web/reactive/function/server/DefaultServerResponseBuilderTests.java index 9cd606e8fac..a116cb6a305 100644 --- a/spring-webflux/src/test/java/org/springframework/web/reactive/function/server/DefaultServerResponseBuilderTests.java +++ b/spring-webflux/src/test/java/org/springframework/web/reactive/function/server/DefaultServerResponseBuilderTests.java @@ -32,7 +32,10 @@ import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; +import org.springframework.http.ResponseCookie; import org.springframework.mock.http.server.reactive.test.MockServerHttpResponse; +import org.springframework.util.LinkedMultiValueMap; +import org.springframework.util.MultiValueMap; import org.springframework.web.server.ServerWebExchange; import static org.junit.Assert.*; @@ -264,10 +267,25 @@ public class DefaultServerResponseBuilderTests { } + @Test + public void cookies() throws Exception { + MultiValueMap newCookies = new LinkedMultiValueMap<>(); + newCookies.add("name", ResponseCookie.from("name", "value").build()); + Mono result = + ServerResponse.ok().cookies(cookies -> cookies.addAll(newCookies)).build(); + StepVerifier.create(result) + .expectNextMatches(response -> newCookies.equals(response.cookies())) + .expectComplete() + .verify(); + } + @Test public void build() throws Exception { + ResponseCookie cookie = ResponseCookie.from("name", "value").build(); Mono - result = ServerResponse.status(HttpStatus.CREATED).header("MyKey", "MyValue").build(); + result = ServerResponse.status(HttpStatus.CREATED) + .header("MyKey", "MyValue") + .cookie(cookie).build(); ServerWebExchange exchange = mock(ServerWebExchange.class); MockServerHttpResponse response = new MockServerHttpResponse(); @@ -278,6 +296,7 @@ public class DefaultServerResponseBuilderTests { assertEquals(HttpStatus.CREATED, response.getStatusCode()); assertEquals("MyValue", response.getHeaders().getFirst("MyKey")); + assertEquals("value", response.getCookies().getFirst("name").getValue()); StepVerifier.create(response.getBody()).expectComplete().verify(); } diff --git a/spring-webflux/src/test/java/org/springframework/web/reactive/function/server/RouterFunctionsTests.java b/spring-webflux/src/test/java/org/springframework/web/reactive/function/server/RouterFunctionsTests.java index 835069f0bf3..b597898862b 100644 --- a/spring-webflux/src/test/java/org/springframework/web/reactive/function/server/RouterFunctionsTests.java +++ b/spring-webflux/src/test/java/org/springframework/web/reactive/function/server/RouterFunctionsTests.java @@ -25,9 +25,12 @@ import reactor.test.StepVerifier; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseCookie; import org.springframework.http.server.reactive.HttpHandler; import org.springframework.mock.http.server.reactive.test.MockServerHttpRequest; import org.springframework.mock.http.server.reactive.test.MockServerHttpResponse; +import org.springframework.util.LinkedMultiValueMap; +import org.springframework.util.MultiValueMap; import org.springframework.web.server.ResponseStatusException; import org.springframework.web.server.ServerWebExchange; import org.springframework.web.server.WebFilter; @@ -197,6 +200,11 @@ public class RouterFunctionsTests { return new HttpHeaders(); } + @Override + public MultiValueMap cookies() { + return new LinkedMultiValueMap<>(); + } + @Override public Mono writeTo(ServerWebExchange exchange, Context context) { @@ -230,6 +238,11 @@ public class RouterFunctionsTests { return new HttpHeaders(); } + @Override + public MultiValueMap cookies() { + return new LinkedMultiValueMap<>(); + } + @Override public Mono writeTo(ServerWebExchange exchange, Context context) {