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.http; 018 019import java.net.URI; 020import java.time.Instant; 021import java.time.ZonedDateTime; 022import java.util.Arrays; 023import java.util.LinkedHashSet; 024import java.util.Optional; 025import java.util.Set; 026import java.util.function.Consumer; 027 028import org.springframework.lang.Nullable; 029import org.springframework.util.Assert; 030import org.springframework.util.MultiValueMap; 031import org.springframework.util.ObjectUtils; 032 033/** 034 * Extension of {@link HttpEntity} that adds a {@link HttpStatus} status code. 035 * Used in {@code RestTemplate} as well {@code @Controller} methods. 036 * 037 * <p>In {@code RestTemplate}, this class is returned by 038 * {@link org.springframework.web.client.RestTemplate#getForEntity getForEntity()} and 039 * {@link org.springframework.web.client.RestTemplate#exchange exchange()}: 040 * <pre class="code"> 041 * ResponseEntity<String> entity = template.getForEntity("https://example.com", String.class); 042 * String body = entity.getBody(); 043 * MediaType contentType = entity.getHeaders().getContentType(); 044 * HttpStatus statusCode = entity.getStatusCode(); 045 * </pre> 046 * 047 * <p>Can also be used in Spring MVC, as the return value from a @Controller method: 048 * <pre class="code"> 049 * @RequestMapping("/handle") 050 * public ResponseEntity<String> handle() { 051 * URI location = ...; 052 * HttpHeaders responseHeaders = new HttpHeaders(); 053 * responseHeaders.setLocation(location); 054 * responseHeaders.set("MyResponseHeader", "MyValue"); 055 * return new ResponseEntity<String>("Hello World", responseHeaders, HttpStatus.CREATED); 056 * } 057 * </pre> 058 * 059 * Or, by using a builder accessible via static methods: 060 * <pre class="code"> 061 * @RequestMapping("/handle") 062 * public ResponseEntity<String> handle() { 063 * URI location = ...; 064 * return ResponseEntity.created(location).header("MyResponseHeader", "MyValue").body("Hello World"); 065 * } 066 * </pre> 067 * 068 * @author Arjen Poutsma 069 * @author Brian Clozel 070 * @since 3.0.2 071 * @param <T> the body type 072 * @see #getStatusCode() 073 * @see org.springframework.web.client.RestOperations#getForEntity(String, Class, Object...) 074 * @see org.springframework.web.client.RestOperations#getForEntity(String, Class, java.util.Map) 075 * @see org.springframework.web.client.RestOperations#getForEntity(URI, Class) 076 * @see RequestEntity 077 */ 078public class ResponseEntity<T> extends HttpEntity<T> { 079 080 private final Object status; 081 082 083 /** 084 * Create a new {@code ResponseEntity} with the given status code, and no body nor headers. 085 * @param status the status code 086 */ 087 public ResponseEntity(HttpStatus status) { 088 this(null, null, status); 089 } 090 091 /** 092 * Create a new {@code ResponseEntity} with the given body and status code, and no headers. 093 * @param body the entity body 094 * @param status the status code 095 */ 096 public ResponseEntity(@Nullable T body, HttpStatus status) { 097 this(body, null, status); 098 } 099 100 /** 101 * Create a new {@code HttpEntity} with the given headers and status code, and no body. 102 * @param headers the entity headers 103 * @param status the status code 104 */ 105 public ResponseEntity(MultiValueMap<String, String> headers, HttpStatus status) { 106 this(null, headers, status); 107 } 108 109 /** 110 * Create a new {@code HttpEntity} with the given body, headers, and status code. 111 * @param body the entity body 112 * @param headers the entity headers 113 * @param status the status code 114 */ 115 public ResponseEntity(@Nullable T body, @Nullable MultiValueMap<String, String> headers, HttpStatus status) { 116 super(body, headers); 117 Assert.notNull(status, "HttpStatus must not be null"); 118 this.status = status; 119 } 120 121 /** 122 * Create a new {@code HttpEntity} with the given body, headers, and status code. 123 * Just used behind the nested builder API. 124 * @param body the entity body 125 * @param headers the entity headers 126 * @param status the status code (as {@code HttpStatus} or as {@code Integer} value) 127 */ 128 private ResponseEntity(@Nullable T body, @Nullable MultiValueMap<String, String> headers, Object status) { 129 super(body, headers); 130 Assert.notNull(status, "HttpStatus must not be null"); 131 this.status = status; 132 } 133 134 135 /** 136 * Return the HTTP status code of the response. 137 * @return the HTTP status as an HttpStatus enum entry 138 */ 139 public HttpStatus getStatusCode() { 140 if (this.status instanceof HttpStatus) { 141 return (HttpStatus) this.status; 142 } 143 else { 144 return HttpStatus.valueOf((Integer) this.status); 145 } 146 } 147 148 /** 149 * Return the HTTP status code of the response. 150 * @return the HTTP status as an int value 151 * @since 4.3 152 */ 153 public int getStatusCodeValue() { 154 if (this.status instanceof HttpStatus) { 155 return ((HttpStatus) this.status).value(); 156 } 157 else { 158 return (Integer) this.status; 159 } 160 } 161 162 163 @Override 164 public boolean equals(@Nullable Object other) { 165 if (this == other) { 166 return true; 167 } 168 if (!super.equals(other)) { 169 return false; 170 } 171 ResponseEntity<?> otherEntity = (ResponseEntity<?>) other; 172 return ObjectUtils.nullSafeEquals(this.status, otherEntity.status); 173 } 174 175 @Override 176 public int hashCode() { 177 return (29 * super.hashCode() + ObjectUtils.nullSafeHashCode(this.status)); 178 } 179 180 @Override 181 public String toString() { 182 StringBuilder builder = new StringBuilder("<"); 183 builder.append(this.status.toString()); 184 if (this.status instanceof HttpStatus) { 185 builder.append(' '); 186 builder.append(((HttpStatus) this.status).getReasonPhrase()); 187 } 188 builder.append(','); 189 T body = getBody(); 190 HttpHeaders headers = getHeaders(); 191 if (body != null) { 192 builder.append(body); 193 builder.append(','); 194 } 195 builder.append(headers); 196 builder.append('>'); 197 return builder.toString(); 198 } 199 200 201 // Static builder methods 202 203 /** 204 * Create a builder with the given status. 205 * @param status the response status 206 * @return the created builder 207 * @since 4.1 208 */ 209 public static BodyBuilder status(HttpStatus status) { 210 Assert.notNull(status, "HttpStatus must not be null"); 211 return new DefaultBuilder(status); 212 } 213 214 /** 215 * Create a builder with the given status. 216 * @param status the response status 217 * @return the created builder 218 * @since 4.1 219 */ 220 public static BodyBuilder status(int status) { 221 return new DefaultBuilder(status); 222 } 223 224 /** 225 * Create a builder with the status set to {@linkplain HttpStatus#OK OK}. 226 * @return the created builder 227 * @since 4.1 228 */ 229 public static BodyBuilder ok() { 230 return status(HttpStatus.OK); 231 } 232 233 /** 234 * A shortcut for creating a {@code ResponseEntity} with the given body and 235 * the status set to {@linkplain HttpStatus#OK OK}. 236 * @return the created {@code ResponseEntity} 237 * @since 4.1 238 */ 239 public static <T> ResponseEntity<T> ok(T body) { 240 return ok().body(body); 241 } 242 243 /** 244 * A shortcut for creating a {@code ResponseEntity} with the given body 245 * and the {@linkplain HttpStatus#OK OK} status, or an empty body and a 246 * {@linkplain HttpStatus#NOT_FOUND NOT FOUND} status in case of an 247 * {@linkplain Optional#empty()} parameter. 248 * @return the created {@code ResponseEntity} 249 * @since 5.1 250 */ 251 public static <T> ResponseEntity<T> of(Optional<T> body) { 252 Assert.notNull(body, "Body must not be null"); 253 return body.map(ResponseEntity::ok).orElseGet(() -> notFound().build()); 254 } 255 256 /** 257 * Create a new builder with a {@linkplain HttpStatus#CREATED CREATED} status 258 * and a location header set to the given URI. 259 * @param location the location URI 260 * @return the created builder 261 * @since 4.1 262 */ 263 public static BodyBuilder created(URI location) { 264 return status(HttpStatus.CREATED).location(location); 265 } 266 267 /** 268 * Create a builder with an {@linkplain HttpStatus#ACCEPTED ACCEPTED} status. 269 * @return the created builder 270 * @since 4.1 271 */ 272 public static BodyBuilder accepted() { 273 return status(HttpStatus.ACCEPTED); 274 } 275 276 /** 277 * Create a builder with a {@linkplain HttpStatus#NO_CONTENT NO_CONTENT} status. 278 * @return the created builder 279 * @since 4.1 280 */ 281 public static HeadersBuilder<?> noContent() { 282 return status(HttpStatus.NO_CONTENT); 283 } 284 285 /** 286 * Create a builder with a {@linkplain HttpStatus#BAD_REQUEST BAD_REQUEST} status. 287 * @return the created builder 288 * @since 4.1 289 */ 290 public static BodyBuilder badRequest() { 291 return status(HttpStatus.BAD_REQUEST); 292 } 293 294 /** 295 * Create a builder with a {@linkplain HttpStatus#NOT_FOUND NOT_FOUND} status. 296 * @return the created builder 297 * @since 4.1 298 */ 299 public static HeadersBuilder<?> notFound() { 300 return status(HttpStatus.NOT_FOUND); 301 } 302 303 /** 304 * Create a builder with an 305 * {@linkplain HttpStatus#UNPROCESSABLE_ENTITY UNPROCESSABLE_ENTITY} status. 306 * @return the created builder 307 * @since 4.1.3 308 */ 309 public static BodyBuilder unprocessableEntity() { 310 return status(HttpStatus.UNPROCESSABLE_ENTITY); 311 } 312 313 314 /** 315 * Defines a builder that adds headers to the response entity. 316 * @since 4.1 317 * @param <B> the builder subclass 318 */ 319 public interface HeadersBuilder<B extends HeadersBuilder<B>> { 320 321 /** 322 * Add the given, single header value under the given name. 323 * @param headerName the header name 324 * @param headerValues the header value(s) 325 * @return this builder 326 * @see HttpHeaders#add(String, String) 327 */ 328 B header(String headerName, String... headerValues); 329 330 /** 331 * Copy the given headers into the entity's headers map. 332 * @param headers the existing HttpHeaders to copy from 333 * @return this builder 334 * @since 4.1.2 335 * @see HttpHeaders#add(String, String) 336 */ 337 B headers(@Nullable HttpHeaders headers); 338 339 /** 340 * Manipulate this entity's headers with the given consumer. The 341 * headers provided to the consumer are "live", so that the consumer can be used to 342 * {@linkplain HttpHeaders#set(String, String) overwrite} existing header values, 343 * {@linkplain HttpHeaders#remove(Object) remove} values, or use any of the other 344 * {@link HttpHeaders} methods. 345 * @param headersConsumer a function that consumes the {@code HttpHeaders} 346 * @return this builder 347 * @since 5.2 348 */ 349 B headers(Consumer<HttpHeaders> headersConsumer); 350 351 /** 352 * Set the set of allowed {@link HttpMethod HTTP methods}, as specified 353 * by the {@code Allow} header. 354 * @param allowedMethods the allowed methods 355 * @return this builder 356 * @see HttpHeaders#setAllow(Set) 357 */ 358 B allow(HttpMethod... allowedMethods); 359 360 /** 361 * Set the entity tag of the body, as specified by the {@code ETag} header. 362 * @param etag the new entity tag 363 * @return this builder 364 * @see HttpHeaders#setETag(String) 365 */ 366 B eTag(String etag); 367 368 /** 369 * Set the time the resource was last changed, as specified by the 370 * {@code Last-Modified} header. 371 * @param lastModified the last modified date 372 * @return this builder 373 * @since 5.1.4 374 * @see HttpHeaders#setLastModified(ZonedDateTime) 375 */ 376 B lastModified(ZonedDateTime lastModified); 377 378 /** 379 * Set the time the resource was last changed, as specified by the 380 * {@code Last-Modified} header. 381 * @param lastModified the last modified date 382 * @return this builder 383 * @since 5.1.4 384 * @see HttpHeaders#setLastModified(Instant) 385 */ 386 B lastModified(Instant lastModified); 387 388 /** 389 * Set the time the resource was last changed, as specified by the 390 * {@code Last-Modified} header. 391 * <p>The date should be specified as the number of milliseconds since 392 * January 1, 1970 GMT. 393 * @param lastModified the last modified date 394 * @return this builder 395 * @see HttpHeaders#setLastModified(long) 396 */ 397 B lastModified(long lastModified); 398 399 /** 400 * Set the location of a resource, as specified by the {@code Location} header. 401 * @param location the location 402 * @return this builder 403 * @see HttpHeaders#setLocation(URI) 404 */ 405 B location(URI location); 406 407 /** 408 * Set the caching directives for the resource, as specified by the HTTP 1.1 409 * {@code Cache-Control} header. 410 * <p>A {@code CacheControl} instance can be built like 411 * {@code CacheControl.maxAge(3600).cachePublic().noTransform()}. 412 * @param cacheControl a builder for cache-related HTTP response headers 413 * @return this builder 414 * @since 4.2 415 * @see <a href="https://tools.ietf.org/html/rfc7234#section-5.2">RFC-7234 Section 5.2</a> 416 */ 417 B cacheControl(CacheControl cacheControl); 418 419 /** 420 * Configure one or more request header names (e.g. "Accept-Language") to 421 * add to the "Vary" response header to inform clients that the response is 422 * subject to content negotiation and variances based on the value of the 423 * given request headers. The configured request header names are added only 424 * if not already present in the response "Vary" header. 425 * @param requestHeaders request header names 426 * @since 4.3 427 */ 428 B varyBy(String... requestHeaders); 429 430 /** 431 * Build the response entity with no body. 432 * @return the response entity 433 * @see BodyBuilder#body(Object) 434 */ 435 <T> ResponseEntity<T> build(); 436 } 437 438 439 /** 440 * Defines a builder that adds a body to the response entity. 441 * @since 4.1 442 */ 443 public interface BodyBuilder extends HeadersBuilder<BodyBuilder> { 444 445 /** 446 * Set the length of the body in bytes, as specified by the 447 * {@code Content-Length} header. 448 * @param contentLength the content length 449 * @return this builder 450 * @see HttpHeaders#setContentLength(long) 451 */ 452 BodyBuilder contentLength(long contentLength); 453 454 /** 455 * Set the {@linkplain MediaType media type} of the body, as specified by the 456 * {@code Content-Type} header. 457 * @param contentType the content type 458 * @return this builder 459 * @see HttpHeaders#setContentType(MediaType) 460 */ 461 BodyBuilder contentType(MediaType contentType); 462 463 /** 464 * Set the body of the response entity and returns it. 465 * @param <T> the type of the body 466 * @param body the body of the response entity 467 * @return the built response entity 468 */ 469 <T> ResponseEntity<T> body(@Nullable T body); 470 } 471 472 473 private static class DefaultBuilder implements BodyBuilder { 474 475 private final Object statusCode; 476 477 private final HttpHeaders headers = new HttpHeaders(); 478 479 public DefaultBuilder(Object statusCode) { 480 this.statusCode = statusCode; 481 } 482 483 @Override 484 public BodyBuilder header(String headerName, String... headerValues) { 485 for (String headerValue : headerValues) { 486 this.headers.add(headerName, headerValue); 487 } 488 return this; 489 } 490 491 @Override 492 public BodyBuilder headers(@Nullable HttpHeaders headers) { 493 if (headers != null) { 494 this.headers.putAll(headers); 495 } 496 return this; 497 } 498 499 @Override 500 public BodyBuilder headers(Consumer<HttpHeaders> headersConsumer) { 501 headersConsumer.accept(this.headers); 502 return this; 503 } 504 505 @Override 506 public BodyBuilder allow(HttpMethod... allowedMethods) { 507 this.headers.setAllow(new LinkedHashSet<>(Arrays.asList(allowedMethods))); 508 return this; 509 } 510 511 @Override 512 public BodyBuilder contentLength(long contentLength) { 513 this.headers.setContentLength(contentLength); 514 return this; 515 } 516 517 @Override 518 public BodyBuilder contentType(MediaType contentType) { 519 this.headers.setContentType(contentType); 520 return this; 521 } 522 523 @Override 524 public BodyBuilder eTag(String etag) { 525 if (!etag.startsWith("\"") && !etag.startsWith("W/\"")) { 526 etag = "\"" + etag; 527 } 528 if (!etag.endsWith("\"")) { 529 etag = etag + "\""; 530 } 531 this.headers.setETag(etag); 532 return this; 533 } 534 535 @Override 536 public BodyBuilder lastModified(ZonedDateTime date) { 537 this.headers.setLastModified(date); 538 return this; 539 } 540 541 @Override 542 public BodyBuilder lastModified(Instant date) { 543 this.headers.setLastModified(date); 544 return this; 545 } 546 547 @Override 548 public BodyBuilder lastModified(long date) { 549 this.headers.setLastModified(date); 550 return this; 551 } 552 553 @Override 554 public BodyBuilder location(URI location) { 555 this.headers.setLocation(location); 556 return this; 557 } 558 559 @Override 560 public BodyBuilder cacheControl(CacheControl cacheControl) { 561 this.headers.setCacheControl(cacheControl); 562 return this; 563 } 564 565 @Override 566 public BodyBuilder varyBy(String... requestHeaders) { 567 this.headers.setVary(Arrays.asList(requestHeaders)); 568 return this; 569 } 570 571 @Override 572 public <T> ResponseEntity<T> build() { 573 return body(null); 574 } 575 576 @Override 577 public <T> ResponseEntity<T> body(@Nullable T body) { 578 return new ResponseEntity<>(body, this.headers, this.statusCode); 579 } 580 } 581 582}