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&lt;String&gt; 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 * &#64;RequestMapping("/handle")
050 * public ResponseEntity&lt;String&gt; handle() {
051 *   URI location = ...;
052 *   HttpHeaders responseHeaders = new HttpHeaders();
053 *   responseHeaders.setLocation(location);
054 *   responseHeaders.set("MyResponseHeader", "MyValue");
055 *   return new ResponseEntity&lt;String&gt;("Hello World", responseHeaders, HttpStatus.CREATED);
056 * }
057 * </pre>
058 *
059 * Or, by using a builder accessible via static methods:
060 * <pre class="code">
061 * &#64;RequestMapping("/handle")
062 * public ResponseEntity&lt;String&gt; 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}