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.lang.reflect.Type;
020import java.net.URI;
021import java.nio.charset.Charset;
022import java.time.Instant;
023import java.time.ZonedDateTime;
024import java.util.Arrays;
025import java.util.function.Consumer;
026
027import org.springframework.lang.Nullable;
028import org.springframework.util.MultiValueMap;
029import org.springframework.util.ObjectUtils;
030
031/**
032 * Extension of {@link HttpEntity} that adds a {@linkplain HttpMethod method} and
033 * {@linkplain URI uri}. Used in {@code RestTemplate} and {@code @Controller} methods.
034 *
035 * <p>In {@code RestTemplate}, this class is used as parameter in
036 * {@link org.springframework.web.client.RestTemplate#exchange(RequestEntity, Class) exchange()}:
037 * <pre class="code">
038 * MyRequest body = ...
039 * RequestEntity&lt;MyRequest&gt; request = RequestEntity
040 *     .post(new URI(&quot;https://example.com/bar&quot;))
041 *     .accept(MediaType.APPLICATION_JSON)
042 *     .body(body);
043 * ResponseEntity&lt;MyResponse&gt; response = template.exchange(request, MyResponse.class);
044 * </pre>
045 *
046 * <p>If you would like to provide a URI template with variables, consider using
047 * {@link org.springframework.web.util.DefaultUriBuilderFactory DefaultUriBuilderFactory}:
048 * <pre class="code">
049 * // Create shared factory
050 * UriBuilderFactory factory = new DefaultUriBuilderFactory();
051 *
052 * // Use factory to create URL from template
053 * URI uri = factory.uriString(&quot;https://example.com/{foo}&quot;).build(&quot;bar&quot;);
054 * RequestEntity&lt;MyRequest&gt; request = RequestEntity.post(uri).accept(MediaType.APPLICATION_JSON).body(body);
055 * </pre>
056 *
057 * <p>Can also be used in Spring MVC, as a parameter in a @Controller method:
058 * <pre class="code">
059 * &#64;RequestMapping("/handle")
060 * public void handle(RequestEntity&lt;String&gt; request) {
061 *   HttpMethod method = request.getMethod();
062 *   URI url = request.getUrl();
063 *   String body = request.getBody();
064 * }
065 * </pre>
066 *
067 * @author Arjen Poutsma
068 * @author Sebastien Deleuze
069 * @since 4.1
070 * @param <T> the body type
071 * @see #getMethod()
072 * @see #getUrl()
073 * @see org.springframework.web.client.RestOperations#exchange(RequestEntity, Class)
074 * @see ResponseEntity
075 */
076public class RequestEntity<T> extends HttpEntity<T> {
077
078        @Nullable
079        private final HttpMethod method;
080
081        private final URI url;
082
083        @Nullable
084        private final Type type;
085
086
087        /**
088         * Constructor with method and URL but without body nor headers.
089         * @param method the method
090         * @param url the URL
091         */
092        public RequestEntity(HttpMethod method, URI url) {
093                this(null, null, method, url, null);
094        }
095
096        /**
097         * Constructor with method, URL and body but without headers.
098         * @param body the body
099         * @param method the method
100         * @param url the URL
101         */
102        public RequestEntity(@Nullable T body, HttpMethod method, URI url) {
103                this(body, null, method, url, null);
104        }
105
106        /**
107         * Constructor with method, URL, body and type but without headers.
108         * @param body the body
109         * @param method the method
110         * @param url the URL
111         * @param type the type used for generic type resolution
112         * @since 4.3
113         */
114        public RequestEntity(@Nullable T body, HttpMethod method, URI url, Type type) {
115                this(body, null, method, url, type);
116        }
117
118        /**
119         * Constructor with method, URL and headers but without body.
120         * @param headers the headers
121         * @param method the method
122         * @param url the URL
123         */
124        public RequestEntity(MultiValueMap<String, String> headers, HttpMethod method, URI url) {
125                this(null, headers, method, url, null);
126        }
127
128        /**
129         * Constructor with method, URL, headers and body.
130         * @param body the body
131         * @param headers the headers
132         * @param method the method
133         * @param url the URL
134         */
135        public RequestEntity(@Nullable T body, @Nullable MultiValueMap<String, String> headers,
136                        @Nullable HttpMethod method, URI url) {
137
138                this(body, headers, method, url, null);
139        }
140
141        /**
142         * Constructor with method, URL, headers, body and type.
143         * @param body the body
144         * @param headers the headers
145         * @param method the method
146         * @param url the URL
147         * @param type the type used for generic type resolution
148         * @since 4.3
149         */
150        public RequestEntity(@Nullable T body, @Nullable MultiValueMap<String, String> headers,
151                        @Nullable HttpMethod method, URI url, @Nullable Type type) {
152
153                super(body, headers);
154                this.method = method;
155                this.url = url;
156                this.type = type;
157        }
158
159
160        /**
161         * Return the HTTP method of the request.
162         * @return the HTTP method as an {@code HttpMethod} enum value
163         */
164        @Nullable
165        public HttpMethod getMethod() {
166                return this.method;
167        }
168
169        /**
170         * Return the URL of the request.
171         * @return the URL as a {@code URI}
172         */
173        public URI getUrl() {
174                return this.url;
175        }
176
177        /**
178         * Return the type of the request's body.
179         * @return the request's body type, or {@code null} if not known
180         * @since 4.3
181         */
182        @Nullable
183        public Type getType() {
184                if (this.type == null) {
185                        T body = getBody();
186                        if (body != null) {
187                                return body.getClass();
188                        }
189                }
190                return this.type;
191        }
192
193
194        @Override
195        public boolean equals(@Nullable Object other) {
196                if (this == other) {
197                        return true;
198                }
199                if (!super.equals(other)) {
200                        return false;
201                }
202                RequestEntity<?> otherEntity = (RequestEntity<?>) other;
203                return (ObjectUtils.nullSafeEquals(getMethod(), otherEntity.getMethod()) &&
204                                ObjectUtils.nullSafeEquals(getUrl(), otherEntity.getUrl()));
205        }
206
207        @Override
208        public int hashCode() {
209                int hashCode = super.hashCode();
210                hashCode = 29 * hashCode + ObjectUtils.nullSafeHashCode(this.method);
211                hashCode = 29 * hashCode + ObjectUtils.nullSafeHashCode(this.url);
212                return hashCode;
213        }
214
215        @Override
216        public String toString() {
217                StringBuilder builder = new StringBuilder("<");
218                builder.append(getMethod());
219                builder.append(' ');
220                builder.append(getUrl());
221                builder.append(',');
222                T body = getBody();
223                HttpHeaders headers = getHeaders();
224                if (body != null) {
225                        builder.append(body);
226                        builder.append(',');
227                }
228                builder.append(headers);
229                builder.append('>');
230                return builder.toString();
231        }
232
233
234        // Static builder methods
235
236        /**
237         * Create a builder with the given method and url.
238         * @param method the HTTP method (GET, POST, etc)
239         * @param url the URL
240         * @return the created builder
241         */
242        public static BodyBuilder method(HttpMethod method, URI url) {
243                return new DefaultBodyBuilder(method, url);
244        }
245
246        /**
247         * Create an HTTP GET builder with the given url.
248         * @param url the URL
249         * @return the created builder
250         */
251        public static HeadersBuilder<?> get(URI url) {
252                return method(HttpMethod.GET, url);
253        }
254
255        /**
256         * Create an HTTP HEAD builder with the given url.
257         * @param url the URL
258         * @return the created builder
259         */
260        public static HeadersBuilder<?> head(URI url) {
261                return method(HttpMethod.HEAD, url);
262        }
263
264        /**
265         * Create an HTTP POST builder with the given url.
266         * @param url the URL
267         * @return the created builder
268         */
269        public static BodyBuilder post(URI url) {
270                return method(HttpMethod.POST, url);
271        }
272
273        /**
274         * Create an HTTP PUT builder with the given url.
275         * @param url the URL
276         * @return the created builder
277         */
278        public static BodyBuilder put(URI url) {
279                return method(HttpMethod.PUT, url);
280        }
281
282        /**
283         * Create an HTTP PATCH builder with the given url.
284         * @param url the URL
285         * @return the created builder
286         */
287        public static BodyBuilder patch(URI url) {
288                return method(HttpMethod.PATCH, url);
289        }
290
291        /**
292         * Create an HTTP DELETE builder with the given url.
293         * @param url the URL
294         * @return the created builder
295         */
296        public static HeadersBuilder<?> delete(URI url) {
297                return method(HttpMethod.DELETE, url);
298        }
299
300        /**
301         * Creates an HTTP OPTIONS builder with the given url.
302         * @param url the URL
303         * @return the created builder
304         */
305        public static HeadersBuilder<?> options(URI url) {
306                return method(HttpMethod.OPTIONS, url);
307        }
308
309
310        /**
311         * Defines a builder that adds headers to the request entity.
312         * @param <B> the builder subclass
313         */
314        public interface HeadersBuilder<B extends HeadersBuilder<B>> {
315
316                /**
317                 * Add the given, single header value under the given name.
318                 * @param headerName  the header name
319                 * @param headerValues the header value(s)
320                 * @return this builder
321                 * @see HttpHeaders#add(String, String)
322                 */
323                B header(String headerName, String... headerValues);
324
325                /**
326                 * Copy the given headers into the entity's headers map.
327                 * @param headers the existing HttpHeaders to copy from
328                 * @return this builder
329                 * @since 5.2
330                 * @see HttpHeaders#add(String, String)
331                 */
332                B headers(@Nullable HttpHeaders headers);
333
334                /**
335                 * Manipulate this entity's headers with the given consumer. The
336                 * headers provided to the consumer are "live", so that the consumer can be used to
337                 * {@linkplain HttpHeaders#set(String, String) overwrite} existing header values,
338                 * {@linkplain HttpHeaders#remove(Object) remove} values, or use any of the other
339                 * {@link HttpHeaders} methods.
340                 * @param headersConsumer a function that consumes the {@code HttpHeaders}
341                 * @return this builder
342                 * @since 5.2
343                 */
344                B headers(Consumer<HttpHeaders> headersConsumer);
345
346                /**
347                 * Set the list of acceptable {@linkplain MediaType media types}, as
348                 * specified by the {@code Accept} header.
349                 * @param acceptableMediaTypes the acceptable media types
350                 */
351                B accept(MediaType... acceptableMediaTypes);
352
353                /**
354                 * Set the list of acceptable {@linkplain Charset charsets}, as specified
355                 * by the {@code Accept-Charset} header.
356                 * @param acceptableCharsets the acceptable charsets
357                 */
358                B acceptCharset(Charset... acceptableCharsets);
359
360                /**
361                 * Set the value of the {@code If-Modified-Since} header.
362                 * @param ifModifiedSince the new value of the header
363                 * @since 5.1.4
364                 */
365                B ifModifiedSince(ZonedDateTime ifModifiedSince);
366
367                /**
368                 * Set the value of the {@code If-Modified-Since} header.
369                 * @param ifModifiedSince the new value of the header
370                 * @since 5.1.4
371                 */
372                B ifModifiedSince(Instant ifModifiedSince);
373
374                /**
375                 * Set the value of the {@code If-Modified-Since} header.
376                 * <p>The date should be specified as the number of milliseconds since
377                 * January 1, 1970 GMT.
378                 * @param ifModifiedSince the new value of the header
379                 */
380                B ifModifiedSince(long ifModifiedSince);
381
382                /**
383                 * Set the values of the {@code If-None-Match} header.
384                 * @param ifNoneMatches the new value of the header
385                 */
386                B ifNoneMatch(String... ifNoneMatches);
387
388                /**
389                 * Builds the request entity with no body.
390                 * @return the request entity
391                 * @see BodyBuilder#body(Object)
392                 */
393                RequestEntity<Void> build();
394        }
395
396
397        /**
398         * Defines a builder that adds a body to the response entity.
399         */
400        public interface BodyBuilder extends HeadersBuilder<BodyBuilder> {
401
402                /**
403                 * Set the length of the body in bytes, as specified by the
404                 * {@code Content-Length} header.
405                 * @param contentLength the content length
406                 * @return this builder
407                 * @see HttpHeaders#setContentLength(long)
408                 */
409                BodyBuilder contentLength(long contentLength);
410
411                /**
412                 * Set the {@linkplain MediaType media type} of the body, as specified
413                 * by the {@code Content-Type} header.
414                 * @param contentType the content type
415                 * @return this builder
416                 * @see HttpHeaders#setContentType(MediaType)
417                 */
418                BodyBuilder contentType(MediaType contentType);
419
420                /**
421                 * Set the body of the request entity and build the RequestEntity.
422                 * @param <T> the type of the body
423                 * @param body the body of the request entity
424                 * @return the built request entity
425                 */
426                <T> RequestEntity<T> body(T body);
427
428                /**
429                 * Set the body and type of the request entity and build the RequestEntity.
430                 * @param <T> the type of the body
431                 * @param body the body of the request entity
432                 * @param type the type of the body, useful for generic type resolution
433                 * @return the built request entity
434                 * @since 4.3
435                 */
436                <T> RequestEntity<T> body(T body, Type type);
437        }
438
439
440        private static class DefaultBodyBuilder implements BodyBuilder {
441
442                private final HttpMethod method;
443
444                private final URI url;
445
446                private final HttpHeaders headers = new HttpHeaders();
447
448                public DefaultBodyBuilder(HttpMethod method, URI url) {
449                        this.method = method;
450                        this.url = url;
451                }
452
453                @Override
454                public BodyBuilder header(String headerName, String... headerValues) {
455                        for (String headerValue : headerValues) {
456                                this.headers.add(headerName, headerValue);
457                        }
458                        return this;
459                }
460
461                @Override
462                public BodyBuilder headers(@Nullable HttpHeaders headers) {
463                        if (headers != null) {
464                                this.headers.putAll(headers);
465                        }
466                        return this;
467                }
468
469                @Override
470                public BodyBuilder headers(Consumer<HttpHeaders> headersConsumer) {
471                        headersConsumer.accept(this.headers);
472                        return this;
473                }
474
475                @Override
476                public BodyBuilder accept(MediaType... acceptableMediaTypes) {
477                        this.headers.setAccept(Arrays.asList(acceptableMediaTypes));
478                        return this;
479                }
480
481                @Override
482                public BodyBuilder acceptCharset(Charset... acceptableCharsets) {
483                        this.headers.setAcceptCharset(Arrays.asList(acceptableCharsets));
484                        return this;
485                }
486
487                @Override
488                public BodyBuilder contentLength(long contentLength) {
489                        this.headers.setContentLength(contentLength);
490                        return this;
491                }
492
493                @Override
494                public BodyBuilder contentType(MediaType contentType) {
495                        this.headers.setContentType(contentType);
496                        return this;
497                }
498
499                @Override
500                public BodyBuilder ifModifiedSince(ZonedDateTime ifModifiedSince) {
501                        this.headers.setIfModifiedSince(ifModifiedSince);
502                        return this;
503                }
504
505                @Override
506                public BodyBuilder ifModifiedSince(Instant ifModifiedSince) {
507                        this.headers.setIfModifiedSince(ifModifiedSince);
508                        return this;
509                }
510
511                @Override
512                public BodyBuilder ifModifiedSince(long ifModifiedSince) {
513                        this.headers.setIfModifiedSince(ifModifiedSince);
514                        return this;
515                }
516
517                @Override
518                public BodyBuilder ifNoneMatch(String... ifNoneMatches) {
519                        this.headers.setIfNoneMatch(Arrays.asList(ifNoneMatches));
520                        return this;
521                }
522
523                @Override
524                public RequestEntity<Void> build() {
525                        return new RequestEntity<>(this.headers, this.method, this.url);
526                }
527
528                @Override
529                public <T> RequestEntity<T> body(T body) {
530                        return new RequestEntity<>(body, this.headers, this.method, this.url);
531                }
532
533                @Override
534                public <T> RequestEntity<T> body(T body, Type type) {
535                        return new RequestEntity<>(body, this.headers, this.method, this.url, type);
536                }
537        }
538
539}