001/* 002 * Copyright 2002-2020 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.client; 018 019import java.util.Collections; 020import java.util.List; 021import java.util.Optional; 022import java.util.OptionalLong; 023import java.util.function.Consumer; 024 025import reactor.core.publisher.Flux; 026import reactor.core.publisher.Mono; 027 028import org.springframework.core.ParameterizedTypeReference; 029import org.springframework.core.io.buffer.DataBuffer; 030import org.springframework.http.HttpHeaders; 031import org.springframework.http.HttpRequest; 032import org.springframework.http.HttpStatus; 033import org.springframework.http.MediaType; 034import org.springframework.http.ResponseCookie; 035import org.springframework.http.ResponseEntity; 036import org.springframework.http.client.reactive.ClientHttpResponse; 037import org.springframework.http.codec.HttpMessageReader; 038import org.springframework.http.codec.HttpMessageWriter; 039import org.springframework.util.MultiValueMap; 040import org.springframework.web.reactive.function.BodyExtractor; 041 042/** 043 * Represents an HTTP response, as returned by {@link WebClient} and also 044 * {@link ExchangeFunction}. Provides access to the response status and 045 * headers, and also methods to consume the response body. 046 * 047 * <p><strong>NOTE:</strong> When using a {@link ClientResponse} 048 * through the {@code WebClient} 049 * {@link WebClient.RequestHeadersSpec#exchange() exchange()} method, 050 * you have to make sure that the body is consumed or released by using 051 * one of the following methods: 052 * <ul> 053 * <li>{@link #body(BodyExtractor)}</li> 054 * <li>{@link #bodyToMono(Class)} or 055 * {@link #bodyToMono(ParameterizedTypeReference)}</li> 056 * <li>{@link #bodyToFlux(Class)} or 057 * {@link #bodyToFlux(ParameterizedTypeReference)}</li> 058 * <li>{@link #toEntity(Class)} or 059 * {@link #toEntity(ParameterizedTypeReference)}</li> 060 * <li>{@link #toEntityList(Class)} or 061 * {@link #toEntityList(ParameterizedTypeReference)}</li> 062* <li>{@link #toBodilessEntity()}</li> 063 * <li>{@link #releaseBody()}</li> 064 * </ul> 065 * You can also use {@code bodyToMono(Void.class)} if no response content is 066 * expected. However keep in mind the connection will be closed, instead of 067 * being placed back in the pool, if any content does arrive. This is in 068 * contrast to {@link #releaseBody()} which does consume the full body and 069 * releases any content received. 070 * 071 * @author Brian Clozel 072 * @author Arjen Poutsma 073 * @since 5.0 074 */ 075public interface ClientResponse { 076 077 /** 078 * Return the HTTP status code as an {@link HttpStatus} enum value. 079 * @return the HTTP status as an HttpStatus enum value (never {@code null}) 080 * @throws IllegalArgumentException in case of an unknown HTTP status code 081 * @since #getRawStatusCode() 082 * @see HttpStatus#valueOf(int) 083 */ 084 HttpStatus statusCode(); 085 086 /** 087 * Return the (potentially non-standard) status code of this response. 088 * @return the HTTP status as an integer value 089 * @since 5.1 090 * @see #statusCode() 091 * @see HttpStatus#resolve(int) 092 */ 093 int rawStatusCode(); 094 095 /** 096 * Return the headers of this response. 097 */ 098 Headers headers(); 099 100 /** 101 * Return the cookies of this response. 102 */ 103 MultiValueMap<String, ResponseCookie> cookies(); 104 105 /** 106 * Return the strategies used to convert the body of this response. 107 */ 108 ExchangeStrategies strategies(); 109 110 /** 111 * Extract the body with the given {@code BodyExtractor}. 112 * @param extractor the {@code BodyExtractor} that reads from the response 113 * @param <T> the type of the body returned 114 * @return the extracted body 115 */ 116 <T> T body(BodyExtractor<T, ? super ClientHttpResponse> extractor); 117 118 /** 119 * Extract the body to a {@code Mono}. 120 * @param elementClass the class of element in the {@code Mono} 121 * @param <T> the element type 122 * @return a mono containing the body of the given type {@code T} 123 */ 124 <T> Mono<T> bodyToMono(Class<? extends T> elementClass); 125 126 /** 127 * Extract the body to a {@code Mono}. 128 * @param elementTypeRef the type reference of element in the {@code Mono} 129 * @param <T> the element type 130 * @return a mono containing the body of the given type {@code T} 131 */ 132 <T> Mono<T> bodyToMono(ParameterizedTypeReference<T> elementTypeRef); 133 134 /** 135 * Extract the body to a {@code Flux}. 136 * @param elementClass the class of elements in the {@code Flux} 137 * @param <T> the element type 138 * @return a flux containing the body of the given type {@code T} 139 */ 140 <T> Flux<T> bodyToFlux(Class<? extends T> elementClass); 141 142 /** 143 * Extract the body to a {@code Flux}. 144 * @param elementTypeRef the type reference of elements in the {@code Flux} 145 * @param <T> the element type 146 * @return a flux containing the body of the given type {@code T} 147 */ 148 <T> Flux<T> bodyToFlux(ParameterizedTypeReference<T> elementTypeRef); 149 150 /** 151 * Release the body of this response. 152 * @return a completion signal 153 * @since 5.2 154 * @see org.springframework.core.io.buffer.DataBufferUtils#release(DataBuffer) 155 */ 156 Mono<Void> releaseBody(); 157 158 /** 159 * Return this response as a delayed {@code ResponseEntity}. 160 * @param bodyClass the expected response body type 161 * @param <T> response body type 162 * @return {@code Mono} with the {@code ResponseEntity} 163 */ 164 <T> Mono<ResponseEntity<T>> toEntity(Class<T> bodyClass); 165 166 /** 167 * Return this response as a delayed {@code ResponseEntity}. 168 * @param bodyTypeReference a type reference describing the expected response body type 169 * @param <T> response body type 170 * @return {@code Mono} with the {@code ResponseEntity} 171 */ 172 <T> Mono<ResponseEntity<T>> toEntity(ParameterizedTypeReference<T> bodyTypeReference); 173 174 /** 175 * Return this response as a delayed list of {@code ResponseEntity}s. 176 * @param elementClass the expected response body list element class 177 * @param <T> the type of elements in the list 178 * @return {@code Mono} with the list of {@code ResponseEntity}s 179 */ 180 <T> Mono<ResponseEntity<List<T>>> toEntityList(Class<T> elementClass); 181 182 /** 183 * Return this response as a delayed list of {@code ResponseEntity}s. 184 * @param elementTypeRef the expected response body list element reference type 185 * @param <T> the type of elements in the list 186 * @return {@code Mono} with the list of {@code ResponseEntity}s 187 */ 188 <T> Mono<ResponseEntity<List<T>>> toEntityList(ParameterizedTypeReference<T> elementTypeRef); 189 190 /** 191 * Return this response as a delayed {@code ResponseEntity} containing 192 * status and headers, but no body. Calling this method will 193 * {@linkplain #releaseBody() release} the body of the response. 194 * @return {@code Mono} with the bodiless {@code ResponseEntity} 195 * @since 5.2 196 */ 197 Mono<ResponseEntity<Void>> toBodilessEntity(); 198 199 /** 200 * Create a {@link WebClientResponseException} that contains the response 201 * status, headers, body, and the originating request. 202 * @return a {@code Mono} with the created exception 203 * @since 5.2 204 */ 205 Mono<WebClientResponseException> createException(); 206 207 /** 208 * Return a log message prefix to use to correlate messages for this exchange. 209 * <p>The prefix is based on {@linkplain ClientRequest#logPrefix()}, which 210 * itself is based on the value of the {@link ClientRequest#LOG_ID_ATTRIBUTE 211 * LOG_ID_ATTRIBUTE} request attribute, further surrounded with "[" and "]". 212 * @return the log message prefix or an empty String if the 213 * {@link ClientRequest#LOG_ID_ATTRIBUTE LOG_ID_ATTRIBUTE} is not set 214 * @since 5.2.3 215 */ 216 String logPrefix(); 217 218 219 // Static builder methods 220 221 /** 222 * Create a builder with the status, headers, and cookies of the given response. 223 * @param other the response to copy the status, headers, and cookies from 224 * @return the created builder 225 */ 226 static Builder from(ClientResponse other) { 227 return new DefaultClientResponseBuilder(other); 228 } 229 230 /** 231 * Create a response builder with the given status code and using default strategies for 232 * reading the body. 233 * @param statusCode the status code 234 * @return the created builder 235 */ 236 static Builder create(HttpStatus statusCode) { 237 return create(statusCode, ExchangeStrategies.withDefaults()); 238 } 239 240 /** 241 * Create a response builder with the given status code and strategies for reading the body. 242 * @param statusCode the status code 243 * @param strategies the strategies 244 * @return the created builder 245 */ 246 static Builder create(HttpStatus statusCode, ExchangeStrategies strategies) { 247 return new DefaultClientResponseBuilder(strategies).statusCode(statusCode); 248 } 249 250 /** 251 * Create a response builder with the given raw status code and strategies for reading the body. 252 * @param statusCode the status code 253 * @param strategies the strategies 254 * @return the created builder 255 * @since 5.1.9 256 */ 257 static Builder create(int statusCode, ExchangeStrategies strategies) { 258 return new DefaultClientResponseBuilder(strategies).rawStatusCode(statusCode); 259 } 260 261 /** 262 * Create a response builder with the given status code and message body readers. 263 * @param statusCode the status code 264 * @param messageReaders the message readers 265 * @return the created builder 266 */ 267 static Builder create(HttpStatus statusCode, List<HttpMessageReader<?>> messageReaders) { 268 return create(statusCode, new ExchangeStrategies() { 269 @Override 270 public List<HttpMessageReader<?>> messageReaders() { 271 return messageReaders; 272 } 273 @Override 274 public List<HttpMessageWriter<?>> messageWriters() { 275 // not used in the response 276 return Collections.emptyList(); 277 } 278 }); 279 } 280 281 282 /** 283 * Represents the headers of the HTTP response. 284 * @see ClientResponse#headers() 285 */ 286 interface Headers { 287 288 /** 289 * Return the length of the body in bytes, as specified by the 290 * {@code Content-Length} header. 291 */ 292 OptionalLong contentLength(); 293 294 /** 295 * Return the {@linkplain MediaType media type} of the body, as specified 296 * by the {@code Content-Type} header. 297 */ 298 Optional<MediaType> contentType(); 299 300 /** 301 * Return the header value(s), if any, for the header of the given name. 302 * <p>Return an empty list if no header values are found. 303 * @param headerName the header name 304 */ 305 List<String> header(String headerName); 306 307 /** 308 * Return the headers as an {@link HttpHeaders} instance. 309 */ 310 HttpHeaders asHttpHeaders(); 311 } 312 313 314 /** 315 * Defines a builder for a response. 316 */ 317 interface Builder { 318 319 /** 320 * Set the status code of the response. 321 * @param statusCode the new status code 322 * @return this builder 323 */ 324 Builder statusCode(HttpStatus statusCode); 325 326 /** 327 * Set the raw status code of the response. 328 * @param statusCode the new status code 329 * @return this builder 330 * @since 5.1.9 331 */ 332 Builder rawStatusCode(int statusCode); 333 334 /** 335 * Add the given header value(s) under the given name. 336 * @param headerName the header name 337 * @param headerValues the header value(s) 338 * @return this builder 339 * @see HttpHeaders#add(String, String) 340 */ 341 Builder header(String headerName, String... headerValues); 342 343 /** 344 * Manipulate this response's headers with the given consumer. 345 * <p>The headers provided to the consumer are "live", so that the consumer 346 * can be used to {@linkplain HttpHeaders#set(String, String) overwrite} 347 * existing header values, {@linkplain HttpHeaders#remove(Object) remove} 348 * values, or use any of the other {@link HttpHeaders} methods. 349 * @param headersConsumer a function that consumes the {@code HttpHeaders} 350 * @return this builder 351 */ 352 Builder headers(Consumer<HttpHeaders> headersConsumer); 353 354 /** 355 * Add a cookie with the given name and value(s). 356 * @param name the cookie name 357 * @param values the cookie value(s) 358 * @return this builder 359 */ 360 Builder cookie(String name, String... values); 361 362 /** 363 * Manipulate this response's cookies with the given consumer. 364 * <p>The map provided to the consumer is "live", so that the consumer can be used to 365 * {@linkplain MultiValueMap#set(Object, Object) overwrite} existing cookie values, 366 * {@linkplain MultiValueMap#remove(Object) remove} values, or use any of the other 367 * {@link MultiValueMap} methods. 368 * @param cookiesConsumer a function that consumes the cookies map 369 * @return this builder 370 */ 371 Builder cookies(Consumer<MultiValueMap<String, ResponseCookie>> cookiesConsumer); 372 373 /** 374 * Set the body of the response. 375 * <p>Calling this methods will 376 * {@linkplain org.springframework.core.io.buffer.DataBufferUtils#release(DataBuffer) release} 377 * the existing body of the builder. 378 * @param body the new body 379 * @return this builder 380 */ 381 Builder body(Flux<DataBuffer> body); 382 383 /** 384 * Set the body of the response to the UTF-8 encoded bytes of the given string. 385 * <p>Calling this methods will 386 * {@linkplain org.springframework.core.io.buffer.DataBufferUtils#release(DataBuffer) release} 387 * the existing body of the builder. 388 * @param body the new body 389 * @return this builder 390 */ 391 Builder body(String body); 392 393 /** 394 * Set the request associated with the response. 395 * @param request the request 396 * @return this builder 397 * @since 5.2 398 */ 399 Builder request(HttpRequest request); 400 401 /** 402 * Build the response. 403 */ 404 ClientResponse build(); 405 } 406 407}