Skip to content

Commit e6c2d44

Browse files
committed
Better integrate ResponseCookie in CookieLocaleResolver
Improve ResponseCookie to allow an existing instance to be mutated and also to set the cookie value through the builder. This allows CookieLocaleResolver to avoid duplicating all the fields of ResponseCookie and to have only a ResponseCookie field instead. Closes gh-28779
1 parent 075fccc commit e6c2d44

File tree

2 files changed

+165
-140
lines changed

2 files changed

+165
-140
lines changed

spring-web/src/main/java/org/springframework/http/ResponseCookie.java

Lines changed: 136 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ public final class ResponseCookie extends HttpCookie {
5454
/**
5555
* Private constructor. See {@link #from(String, String)}.
5656
*/
57-
private ResponseCookie(String name, String value, Duration maxAge, @Nullable String domain,
57+
private ResponseCookie(String name, @Nullable String value, Duration maxAge, @Nullable String domain,
5858
@Nullable String path, boolean secure, boolean httpOnly, @Nullable String sameSite) {
5959

6060
super(name, value);
@@ -128,6 +128,19 @@ public String getSameSite() {
128128
return this.sameSite;
129129
}
130130

131+
/**
132+
* Return a builder pre-populated with values from {@code "this"} instance.
133+
* @since 6.0
134+
*/
135+
public ResponseCookieBuilder mutate() {
136+
return new DefaultResponseCookieBuilder(getName(), getValue(), false)
137+
.maxAge(this.maxAge)
138+
.domain(this.domain)
139+
.path(this.path)
140+
.secure(this.secure)
141+
.httpOnly(this.httpOnly)
142+
.sameSite(this.sameSite);
143+
}
131144

132145
@Override
133146
public boolean equals(@Nullable Object other) {
@@ -179,6 +192,18 @@ public String toString() {
179192
}
180193

181194

195+
/**
196+
* Factory method to obtain a builder for a server-defined cookie, given its
197+
* name only, and where the value as well as other attributes can be set
198+
* later via builder methods.
199+
* @param name the cookie name
200+
* @return a builder to create the cookie with
201+
* @since 6.0
202+
*/
203+
public static ResponseCookieBuilder from(final String name) {
204+
return new DefaultResponseCookieBuilder(name, null, false);
205+
}
206+
182207
/**
183208
* Factory method to obtain a builder for a server-defined cookie that starts
184209
* with a name-value pair and may also include attributes.
@@ -187,7 +212,7 @@ public String toString() {
187212
* @return a builder to create the cookie with
188213
*/
189214
public static ResponseCookieBuilder from(final String name, final String value) {
190-
return from(name, value, false);
215+
return new DefaultResponseCookieBuilder(name, value, false);
191216
}
192217

193218
/**
@@ -201,90 +226,7 @@ public static ResponseCookieBuilder from(final String name, final String value)
201226
* @since 5.2.5
202227
*/
203228
public static ResponseCookieBuilder fromClientResponse(final String name, final String value) {
204-
return from(name, value, true);
205-
}
206-
207-
208-
private static ResponseCookieBuilder from(final String name, final String value, boolean lenient) {
209-
210-
return new ResponseCookieBuilder() {
211-
212-
private Duration maxAge = Duration.ofSeconds(-1);
213-
214-
@Nullable
215-
private String domain;
216-
217-
@Nullable
218-
private String path;
219-
220-
private boolean secure;
221-
222-
private boolean httpOnly;
223-
224-
@Nullable
225-
private String sameSite;
226-
227-
@Override
228-
public ResponseCookieBuilder maxAge(Duration maxAge) {
229-
this.maxAge = maxAge;
230-
return this;
231-
}
232-
233-
@Override
234-
public ResponseCookieBuilder maxAge(long maxAgeSeconds) {
235-
this.maxAge = maxAgeSeconds >= 0 ? Duration.ofSeconds(maxAgeSeconds) : Duration.ofSeconds(-1);
236-
return this;
237-
}
238-
239-
@Override
240-
public ResponseCookieBuilder domain(@Nullable String domain) {
241-
this.domain = initDomain(domain);
242-
return this;
243-
}
244-
245-
@Nullable
246-
private String initDomain(@Nullable String domain) {
247-
if (lenient && StringUtils.hasLength(domain)) {
248-
String str = domain.trim();
249-
if (str.startsWith("\"") && str.endsWith("\"")) {
250-
if (str.substring(1, str.length() - 1).trim().isEmpty()) {
251-
return null;
252-
}
253-
}
254-
}
255-
return domain;
256-
}
257-
258-
@Override
259-
public ResponseCookieBuilder path(@Nullable String path) {
260-
this.path = path;
261-
return this;
262-
}
263-
264-
@Override
265-
public ResponseCookieBuilder secure(boolean secure) {
266-
this.secure = secure;
267-
return this;
268-
}
269-
270-
@Override
271-
public ResponseCookieBuilder httpOnly(boolean httpOnly) {
272-
this.httpOnly = httpOnly;
273-
return this;
274-
}
275-
276-
@Override
277-
public ResponseCookieBuilder sameSite(@Nullable String sameSite) {
278-
this.sameSite = sameSite;
279-
return this;
280-
}
281-
282-
@Override
283-
public ResponseCookie build() {
284-
return new ResponseCookie(name, value, this.maxAge, this.domain, this.path,
285-
this.secure, this.httpOnly, this.sameSite);
286-
}
287-
};
229+
return new DefaultResponseCookieBuilder(name, value, true);
288230
}
289231

290232

@@ -293,6 +235,12 @@ public ResponseCookie build() {
293235
*/
294236
public interface ResponseCookieBuilder {
295237

238+
/**
239+
* Set the cookie value.
240+
* @since 6.0
241+
*/
242+
ResponseCookieBuilder value(@Nullable String value);
243+
296244
/**
297245
* Set the cookie "Max-Age" attribute.
298246
*
@@ -429,4 +377,106 @@ public static void validatePath(@Nullable String path) {
429377
}
430378
}
431379

380+
381+
/**
382+
* Default implementation of {@link ResponseCookieBuilder}.
383+
*/
384+
private static class DefaultResponseCookieBuilder implements ResponseCookieBuilder {
385+
386+
private final String name;
387+
388+
@Nullable
389+
private String value;
390+
391+
private final boolean lenient;
392+
393+
private Duration maxAge = Duration.ofSeconds(-1);
394+
395+
@Nullable
396+
private String domain;
397+
398+
@Nullable
399+
private String path;
400+
401+
private boolean secure;
402+
403+
private boolean httpOnly;
404+
405+
@Nullable
406+
private String sameSite;
407+
408+
public DefaultResponseCookieBuilder(String name, @Nullable String value, boolean lenient) {
409+
this.name = name;
410+
this.value = value;
411+
this.lenient = lenient;
412+
}
413+
414+
@Override
415+
public ResponseCookieBuilder value(@Nullable String value) {
416+
this.value = value;
417+
return this;
418+
}
419+
420+
@Override
421+
public ResponseCookieBuilder maxAge(Duration maxAge) {
422+
this.maxAge = maxAge;
423+
return this;
424+
}
425+
426+
@Override
427+
public ResponseCookieBuilder maxAge(long maxAgeSeconds) {
428+
this.maxAge = (maxAgeSeconds >= 0 ? Duration.ofSeconds(maxAgeSeconds) : Duration.ofSeconds(-1));
429+
return this;
430+
}
431+
432+
@Override
433+
public ResponseCookieBuilder domain(@Nullable String domain) {
434+
this.domain = initDomain(domain);
435+
return this;
436+
}
437+
438+
@Nullable
439+
private String initDomain(@Nullable String domain) {
440+
if (this.lenient && StringUtils.hasLength(domain)) {
441+
String str = domain.trim();
442+
if (str.startsWith("\"") && str.endsWith("\"")) {
443+
if (str.substring(1, str.length() - 1).trim().isEmpty()) {
444+
return null;
445+
}
446+
}
447+
}
448+
return domain;
449+
}
450+
451+
@Override
452+
public ResponseCookieBuilder path(@Nullable String path) {
453+
this.path = path;
454+
return this;
455+
}
456+
457+
@Override
458+
public ResponseCookieBuilder secure(boolean secure) {
459+
this.secure = secure;
460+
return this;
461+
}
462+
463+
@Override
464+
public ResponseCookieBuilder httpOnly(boolean httpOnly) {
465+
this.httpOnly = httpOnly;
466+
return this;
467+
}
468+
469+
@Override
470+
public ResponseCookieBuilder sameSite(@Nullable String sameSite) {
471+
this.sameSite = sameSite;
472+
return this;
473+
}
474+
475+
@Override
476+
public ResponseCookie build() {
477+
return new ResponseCookie(this.name, this.value, this.maxAge,
478+
this.domain, this.path, this.secure, this.httpOnly, this.sameSite);
479+
}
480+
}
481+
432482
}

0 commit comments

Comments
 (0)