001/* 002 * Copyright 2002-2019 the original author or authors. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * https://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.springframework.web.reactive.function.server; 018 019import java.net.URI; 020import java.time.Instant; 021import java.time.ZonedDateTime; 022import java.util.Collection; 023import java.util.List; 024import java.util.Map; 025import java.util.Set; 026import java.util.function.BiFunction; 027import java.util.function.Consumer; 028 029import org.reactivestreams.Publisher; 030import reactor.core.publisher.Mono; 031 032import org.springframework.core.ParameterizedTypeReference; 033import org.springframework.core.ReactiveAdapterRegistry; 034import org.springframework.http.CacheControl; 035import org.springframework.http.HttpHeaders; 036import org.springframework.http.HttpMethod; 037import org.springframework.http.HttpStatus; 038import org.springframework.http.MediaType; 039import org.springframework.http.ResponseCookie; 040import org.springframework.http.codec.HttpMessageWriter; 041import org.springframework.http.codec.json.Jackson2CodecSupport; 042import org.springframework.http.server.reactive.ServerHttpResponse; 043import org.springframework.util.MultiValueMap; 044import org.springframework.web.reactive.function.BodyInserter; 045import org.springframework.web.reactive.function.BodyInserters; 046import org.springframework.web.reactive.result.view.ViewResolver; 047import org.springframework.web.server.ServerWebExchange; 048 049/** 050 * Represents a typed server-side HTTP response, as returned 051 * by a {@linkplain HandlerFunction handler function} or 052 * {@linkplain HandlerFilterFunction filter function}. 053 * 054 * @author Arjen Poutsma 055 * @author Juergen Hoeller 056 * @author Sebastien Deleuze 057 * @since 5.0 058 */ 059public interface ServerResponse { 060 061 /** 062 * Return the status code of this response. 063 * @return the status as an HttpStatus enum value 064 * @throws IllegalArgumentException in case of an unknown HTTP status code 065 * @see HttpStatus#valueOf(int) 066 */ 067 HttpStatus statusCode(); 068 069 /** 070 * Return the (potentially non-standard) status code of this response. 071 * @return the status as an integer 072 * @since 5.2 073 * @see #statusCode() 074 * @see HttpStatus#resolve(int) 075 */ 076 int rawStatusCode(); 077 078 /** 079 * Return the headers of this response. 080 */ 081 HttpHeaders headers(); 082 083 /** 084 * Return the cookies of this response. 085 */ 086 MultiValueMap<String, ResponseCookie> cookies(); 087 088 /** 089 * Write this response to the given web exchange. 090 * @param exchange the web exchange to write to 091 * @param context the context to use when writing 092 * @return {@code Mono<Void>} to indicate when writing is complete 093 */ 094 Mono<Void> writeTo(ServerWebExchange exchange, Context context); 095 096 097 // Static methods 098 099 /** 100 * Create a builder with the status code and headers of the given response. 101 * @param other the response to copy the status and headers from 102 * @return the created builder 103 */ 104 static BodyBuilder from(ServerResponse other) { 105 return new DefaultServerResponseBuilder(other); 106 } 107 108 /** 109 * Create a builder with the given HTTP status. 110 * @param status the response status 111 * @return the created builder 112 */ 113 static BodyBuilder status(HttpStatus status) { 114 return new DefaultServerResponseBuilder(status); 115 } 116 117 /** 118 * Create a builder with the given HTTP status. 119 * @param status the response status 120 * @return the created builder 121 * @since 5.0.3 122 */ 123 static BodyBuilder status(int status) { 124 return new DefaultServerResponseBuilder(status); 125 } 126 127 /** 128 * Create a builder with the status set to {@linkplain HttpStatus#OK 200 OK}. 129 * @return the created builder 130 */ 131 static BodyBuilder ok() { 132 return status(HttpStatus.OK); 133 } 134 135 /** 136 * Create a new builder with a {@linkplain HttpStatus#CREATED 201 Created} status 137 * and a location header set to the given URI. 138 * @param location the location URI 139 * @return the created builder 140 */ 141 static BodyBuilder created(URI location) { 142 BodyBuilder builder = status(HttpStatus.CREATED); 143 return builder.location(location); 144 } 145 146 /** 147 * Create a builder with an {@linkplain HttpStatus#ACCEPTED 202 Accepted} status. 148 * @return the created builder 149 */ 150 static BodyBuilder accepted() { 151 return status(HttpStatus.ACCEPTED); 152 } 153 154 /** 155 * Create a builder with a {@linkplain HttpStatus#NO_CONTENT 204 No Content} status. 156 * @return the created builder 157 */ 158 static HeadersBuilder<?> noContent() { 159 return status(HttpStatus.NO_CONTENT); 160 } 161 162 /** 163 * Create a builder with a {@linkplain HttpStatus#SEE_OTHER 303 See Other} 164 * status and a location header set to the given URI. 165 * @param location the location URI 166 * @return the created builder 167 */ 168 static BodyBuilder seeOther(URI location) { 169 BodyBuilder builder = status(HttpStatus.SEE_OTHER); 170 return builder.location(location); 171 } 172 173 /** 174 * Create a builder with a {@linkplain HttpStatus#TEMPORARY_REDIRECT 307 Temporary Redirect} 175 * status and a location header set to the given URI. 176 * @param location the location URI 177 * @return the created builder 178 */ 179 static BodyBuilder temporaryRedirect(URI location) { 180 BodyBuilder builder = status(HttpStatus.TEMPORARY_REDIRECT); 181 return builder.location(location); 182 } 183 184 /** 185 * Create a builder with a {@linkplain HttpStatus#PERMANENT_REDIRECT 308 Permanent Redirect} 186 * status and a location header set to the given URI. 187 * @param location the location URI 188 * @return the created builder 189 */ 190 static BodyBuilder permanentRedirect(URI location) { 191 BodyBuilder builder = status(HttpStatus.PERMANENT_REDIRECT); 192 return builder.location(location); 193 } 194 195 /** 196 * Create a builder with a {@linkplain HttpStatus#BAD_REQUEST 400 Bad Request} status. 197 * @return the created builder 198 */ 199 static BodyBuilder badRequest() { 200 return status(HttpStatus.BAD_REQUEST); 201 } 202 203 /** 204 * Create a builder with a {@linkplain HttpStatus#NOT_FOUND 404 Not Found} status. 205 * @return the created builder 206 */ 207 static HeadersBuilder<?> notFound() { 208 return status(HttpStatus.NOT_FOUND); 209 } 210 211 /** 212 * Create a builder with an 213 * {@linkplain HttpStatus#UNPROCESSABLE_ENTITY 422 Unprocessable Entity} status. 214 * @return the created builder 215 */ 216 static BodyBuilder unprocessableEntity() { 217 return status(HttpStatus.UNPROCESSABLE_ENTITY); 218 } 219 220 221 /** 222 * Defines a builder that adds headers to the response. 223 * @param <B> the builder subclass 224 */ 225 interface HeadersBuilder<B extends HeadersBuilder<B>> { 226 227 /** 228 * Add the given header value(s) under the given name. 229 * @param headerName the header name 230 * @param headerValues the header value(s) 231 * @return this builder 232 * @see HttpHeaders#add(String, String) 233 */ 234 B header(String headerName, String... headerValues); 235 236 /** 237 * Manipulate this response's headers with the given consumer. The 238 * headers provided to the consumer are "live", so that the consumer can be used to 239 * {@linkplain HttpHeaders#set(String, String) overwrite} existing header values, 240 * {@linkplain HttpHeaders#remove(Object) remove} values, or use any of the other 241 * {@link HttpHeaders} methods. 242 * @param headersConsumer a function that consumes the {@code HttpHeaders} 243 * @return this builder 244 */ 245 B headers(Consumer<HttpHeaders> headersConsumer); 246 247 /** 248 * Add the given cookie to the response. 249 * @param cookie the cookie to add 250 * @return this builder 251 */ 252 B cookie(ResponseCookie cookie); 253 254 /** 255 * Manipulate this response's cookies with the given consumer. The 256 * cookies provided to the consumer are "live", so that the consumer can be used to 257 * {@linkplain MultiValueMap#set(Object, Object) overwrite} existing cookies, 258 * {@linkplain MultiValueMap#remove(Object) remove} cookies, or use any of the other 259 * {@link MultiValueMap} methods. 260 * @param cookiesConsumer a function that consumes the cookies 261 * @return this builder 262 */ 263 B cookies(Consumer<MultiValueMap<String, ResponseCookie>> cookiesConsumer); 264 265 /** 266 * Set the set of allowed {@link HttpMethod HTTP methods}, as specified 267 * by the {@code Allow} header. 268 * 269 * @param allowedMethods the allowed methods 270 * @return this builder 271 * @see HttpHeaders#setAllow(Set) 272 */ 273 B allow(HttpMethod... allowedMethods); 274 275 /** 276 * Set the set of allowed {@link HttpMethod HTTP methods}, as specified 277 * by the {@code Allow} header. 278 * @param allowedMethods the allowed methods 279 * @return this builder 280 * @see HttpHeaders#setAllow(Set) 281 */ 282 B allow(Set<HttpMethod> allowedMethods); 283 284 /** 285 * Set the entity tag of the body, as specified by the {@code ETag} header. 286 * @param eTag the new entity tag 287 * @return this builder 288 * @see HttpHeaders#setETag(String) 289 */ 290 B eTag(String eTag); 291 292 /** 293 * Set the time the resource was last changed, as specified by the 294 * {@code Last-Modified} header. 295 * @param lastModified the last modified date 296 * @return this builder 297 * @see HttpHeaders#setLastModified(long) 298 */ 299 B lastModified(ZonedDateTime lastModified); 300 301 /** 302 * Set the time the resource was last changed, as specified by the 303 * {@code Last-Modified} header. 304 * @param lastModified the last modified date 305 * @return this builder 306 * @since 5.1.4 307 * @see HttpHeaders#setLastModified(long) 308 */ 309 B lastModified(Instant lastModified); 310 311 /** 312 * Set the location of a resource, as specified by the {@code Location} header. 313 * @param location the location 314 * @return this builder 315 * @see HttpHeaders#setLocation(URI) 316 */ 317 B location(URI location); 318 319 /** 320 * Set the caching directives for the resource, as specified by the HTTP 1.1 321 * {@code Cache-Control} header. 322 * <p>A {@code CacheControl} instance can be built like 323 * {@code CacheControl.maxAge(3600).cachePublic().noTransform()}. 324 * @param cacheControl a builder for cache-related HTTP response headers 325 * @return this builder 326 * @see <a href="https://tools.ietf.org/html/rfc7234#section-5.2">RFC-7234 Section 5.2</a> 327 */ 328 B cacheControl(CacheControl cacheControl); 329 330 /** 331 * Configure one or more request header names (e.g. "Accept-Language") to 332 * add to the "Vary" response header to inform clients that the response is 333 * subject to content negotiation and variances based on the value of the 334 * given request headers. The configured request header names are added only 335 * if not already present in the response "Vary" header. 336 * @param requestHeaders request header names 337 * @return this builder 338 */ 339 B varyBy(String... requestHeaders); 340 341 /** 342 * Build the response entity with no body. 343 */ 344 Mono<ServerResponse> build(); 345 346 /** 347 * Build the response entity with no body. 348 * <p>The response will be committed when the given {@code voidPublisher} completes. 349 * @param voidPublisher the publisher to indicate when the response should be committed 350 */ 351 Mono<ServerResponse> build(Publisher<Void> voidPublisher); 352 353 /** 354 * Build the response entity with a custom writer function. 355 * @param writeFunction the function used to write to the {@link ServerWebExchange} 356 */ 357 Mono<ServerResponse> build(BiFunction<ServerWebExchange, Context, Mono<Void>> writeFunction); 358 } 359 360 361 /** 362 * Defines a builder that adds a body to the response. 363 */ 364 interface BodyBuilder extends HeadersBuilder<BodyBuilder> { 365 366 /** 367 * Set the length of the body in bytes, as specified by the 368 * {@code Content-Length} header. 369 * @param contentLength the content length 370 * @return this builder 371 * @see HttpHeaders#setContentLength(long) 372 */ 373 BodyBuilder contentLength(long contentLength); 374 375 /** 376 * Set the {@linkplain MediaType media type} of the body, as specified by the 377 * {@code Content-Type} header. 378 * @param contentType the content type 379 * @return this builder 380 * @see HttpHeaders#setContentType(MediaType) 381 */ 382 BodyBuilder contentType(MediaType contentType); 383 384 /** 385 * Add a serialization hint like {@link Jackson2CodecSupport#JSON_VIEW_HINT} 386 * to customize how the body will be serialized. 387 * @param key the hint key 388 * @param value the hint value 389 */ 390 BodyBuilder hint(String key, Object value); 391 392 /** 393 * Customize the serialization hints with the given consumer. 394 * @param hintsConsumer a function that consumes the hints 395 * @return this builder 396 * @since 5.1.6 397 */ 398 BodyBuilder hints(Consumer<Map<String, Object>> hintsConsumer); 399 400 /** 401 * Set the body of the response to the given {@code Object} and return it. 402 * This is a shortcut for using a {@link #body(BodyInserter)} with a 403 * {@linkplain BodyInserters#fromValue value inserter}. 404 * @param body the body of the response 405 * @return the built response 406 * @throws IllegalArgumentException if {@code body} is a 407 * {@link Publisher} or producer known to {@link ReactiveAdapterRegistry} 408 * @since 5.2 409 */ 410 Mono<ServerResponse> bodyValue(Object body); 411 412 /** 413 * Set the body from the given {@code Publisher}. Shortcut for 414 * {@link #body(BodyInserter)} with a 415 * {@linkplain BodyInserters#fromPublisher Publisher inserter}. 416 * @param publisher the {@code Publisher} to write to the response 417 * @param elementClass the type of elements published 418 * @param <T> the type of the elements contained in the publisher 419 * @param <P> the type of the {@code Publisher} 420 * @return the built response 421 */ 422 <T, P extends Publisher<T>> Mono<ServerResponse> body(P publisher, Class<T> elementClass); 423 424 /** 425 * Variant of {@link #body(Publisher, Class)} that allows using any 426 * producer that can be resolved to {@link Publisher} via 427 * {@link ReactiveAdapterRegistry}. 428 * @param publisher the {@code Publisher} to use to write the response 429 * @param elementTypeRef the type of elements produced 430 * @param <T> the type of the elements contained in the publisher 431 * @param <P> the type of the {@code Publisher} 432 * @return the built response 433 */ 434 <T, P extends Publisher<T>> Mono<ServerResponse> body(P publisher, 435 ParameterizedTypeReference<T> elementTypeRef); 436 437 /** 438 * Variant of {@link #body(Publisher, Class)} that allows using any 439 * producer that can be resolved to {@link Publisher} via 440 * {@link ReactiveAdapterRegistry}. 441 * @param producer the producer to write to the request 442 * @param elementClass the type of elements produced 443 * @return the built response 444 * @since 5.2 445 */ 446 Mono<ServerResponse> body(Object producer, Class<?> elementClass); 447 448 /** 449 * Variant of {@link #body(Publisher, ParameterizedTypeReference)} that 450 * allows using any producer that can be resolved to {@link Publisher} 451 * via {@link ReactiveAdapterRegistry}. 452 * @param producer the producer to write to the response 453 * @param elementTypeRef the type of elements produced 454 * @return the built response 455 * @since 5.2 456 */ 457 Mono<ServerResponse> body(Object producer, ParameterizedTypeReference<?> elementTypeRef); 458 459 /** 460 * Set the body of the response to the given {@code BodyInserter} and return it. 461 * @param inserter the {@code BodyInserter} that writes to the response 462 * @return the built response 463 */ 464 Mono<ServerResponse> body(BodyInserter<?, ? super ServerHttpResponse> inserter); 465 466 /** 467 * Set the response body to the given {@code Object} and return it. 468 * As of 5.2 this method delegates to {@link #bodyValue(Object)}. 469 * @deprecated as of Spring Framework 5.2 in favor of {@link #bodyValue(Object)} 470 */ 471 @Deprecated 472 Mono<ServerResponse> syncBody(Object body); 473 474 /** 475 * Render the template with the given {@code name} using the given {@code modelAttributes}. 476 * The model attributes are mapped under a 477 * {@linkplain org.springframework.core.Conventions#getVariableName generated name}. 478 * <p><em>Note: Empty {@link Collection Collections} are not added to 479 * the model when using this method because we cannot correctly determine 480 * the true convention name.</em> 481 * @param name the name of the template to be rendered 482 * @param modelAttributes the modelAttributes used to render the template 483 * @return the built response 484 */ 485 Mono<ServerResponse> render(String name, Object... modelAttributes); 486 487 /** 488 * Render the template with the given {@code name} using the given {@code model}. 489 * @param name the name of the template to be rendered 490 * @param model the model used to render the template 491 * @return the built response 492 */ 493 Mono<ServerResponse> render(String name, Map<String, ?> model); 494 } 495 496 497 /** 498 * Defines the context used during the {@link #writeTo(ServerWebExchange, Context)}. 499 */ 500 interface Context { 501 502 /** 503 * Return the {@link HttpMessageWriter HttpMessageWriters} to be used for response body conversion. 504 * @return the list of message writers 505 */ 506 List<HttpMessageWriter<?>> messageWriters(); 507 508 /** 509 * Return the {@link ViewResolver ViewResolvers} to be used for view name resolution. 510 * @return the list of view resolvers 511 */ 512 List<ViewResolver> viewResolvers(); 513 } 514 515}