forked from spring-projects/spring-framework
-
Notifications
You must be signed in to change notification settings - Fork 4
/
ServerResponse.java
540 lines (476 loc) · 17.9 KB
/
ServerResponse.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
/*
* Copyright 2002-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.web.reactive.function.server;
import java.net.URI;
import java.time.Instant;
import java.time.ZonedDateTime;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.BiFunction;
import java.util.function.Consumer;
import org.reactivestreams.Publisher;
import reactor.core.publisher.Mono;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.core.ReactiveAdapterRegistry;
import org.springframework.http.CacheControl;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.HttpStatusCode;
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.MultiValueMap;
import org.springframework.web.ErrorResponse;
import org.springframework.web.reactive.function.BodyInserter;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.result.view.ViewResolver;
import org.springframework.web.server.ServerWebExchange;
/**
* Represents a typed server-side HTTP response, as returned
* by a {@linkplain HandlerFunction handler function} or
* {@linkplain HandlerFilterFunction filter function}.
*
* @author Arjen Poutsma
* @author Juergen Hoeller
* @author Sebastien Deleuze
* @since 5.0
*/
public interface ServerResponse {
/**
* Return the status code of this response.
* @return the status as an HttpStatusCode value
*/
HttpStatusCode statusCode();
/**
* Return the status code of this response as integer.
* @return the status as an integer
* @since 5.2
* @deprecated as of 6.0, in favor of {@link #statusCode()}
*/
@Deprecated(since = "6.0")
int rawStatusCode();
/**
* Return the headers of this response.
*/
HttpHeaders headers();
/**
* Return the cookies of this response.
*/
MultiValueMap<String, ResponseCookie> cookies();
/**
* Write this response to the given web exchange.
* @param exchange the web exchange to write to
* @param context the context to use when writing
* @return {@code Mono<Void>} to indicate when writing is complete
*/
Mono<Void> writeTo(ServerWebExchange exchange, Context context);
// Static methods
/**
* Create a builder with the status code and headers of the given response.
* @param other the response to copy the status and headers from
* @return the created builder
*/
static BodyBuilder from(ServerResponse other) {
return new DefaultServerResponseBuilder(other);
}
/**
* Create a {@code ServerResponse} from the given {@link ErrorResponse}.
* @param response the {@link ErrorResponse} to initialize from
* @return {@code Mono} with the built response
* @since 6.0
*/
static Mono<ServerResponse> from(ErrorResponse response) {
return status(response.getStatusCode())
.headers(headers -> headers.putAll(response.getHeaders()))
.bodyValue(response.getBody());
}
/**
* Create a builder with the given HTTP status.
* @param status the response status
* @return the created builder
*/
static BodyBuilder status(HttpStatusCode status) {
return new DefaultServerResponseBuilder(status);
}
/**
* Create a builder with the given HTTP status.
* @param status the response status
* @return the created builder
* @since 5.0.3
*/
static BodyBuilder status(int status) {
return new DefaultServerResponseBuilder(HttpStatusCode.valueOf(status));
}
/**
* Create a builder with the status set to {@linkplain HttpStatus#OK 200 OK}.
* @return the created builder
*/
static BodyBuilder ok() {
return status(HttpStatus.OK);
}
/**
* Create a new builder with a {@linkplain HttpStatus#CREATED 201 Created} status
* and a location header set to the given URI.
* @param location the location URI
* @return the created builder
*/
static BodyBuilder created(URI location) {
BodyBuilder builder = status(HttpStatus.CREATED);
return builder.location(location);
}
/**
* Create a builder with an {@linkplain HttpStatus#ACCEPTED 202 Accepted} status.
* @return the created builder
*/
static BodyBuilder accepted() {
return status(HttpStatus.ACCEPTED);
}
/**
* Create a builder with a {@linkplain HttpStatus#NO_CONTENT 204 No Content} status.
* @return the created builder
*/
static HeadersBuilder<?> noContent() {
return status(HttpStatus.NO_CONTENT);
}
/**
* Create a builder with a {@linkplain HttpStatus#SEE_OTHER 303 See Other}
* status and a location header set to the given URI.
* @param location the location URI
* @return the created builder
*/
static BodyBuilder seeOther(URI location) {
BodyBuilder builder = status(HttpStatus.SEE_OTHER);
return builder.location(location);
}
/**
* Create a builder with a {@linkplain HttpStatus#TEMPORARY_REDIRECT 307 Temporary Redirect}
* status and a location header set to the given URI.
* @param location the location URI
* @return the created builder
*/
static BodyBuilder temporaryRedirect(URI location) {
BodyBuilder builder = status(HttpStatus.TEMPORARY_REDIRECT);
return builder.location(location);
}
/**
* Create a builder with a {@linkplain HttpStatus#PERMANENT_REDIRECT 308 Permanent Redirect}
* status and a location header set to the given URI.
* @param location the location URI
* @return the created builder
*/
static BodyBuilder permanentRedirect(URI location) {
BodyBuilder builder = status(HttpStatus.PERMANENT_REDIRECT);
return builder.location(location);
}
/**
* Create a builder with a {@linkplain HttpStatus#BAD_REQUEST 400 Bad Request} status.
* @return the created builder
*/
static BodyBuilder badRequest() {
return status(HttpStatus.BAD_REQUEST);
}
/**
* Create a builder with a {@linkplain HttpStatus#NOT_FOUND 404 Not Found} status.
* @return the created builder
*/
static HeadersBuilder<?> notFound() {
return status(HttpStatus.NOT_FOUND);
}
/**
* Create a builder with an
* {@linkplain HttpStatus#UNPROCESSABLE_ENTITY 422 Unprocessable Entity} status.
* @return the created builder
*/
static BodyBuilder unprocessableEntity() {
return status(HttpStatus.UNPROCESSABLE_ENTITY);
}
/**
* Defines a builder that adds headers to the response.
* @param <B> the builder subclass
*/
interface HeadersBuilder<B extends HeadersBuilder<B>> {
/**
* Add the given header value(s) under the given name.
* @param headerName the header name
* @param headerValues the header value(s)
* @return this builder
* @see HttpHeaders#add(String, String)
*/
B header(String headerName, String... headerValues);
/**
* Manipulate this response's headers with the given consumer. The
* headers provided to the consumer are "live", so that the consumer can be used to
* {@linkplain HttpHeaders#set(String, String) overwrite} existing header values,
* {@linkplain HttpHeaders#remove(Object) remove} values, or use any of the other
* {@link HttpHeaders} methods.
* @param headersConsumer a function that consumes the {@code HttpHeaders}
* @return this builder
*/
B headers(Consumer<HttpHeaders> 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<MultiValueMap<String, ResponseCookie>> cookiesConsumer);
/**
* Set the set of allowed {@link HttpMethod HTTP methods}, as specified
* by the {@code Allow} header.
* @param allowedMethods the allowed methods
* @return this builder
* @see HttpHeaders#setAllow(Set)
*/
B allow(HttpMethod... allowedMethods);
/**
* Set the set of allowed {@link HttpMethod HTTP methods}, as specified
* by the {@code Allow} header.
* @param allowedMethods the allowed methods
* @return this builder
* @see HttpHeaders#setAllow(Set)
*/
B allow(Set<HttpMethod> allowedMethods);
/**
* Set the entity tag of the body, as specified by the {@code ETag} header.
* @param eTag the new entity tag
* @return this builder
* @see HttpHeaders#setETag(String)
*/
B eTag(String eTag);
/**
* Set the time the resource was last changed, as specified by the
* {@code Last-Modified} header.
* @param lastModified the last modified date
* @return this builder
* @see HttpHeaders#setLastModified(long)
*/
B lastModified(ZonedDateTime lastModified);
/**
* Set the time the resource was last changed, as specified by the
* {@code Last-Modified} header.
* @param lastModified the last modified date
* @return this builder
* @since 5.1.4
* @see HttpHeaders#setLastModified(long)
*/
B lastModified(Instant lastModified);
/**
* Set the location of a resource, as specified by the {@code Location} header.
* @param location the location
* @return this builder
* @see HttpHeaders#setLocation(URI)
*/
B location(URI location);
/**
* Set the caching directives for the resource, as specified by the HTTP 1.1
* {@code Cache-Control} header.
* <p>A {@code CacheControl} instance can be built like
* {@code CacheControl.maxAge(3600).cachePublic().noTransform()}.
* @param cacheControl a builder for cache-related HTTP response headers
* @return this builder
* @see <a href="https://tools.ietf.org/html/rfc7234#section-5.2">RFC-7234 Section 5.2</a>
*/
B cacheControl(CacheControl cacheControl);
/**
* Configure one or more request header names (e.g. "Accept-Language") to
* add to the "Vary" response header to inform clients that the response is
* subject to content negotiation and variances based on the value of the
* given request headers. The configured request header names are added only
* if not already present in the response "Vary" header.
* @param requestHeaders request header names
* @return this builder
*/
B varyBy(String... requestHeaders);
/**
* Build the response entity with no body.
*/
Mono<ServerResponse> build();
/**
* Build the response entity with no body.
* <p>The response will be committed when the given {@code voidPublisher} completes.
* @param voidPublisher the publisher to indicate when the response should be committed
*/
Mono<ServerResponse> build(Publisher<Void> voidPublisher);
/**
* Build the response entity with a custom writer function.
* @param writeFunction the function used to write to the {@link ServerWebExchange}
*/
Mono<ServerResponse> build(BiFunction<ServerWebExchange, Context, Mono<Void>> writeFunction);
}
/**
* Defines a builder that adds a body to the response.
*/
interface BodyBuilder extends HeadersBuilder<BodyBuilder> {
/**
* Set the length of the body in bytes, as specified by the
* {@code Content-Length} header.
* @param contentLength the content length
* @return this builder
* @see HttpHeaders#setContentLength(long)
*/
BodyBuilder contentLength(long contentLength);
/**
* Set the {@linkplain MediaType media type} of the body, as specified by the
* {@code Content-Type} header.
* @param contentType the content type
* @return this builder
* @see HttpHeaders#setContentType(MediaType)
*/
BodyBuilder contentType(MediaType contentType);
/**
* Add a serialization hint like {@link Jackson2CodecSupport#JSON_VIEW_HINT}
* to customize how the body will be serialized.
* @param key the hint key
* @param value the hint value
*/
BodyBuilder hint(String key, Object value);
/**
* Customize the serialization hints with the given consumer.
* @param hintsConsumer a function that consumes the hints
* @return this builder
* @since 5.1.6
*/
BodyBuilder hints(Consumer<Map<String, Object>> hintsConsumer);
/**
* Set the body of the response to the given {@code Object} and return it.
* This is a shortcut for using a {@link #body(BodyInserter)} with a
* {@linkplain BodyInserters#fromValue value inserter}.
* @param body the body of the response
* @return the built response
* @throws IllegalArgumentException if {@code body} is a
* {@link Publisher} or producer known to {@link ReactiveAdapterRegistry}
* @since 5.2
*/
Mono<ServerResponse> bodyValue(Object body);
/**
* Set the body of the response to the given {@code Object} and return it.
* This is a shortcut for using a {@link #body(BodyInserter)} with a
* {@linkplain BodyInserters#fromValue value inserter}.
* @param body the body of the response
* @param bodyType the type of the body, used to capture the generic type
* @param <T> the type of the body
* @return the built response
* @throws IllegalArgumentException if {@code body} is a
* {@link Publisher} or producer known to {@link ReactiveAdapterRegistry}
* @since 6.2
*/
<T> Mono<ServerResponse> bodyValue(T body, ParameterizedTypeReference<T> bodyType);
/**
* Set the body from the given {@code Publisher}. Shortcut for
* {@link #body(BodyInserter)} with a
* {@linkplain BodyInserters#fromPublisher Publisher inserter}.
* @param publisher the {@code Publisher} to write to the response
* @param elementClass the type of elements published
* @param <T> the type of the elements contained in the publisher
* @param <P> the type of the {@code Publisher}
* @return the built response
*/
<T, P extends Publisher<T>> Mono<ServerResponse> body(P publisher, Class<T> elementClass);
/**
* Variant of {@link #body(Publisher, Class)} that allows using any
* producer that can be resolved to {@link Publisher} via
* {@link ReactiveAdapterRegistry}.
* @param publisher the {@code Publisher} to use to write the response
* @param elementTypeRef the type of elements produced
* @param <T> the type of the elements contained in the publisher
* @param <P> the type of the {@code Publisher}
* @return the built response
*/
<T, P extends Publisher<T>> Mono<ServerResponse> body(P publisher,
ParameterizedTypeReference<T> elementTypeRef);
/**
* Variant of {@link #body(Publisher, Class)} that allows using any
* producer that can be resolved to {@link Publisher} via
* {@link ReactiveAdapterRegistry}.
* @param producer the producer to write to the request
* @param elementClass the type of elements produced
* @return the built response
* @since 5.2
*/
Mono<ServerResponse> body(Object producer, Class<?> elementClass);
/**
* Variant of {@link #body(Publisher, ParameterizedTypeReference)} that
* allows using any producer that can be resolved to {@link Publisher}
* via {@link ReactiveAdapterRegistry}.
* @param producer the producer to write to the response
* @param elementTypeRef the type of elements produced
* @return the built response
* @since 5.2
*/
Mono<ServerResponse> body(Object producer, ParameterizedTypeReference<?> elementTypeRef);
/**
* Set the body of the response to the given {@code BodyInserter} and return it.
* @param inserter the {@code BodyInserter} that writes to the response
* @return the built response
*/
Mono<ServerResponse> body(BodyInserter<?, ? super ServerHttpResponse> inserter);
/**
* Set the response body to the given {@code Object} and return it.
* As of 5.2 this method delegates to {@link #bodyValue(Object)}.
* @deprecated as of Spring Framework 5.2 in favor of {@link #bodyValue(Object)}
*/
@Deprecated
Mono<ServerResponse> syncBody(Object body);
/**
* Render the template with the given {@code name} using the given {@code modelAttributes}.
* The model attributes are mapped under a
* {@linkplain org.springframework.core.Conventions#getVariableName generated name}.
* <p><em>Note: Empty {@link Collection Collections} are not added to
* the model when using this method because we cannot correctly determine
* the true convention name.</em>
* @param name the name of the template to be rendered
* @param modelAttributes the modelAttributes used to render the template
* @return the built response
*/
Mono<ServerResponse> render(String name, Object... modelAttributes);
/**
* Render the template with the given {@code name} using the given {@code model}.
* @param name the name of the template to be rendered
* @param model the model used to render the template
* @return the built response
*/
Mono<ServerResponse> render(String name, Map<String, ?> model);
}
/**
* Defines the context used during the {@link #writeTo(ServerWebExchange, Context)}.
*/
interface Context {
/**
* Return the {@link HttpMessageWriter HttpMessageWriters} to be used for response body conversion.
* @return the list of message writers
*/
List<HttpMessageWriter<?>> messageWriters();
/**
* Return the {@link ViewResolver ViewResolvers} to be used for view name resolution.
* @return the list of view resolvers
*/
List<ViewResolver> viewResolvers();
}
}