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