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.client; 018 019import java.io.IOException; 020import java.lang.reflect.Type; 021import java.net.URI; 022import java.util.ArrayList; 023import java.util.LinkedHashSet; 024import java.util.LinkedList; 025import java.util.List; 026import java.util.Map; 027import java.util.Set; 028import javax.xml.transform.Source; 029 030import org.springframework.core.ParameterizedTypeReference; 031import org.springframework.http.HttpEntity; 032import org.springframework.http.HttpHeaders; 033import org.springframework.http.HttpMethod; 034import org.springframework.http.MediaType; 035import org.springframework.http.RequestEntity; 036import org.springframework.http.ResponseEntity; 037import org.springframework.http.client.ClientHttpRequest; 038import org.springframework.http.client.ClientHttpRequestFactory; 039import org.springframework.http.client.ClientHttpResponse; 040import org.springframework.http.client.support.InterceptingHttpAccessor; 041import org.springframework.http.converter.ByteArrayHttpMessageConverter; 042import org.springframework.http.converter.GenericHttpMessageConverter; 043import org.springframework.http.converter.HttpMessageConverter; 044import org.springframework.http.converter.ResourceHttpMessageConverter; 045import org.springframework.http.converter.StringHttpMessageConverter; 046import org.springframework.http.converter.feed.AtomFeedHttpMessageConverter; 047import org.springframework.http.converter.feed.RssChannelHttpMessageConverter; 048import org.springframework.http.converter.json.GsonHttpMessageConverter; 049import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; 050import org.springframework.http.converter.support.AllEncompassingFormHttpMessageConverter; 051import org.springframework.http.converter.xml.Jaxb2RootElementHttpMessageConverter; 052import org.springframework.http.converter.xml.MappingJackson2XmlHttpMessageConverter; 053import org.springframework.http.converter.xml.SourceHttpMessageConverter; 054import org.springframework.util.Assert; 055import org.springframework.util.ClassUtils; 056import org.springframework.web.util.AbstractUriTemplateHandler; 057import org.springframework.web.util.DefaultUriTemplateHandler; 058import org.springframework.web.util.UriTemplateHandler; 059 060/** 061 * <strong>Spring's central class for synchronous client-side HTTP access.</strong> 062 * It simplifies communication with HTTP servers, and enforces RESTful principles. 063 * It handles HTTP connections, leaving application code to provide URLs 064 * (with possible template variables) and extract results. 065 * 066 * <p><strong>Note:</strong> by default the RestTemplate relies on standard JDK 067 * facilities to establish HTTP connections. You can switch to use a different 068 * HTTP library such as Apache HttpComponents, Netty, and OkHttp through the 069 * {@link #setRequestFactory} property. 070 * 071 * <p>The main entry points of this template are the methods named after the six main HTTP methods: 072 * <table> 073 * <tr><th>HTTP method</th><th>RestTemplate methods</th></tr> 074 * <tr><td>DELETE</td><td>{@link #delete}</td></tr> 075 * <tr><td>GET</td><td>{@link #getForObject}</td></tr> 076 * <tr><td></td><td>{@link #getForEntity}</td></tr> 077 * <tr><td>HEAD</td><td>{@link #headForHeaders}</td></tr> 078 * <tr><td>OPTIONS</td><td>{@link #optionsForAllow}</td></tr> 079 * <tr><td>POST</td><td>{@link #postForLocation}</td></tr> 080 * <tr><td></td><td>{@link #postForObject}</td></tr> 081 * <tr><td>PUT</td><td>{@link #put}</td></tr> 082 * <tr><td>any</td><td>{@link #exchange}</td></tr> 083 * <tr><td></td><td>{@link #execute}</td></tr> </table> 084 * 085 * <p>In addition the {@code exchange} and {@code execute} methods are generalized versions of 086 * the above methods and can be used to support additional, less frequent combinations (e.g. 087 * HTTP PATCH, HTTP PUT with response body, etc.). Note however that the underlying HTTP 088 * library used must also support the desired combination. 089 * 090 * <p>For each HTTP method there are three variants: two accept a URI template string 091 * and URI variables (array or map) while a third accepts a {@link URI}. 092 * Note that for URI templates it is assumed encoding is necessary, e.g. 093 * {@code restTemplate.getForObject("https://example.com/hotel list")} becomes 094 * {@code "https://example.com/hotel%20list"}. This also means if the URI template 095 * or URI variables are already encoded, double encoding will occur, e.g. 096 * {@code https://example.com/hotel%20list} becomes 097 * {@code https://example.com/hotel%2520list}). To avoid that use a {@code URI} method 098 * variant to provide (or re-use) a previously encoded URI. To prepare such an URI 099 * with full control over encoding, consider using 100 * {@link org.springframework.web.util.UriComponentsBuilder}. 101 * 102 * <p>Internally the template uses {@link HttpMessageConverter} instances to 103 * convert HTTP messages to and from POJOs. Converters for the main mime types 104 * are registered by default but you can also register additional converters 105 * via {@link #setMessageConverters}. 106 * 107 * <p>This template uses a 108 * {@link org.springframework.http.client.SimpleClientHttpRequestFactory} and a 109 * {@link DefaultResponseErrorHandler} as default strategies for creating HTTP 110 * connections or handling HTTP errors, respectively. These defaults can be overridden 111 * through {@link #setRequestFactory} and {@link #setErrorHandler} respectively. 112 * 113 * @author Arjen Poutsma 114 * @author Brian Clozel 115 * @author Roy Clarkson 116 * @author Juergen Hoeller 117 * @since 3.0 118 * @see HttpMessageConverter 119 * @see RequestCallback 120 * @see ResponseExtractor 121 * @see ResponseErrorHandler 122 * @see AsyncRestTemplate 123 */ 124public class RestTemplate extends InterceptingHttpAccessor implements RestOperations { 125 126 private static final boolean romePresent = 127 ClassUtils.isPresent("com.rometools.rome.feed.WireFeed", 128 RestTemplate.class.getClassLoader()); 129 130 private static final boolean jaxb2Present = 131 ClassUtils.isPresent("javax.xml.bind.Binder", 132 RestTemplate.class.getClassLoader()); 133 134 private static final boolean jackson2Present = 135 ClassUtils.isPresent("com.fasterxml.jackson.databind.ObjectMapper", 136 RestTemplate.class.getClassLoader()) && 137 ClassUtils.isPresent("com.fasterxml.jackson.core.JsonGenerator", 138 RestTemplate.class.getClassLoader()); 139 140 private static final boolean jackson2XmlPresent = 141 ClassUtils.isPresent("com.fasterxml.jackson.dataformat.xml.XmlMapper", 142 RestTemplate.class.getClassLoader()); 143 144 private static final boolean gsonPresent = 145 ClassUtils.isPresent("com.google.gson.Gson", 146 RestTemplate.class.getClassLoader()); 147 148 149 private final List<HttpMessageConverter<?>> messageConverters = new ArrayList<HttpMessageConverter<?>>(); 150 151 private ResponseErrorHandler errorHandler = new DefaultResponseErrorHandler(); 152 153 private UriTemplateHandler uriTemplateHandler = new DefaultUriTemplateHandler(); 154 155 private final ResponseExtractor<HttpHeaders> headersExtractor = new HeadersExtractor(); 156 157 158 /** 159 * Create a new instance of the {@link RestTemplate} using default settings. 160 * Default {@link HttpMessageConverter}s are initialized. 161 */ 162 public RestTemplate() { 163 this.messageConverters.add(new ByteArrayHttpMessageConverter()); 164 this.messageConverters.add(new StringHttpMessageConverter()); 165 this.messageConverters.add(new ResourceHttpMessageConverter()); 166 this.messageConverters.add(new SourceHttpMessageConverter<Source>()); 167 this.messageConverters.add(new AllEncompassingFormHttpMessageConverter()); 168 169 if (romePresent) { 170 this.messageConverters.add(new AtomFeedHttpMessageConverter()); 171 this.messageConverters.add(new RssChannelHttpMessageConverter()); 172 } 173 174 if (jackson2XmlPresent) { 175 this.messageConverters.add(new MappingJackson2XmlHttpMessageConverter()); 176 } 177 else if (jaxb2Present) { 178 this.messageConverters.add(new Jaxb2RootElementHttpMessageConverter()); 179 } 180 181 if (jackson2Present) { 182 this.messageConverters.add(new MappingJackson2HttpMessageConverter()); 183 } 184 else if (gsonPresent) { 185 this.messageConverters.add(new GsonHttpMessageConverter()); 186 } 187 } 188 189 /** 190 * Create a new instance of the {@link RestTemplate} based on the given {@link ClientHttpRequestFactory}. 191 * @param requestFactory HTTP request factory to use 192 * @see org.springframework.http.client.SimpleClientHttpRequestFactory 193 * @see org.springframework.http.client.HttpComponentsClientHttpRequestFactory 194 */ 195 public RestTemplate(ClientHttpRequestFactory requestFactory) { 196 this(); 197 setRequestFactory(requestFactory); 198 } 199 200 /** 201 * Create a new instance of the {@link RestTemplate} using the given list of 202 * {@link HttpMessageConverter} to use 203 * @param messageConverters the list of {@link HttpMessageConverter} to use 204 * @since 3.2.7 205 */ 206 public RestTemplate(List<HttpMessageConverter<?>> messageConverters) { 207 Assert.notEmpty(messageConverters, "At least one HttpMessageConverter required"); 208 this.messageConverters.addAll(messageConverters); 209 } 210 211 212 /** 213 * Set the message body converters to use. 214 * <p>These converters are used to convert from and to HTTP requests and responses. 215 */ 216 public void setMessageConverters(List<HttpMessageConverter<?>> messageConverters) { 217 Assert.notEmpty(messageConverters, "At least one HttpMessageConverter required"); 218 // Take getMessageConverters() List as-is when passed in here 219 if (this.messageConverters != messageConverters) { 220 this.messageConverters.clear(); 221 this.messageConverters.addAll(messageConverters); 222 } 223 } 224 225 /** 226 * Return the list of message body converters. 227 * <p>The returned {@link List} is active and may get appended to. 228 */ 229 public List<HttpMessageConverter<?>> getMessageConverters() { 230 return this.messageConverters; 231 } 232 233 /** 234 * Set the error handler. 235 * <p>By default, RestTemplate uses a {@link DefaultResponseErrorHandler}. 236 */ 237 public void setErrorHandler(ResponseErrorHandler errorHandler) { 238 Assert.notNull(errorHandler, "ResponseErrorHandler must not be null"); 239 this.errorHandler = errorHandler; 240 } 241 242 /** 243 * Return the error handler. 244 */ 245 public ResponseErrorHandler getErrorHandler() { 246 return this.errorHandler; 247 } 248 249 /** 250 * Configure default URI variable values. This is a shortcut for: 251 * <pre class="code"> 252 * DefaultUriTemplateHandler handler = new DefaultUriTemplateHandler(); 253 * handler.setDefaultUriVariables(...); 254 * 255 * RestTemplate restTemplate = new RestTemplate(); 256 * restTemplate.setUriTemplateHandler(handler); 257 * </pre> 258 * @param defaultUriVariables the default URI variable values 259 * @since 4.3 260 */ 261 public void setDefaultUriVariables(Map<String, ?> defaultUriVariables) { 262 Assert.isInstanceOf(AbstractUriTemplateHandler.class, this.uriTemplateHandler, 263 "Can only use this property in conjunction with an AbstractUriTemplateHandler"); 264 ((AbstractUriTemplateHandler) this.uriTemplateHandler).setDefaultUriVariables(defaultUriVariables); 265 } 266 267 /** 268 * Configure the {@link UriTemplateHandler} to use to expand URI templates. 269 * By default the {@link DefaultUriTemplateHandler} is used which relies on 270 * Spring's URI template support and exposes several useful properties that 271 * customize its behavior for encoding and for prepending a common base URL. 272 * An alternative implementation may be used to plug an external URI 273 * template library. 274 * @param handler the URI template handler to use 275 */ 276 public void setUriTemplateHandler(UriTemplateHandler handler) { 277 Assert.notNull(handler, "UriTemplateHandler must not be null"); 278 this.uriTemplateHandler = handler; 279 } 280 281 /** 282 * Return the configured URI template handler. 283 */ 284 public UriTemplateHandler getUriTemplateHandler() { 285 return this.uriTemplateHandler; 286 } 287 288 289 // GET 290 291 @Override 292 public <T> T getForObject(String url, Class<T> responseType, Object... uriVariables) throws RestClientException { 293 RequestCallback requestCallback = acceptHeaderRequestCallback(responseType); 294 HttpMessageConverterExtractor<T> responseExtractor = 295 new HttpMessageConverterExtractor<T>(responseType, getMessageConverters(), logger); 296 return execute(url, HttpMethod.GET, requestCallback, responseExtractor, uriVariables); 297 } 298 299 @Override 300 public <T> T getForObject(String url, Class<T> responseType, Map<String, ?> uriVariables) throws RestClientException { 301 RequestCallback requestCallback = acceptHeaderRequestCallback(responseType); 302 HttpMessageConverterExtractor<T> responseExtractor = 303 new HttpMessageConverterExtractor<T>(responseType, getMessageConverters(), logger); 304 return execute(url, HttpMethod.GET, requestCallback, responseExtractor, uriVariables); 305 } 306 307 @Override 308 public <T> T getForObject(URI url, Class<T> responseType) throws RestClientException { 309 RequestCallback requestCallback = acceptHeaderRequestCallback(responseType); 310 HttpMessageConverterExtractor<T> responseExtractor = 311 new HttpMessageConverterExtractor<T>(responseType, getMessageConverters(), logger); 312 return execute(url, HttpMethod.GET, requestCallback, responseExtractor); 313 } 314 315 @Override 316 public <T> ResponseEntity<T> getForEntity(String url, Class<T> responseType, Object... uriVariables) 317 throws RestClientException { 318 319 RequestCallback requestCallback = acceptHeaderRequestCallback(responseType); 320 ResponseExtractor<ResponseEntity<T>> responseExtractor = responseEntityExtractor(responseType); 321 return execute(url, HttpMethod.GET, requestCallback, responseExtractor, uriVariables); 322 } 323 324 @Override 325 public <T> ResponseEntity<T> getForEntity(String url, Class<T> responseType, Map<String, ?> uriVariables) 326 throws RestClientException { 327 328 RequestCallback requestCallback = acceptHeaderRequestCallback(responseType); 329 ResponseExtractor<ResponseEntity<T>> responseExtractor = responseEntityExtractor(responseType); 330 return execute(url, HttpMethod.GET, requestCallback, responseExtractor, uriVariables); 331 } 332 333 @Override 334 public <T> ResponseEntity<T> getForEntity(URI url, Class<T> responseType) throws RestClientException { 335 RequestCallback requestCallback = acceptHeaderRequestCallback(responseType); 336 ResponseExtractor<ResponseEntity<T>> responseExtractor = responseEntityExtractor(responseType); 337 return execute(url, HttpMethod.GET, requestCallback, responseExtractor); 338 } 339 340 341 // HEAD 342 343 @Override 344 public HttpHeaders headForHeaders(String url, Object... uriVariables) throws RestClientException { 345 return execute(url, HttpMethod.HEAD, null, headersExtractor(), uriVariables); 346 } 347 348 @Override 349 public HttpHeaders headForHeaders(String url, Map<String, ?> uriVariables) throws RestClientException { 350 return execute(url, HttpMethod.HEAD, null, headersExtractor(), uriVariables); 351 } 352 353 @Override 354 public HttpHeaders headForHeaders(URI url) throws RestClientException { 355 return execute(url, HttpMethod.HEAD, null, headersExtractor()); 356 } 357 358 359 // POST 360 361 @Override 362 public URI postForLocation(String url, Object request, Object... uriVariables) throws RestClientException { 363 RequestCallback requestCallback = httpEntityCallback(request); 364 HttpHeaders headers = execute(url, HttpMethod.POST, requestCallback, headersExtractor(), uriVariables); 365 return headers.getLocation(); 366 } 367 368 @Override 369 public URI postForLocation(String url, Object request, Map<String, ?> uriVariables) throws RestClientException { 370 RequestCallback requestCallback = httpEntityCallback(request); 371 HttpHeaders headers = execute(url, HttpMethod.POST, requestCallback, headersExtractor(), uriVariables); 372 return headers.getLocation(); 373 } 374 375 @Override 376 public URI postForLocation(URI url, Object request) throws RestClientException { 377 RequestCallback requestCallback = httpEntityCallback(request); 378 HttpHeaders headers = execute(url, HttpMethod.POST, requestCallback, headersExtractor()); 379 return headers.getLocation(); 380 } 381 382 @Override 383 public <T> T postForObject(String url, Object request, Class<T> responseType, Object... uriVariables) 384 throws RestClientException { 385 386 RequestCallback requestCallback = httpEntityCallback(request, responseType); 387 HttpMessageConverterExtractor<T> responseExtractor = 388 new HttpMessageConverterExtractor<T>(responseType, getMessageConverters(), logger); 389 return execute(url, HttpMethod.POST, requestCallback, responseExtractor, uriVariables); 390 } 391 392 @Override 393 public <T> T postForObject(String url, Object request, Class<T> responseType, Map<String, ?> uriVariables) 394 throws RestClientException { 395 396 RequestCallback requestCallback = httpEntityCallback(request, responseType); 397 HttpMessageConverterExtractor<T> responseExtractor = 398 new HttpMessageConverterExtractor<T>(responseType, getMessageConverters(), logger); 399 return execute(url, HttpMethod.POST, requestCallback, responseExtractor, uriVariables); 400 } 401 402 @Override 403 public <T> T postForObject(URI url, Object request, Class<T> responseType) throws RestClientException { 404 RequestCallback requestCallback = httpEntityCallback(request, responseType); 405 HttpMessageConverterExtractor<T> responseExtractor = 406 new HttpMessageConverterExtractor<T>(responseType, getMessageConverters()); 407 return execute(url, HttpMethod.POST, requestCallback, responseExtractor); 408 } 409 410 @Override 411 public <T> ResponseEntity<T> postForEntity(String url, Object request, Class<T> responseType, Object... uriVariables) 412 throws RestClientException { 413 414 RequestCallback requestCallback = httpEntityCallback(request, responseType); 415 ResponseExtractor<ResponseEntity<T>> responseExtractor = responseEntityExtractor(responseType); 416 return execute(url, HttpMethod.POST, requestCallback, responseExtractor, uriVariables); 417 } 418 419 @Override 420 public <T> ResponseEntity<T> postForEntity(String url, Object request, Class<T> responseType, Map<String, ?> uriVariables) 421 throws RestClientException { 422 423 RequestCallback requestCallback = httpEntityCallback(request, responseType); 424 ResponseExtractor<ResponseEntity<T>> responseExtractor = responseEntityExtractor(responseType); 425 return execute(url, HttpMethod.POST, requestCallback, responseExtractor, uriVariables); 426 } 427 428 @Override 429 public <T> ResponseEntity<T> postForEntity(URI url, Object request, Class<T> responseType) throws RestClientException { 430 RequestCallback requestCallback = httpEntityCallback(request, responseType); 431 ResponseExtractor<ResponseEntity<T>> responseExtractor = responseEntityExtractor(responseType); 432 return execute(url, HttpMethod.POST, requestCallback, responseExtractor); 433 } 434 435 436 // PUT 437 438 @Override 439 public void put(String url, Object request, Object... uriVariables) throws RestClientException { 440 RequestCallback requestCallback = httpEntityCallback(request); 441 execute(url, HttpMethod.PUT, requestCallback, null, uriVariables); 442 } 443 444 @Override 445 public void put(String url, Object request, Map<String, ?> uriVariables) throws RestClientException { 446 RequestCallback requestCallback = httpEntityCallback(request); 447 execute(url, HttpMethod.PUT, requestCallback, null, uriVariables); 448 } 449 450 @Override 451 public void put(URI url, Object request) throws RestClientException { 452 RequestCallback requestCallback = httpEntityCallback(request); 453 execute(url, HttpMethod.PUT, requestCallback, null); 454 } 455 456 457 // PATCH 458 459 @Override 460 public <T> T patchForObject(String url, Object request, Class<T> responseType, 461 Object... uriVariables) throws RestClientException { 462 463 RequestCallback requestCallback = httpEntityCallback(request, responseType); 464 HttpMessageConverterExtractor<T> responseExtractor = 465 new HttpMessageConverterExtractor<T>(responseType, getMessageConverters(), logger); 466 return execute(url, HttpMethod.PATCH, requestCallback, responseExtractor, uriVariables); 467 } 468 469 @Override 470 public <T> T patchForObject(String url, Object request, Class<T> responseType, 471 Map<String, ?> uriVariables) throws RestClientException { 472 473 RequestCallback requestCallback = httpEntityCallback(request, responseType); 474 HttpMessageConverterExtractor<T> responseExtractor = 475 new HttpMessageConverterExtractor<T>(responseType, getMessageConverters(), logger); 476 return execute(url, HttpMethod.PATCH, requestCallback, responseExtractor, uriVariables); 477 } 478 479 @Override 480 public <T> T patchForObject(URI url, Object request, Class<T> responseType) 481 throws RestClientException { 482 483 RequestCallback requestCallback = httpEntityCallback(request, responseType); 484 HttpMessageConverterExtractor<T> responseExtractor = 485 new HttpMessageConverterExtractor<T>(responseType, getMessageConverters()); 486 return execute(url, HttpMethod.PATCH, requestCallback, responseExtractor); 487 } 488 489 490 // DELETE 491 492 @Override 493 public void delete(String url, Object... uriVariables) throws RestClientException { 494 execute(url, HttpMethod.DELETE, null, null, uriVariables); 495 } 496 497 @Override 498 public void delete(String url, Map<String, ?> uriVariables) throws RestClientException { 499 execute(url, HttpMethod.DELETE, null, null, uriVariables); 500 } 501 502 @Override 503 public void delete(URI url) throws RestClientException { 504 execute(url, HttpMethod.DELETE, null, null); 505 } 506 507 508 // OPTIONS 509 510 @Override 511 public Set<HttpMethod> optionsForAllow(String url, Object... uriVariables) throws RestClientException { 512 ResponseExtractor<HttpHeaders> headersExtractor = headersExtractor(); 513 HttpHeaders headers = execute(url, HttpMethod.OPTIONS, null, headersExtractor, uriVariables); 514 return headers.getAllow(); 515 } 516 517 @Override 518 public Set<HttpMethod> optionsForAllow(String url, Map<String, ?> uriVariables) throws RestClientException { 519 ResponseExtractor<HttpHeaders> headersExtractor = headersExtractor(); 520 HttpHeaders headers = execute(url, HttpMethod.OPTIONS, null, headersExtractor, uriVariables); 521 return headers.getAllow(); 522 } 523 524 @Override 525 public Set<HttpMethod> optionsForAllow(URI url) throws RestClientException { 526 ResponseExtractor<HttpHeaders> headersExtractor = headersExtractor(); 527 HttpHeaders headers = execute(url, HttpMethod.OPTIONS, null, headersExtractor); 528 return headers.getAllow(); 529 } 530 531 532 // exchange 533 534 @Override 535 public <T> ResponseEntity<T> exchange(String url, HttpMethod method, 536 HttpEntity<?> requestEntity, Class<T> responseType, Object... uriVariables) throws RestClientException { 537 538 RequestCallback requestCallback = httpEntityCallback(requestEntity, responseType); 539 ResponseExtractor<ResponseEntity<T>> responseExtractor = responseEntityExtractor(responseType); 540 return execute(url, method, requestCallback, responseExtractor, uriVariables); 541 } 542 543 @Override 544 public <T> ResponseEntity<T> exchange(String url, HttpMethod method, 545 HttpEntity<?> requestEntity, Class<T> responseType, Map<String, ?> uriVariables) throws RestClientException { 546 547 RequestCallback requestCallback = httpEntityCallback(requestEntity, responseType); 548 ResponseExtractor<ResponseEntity<T>> responseExtractor = responseEntityExtractor(responseType); 549 return execute(url, method, requestCallback, responseExtractor, uriVariables); 550 } 551 552 @Override 553 public <T> ResponseEntity<T> exchange(URI url, HttpMethod method, HttpEntity<?> requestEntity, 554 Class<T> responseType) throws RestClientException { 555 556 RequestCallback requestCallback = httpEntityCallback(requestEntity, responseType); 557 ResponseExtractor<ResponseEntity<T>> responseExtractor = responseEntityExtractor(responseType); 558 return execute(url, method, requestCallback, responseExtractor); 559 } 560 561 @Override 562 public <T> ResponseEntity<T> exchange(String url, HttpMethod method, HttpEntity<?> requestEntity, 563 ParameterizedTypeReference<T> responseType, Object... uriVariables) throws RestClientException { 564 565 Type type = responseType.getType(); 566 RequestCallback requestCallback = httpEntityCallback(requestEntity, type); 567 ResponseExtractor<ResponseEntity<T>> responseExtractor = responseEntityExtractor(type); 568 return execute(url, method, requestCallback, responseExtractor, uriVariables); 569 } 570 571 @Override 572 public <T> ResponseEntity<T> exchange(String url, HttpMethod method, HttpEntity<?> requestEntity, 573 ParameterizedTypeReference<T> responseType, Map<String, ?> uriVariables) throws RestClientException { 574 575 Type type = responseType.getType(); 576 RequestCallback requestCallback = httpEntityCallback(requestEntity, type); 577 ResponseExtractor<ResponseEntity<T>> responseExtractor = responseEntityExtractor(type); 578 return execute(url, method, requestCallback, responseExtractor, uriVariables); 579 } 580 581 @Override 582 public <T> ResponseEntity<T> exchange(URI url, HttpMethod method, HttpEntity<?> requestEntity, 583 ParameterizedTypeReference<T> responseType) throws RestClientException { 584 585 Type type = responseType.getType(); 586 RequestCallback requestCallback = httpEntityCallback(requestEntity, type); 587 ResponseExtractor<ResponseEntity<T>> responseExtractor = responseEntityExtractor(type); 588 return execute(url, method, requestCallback, responseExtractor); 589 } 590 591 @Override 592 public <T> ResponseEntity<T> exchange(RequestEntity<?> requestEntity, Class<T> responseType) 593 throws RestClientException { 594 595 Assert.notNull(requestEntity, "RequestEntity must not be null"); 596 597 RequestCallback requestCallback = httpEntityCallback(requestEntity, responseType); 598 ResponseExtractor<ResponseEntity<T>> responseExtractor = responseEntityExtractor(responseType); 599 return execute(requestEntity.getUrl(), requestEntity.getMethod(), requestCallback, responseExtractor); 600 } 601 602 @Override 603 public <T> ResponseEntity<T> exchange(RequestEntity<?> requestEntity, ParameterizedTypeReference<T> responseType) 604 throws RestClientException { 605 606 Assert.notNull(requestEntity, "RequestEntity must not be null"); 607 608 Type type = responseType.getType(); 609 RequestCallback requestCallback = httpEntityCallback(requestEntity, type); 610 ResponseExtractor<ResponseEntity<T>> responseExtractor = responseEntityExtractor(type); 611 return execute(requestEntity.getUrl(), requestEntity.getMethod(), requestCallback, responseExtractor); 612 } 613 614 615 // general execution 616 617 @Override 618 public <T> T execute(String url, HttpMethod method, RequestCallback requestCallback, 619 ResponseExtractor<T> responseExtractor, Object... uriVariables) throws RestClientException { 620 621 URI expanded = getUriTemplateHandler().expand(url, uriVariables); 622 return doExecute(expanded, method, requestCallback, responseExtractor); 623 } 624 625 @Override 626 public <T> T execute(String url, HttpMethod method, RequestCallback requestCallback, 627 ResponseExtractor<T> responseExtractor, Map<String, ?> uriVariables) throws RestClientException { 628 629 URI expanded = getUriTemplateHandler().expand(url, uriVariables); 630 return doExecute(expanded, method, requestCallback, responseExtractor); 631 } 632 633 @Override 634 public <T> T execute(URI url, HttpMethod method, RequestCallback requestCallback, 635 ResponseExtractor<T> responseExtractor) throws RestClientException { 636 637 return doExecute(url, method, requestCallback, responseExtractor); 638 } 639 640 /** 641 * Execute the given method on the provided URI. 642 * <p>The {@link ClientHttpRequest} is processed using the {@link RequestCallback}; 643 * the response with the {@link ResponseExtractor}. 644 * @param url the fully-expanded URL to connect to 645 * @param method the HTTP method to execute (GET, POST, etc.) 646 * @param requestCallback object that prepares the request (can be {@code null}) 647 * @param responseExtractor object that extracts the return value from the response (can be {@code null}) 648 * @return an arbitrary object, as returned by the {@link ResponseExtractor} 649 */ 650 protected <T> T doExecute(URI url, HttpMethod method, RequestCallback requestCallback, 651 ResponseExtractor<T> responseExtractor) throws RestClientException { 652 653 Assert.notNull(url, "'url' must not be null"); 654 Assert.notNull(method, "'method' must not be null"); 655 ClientHttpResponse response = null; 656 try { 657 ClientHttpRequest request = createRequest(url, method); 658 if (requestCallback != null) { 659 requestCallback.doWithRequest(request); 660 } 661 response = request.execute(); 662 handleResponse(url, method, response); 663 if (responseExtractor != null) { 664 return responseExtractor.extractData(response); 665 } 666 else { 667 return null; 668 } 669 } 670 catch (IOException ex) { 671 String resource = url.toString(); 672 String query = url.getRawQuery(); 673 resource = (query != null ? resource.substring(0, resource.indexOf('?')) : resource); 674 throw new ResourceAccessException("I/O error on " + method.name() + 675 " request for \"" + resource + "\": " + ex.getMessage(), ex); 676 } 677 finally { 678 if (response != null) { 679 response.close(); 680 } 681 } 682 } 683 684 /** 685 * Handle the given response, performing appropriate logging and 686 * invoking the {@link ResponseErrorHandler} if necessary. 687 * <p>Can be overridden in subclasses. 688 * @param url the fully-expanded URL to connect to 689 * @param method the HTTP method to execute (GET, POST, etc.) 690 * @param response the resulting {@link ClientHttpResponse} 691 * @throws IOException if propagated from {@link ResponseErrorHandler} 692 * @since 4.1.6 693 * @see #setErrorHandler 694 */ 695 protected void handleResponse(URI url, HttpMethod method, ClientHttpResponse response) throws IOException { 696 ResponseErrorHandler errorHandler = getErrorHandler(); 697 boolean hasError = errorHandler.hasError(response); 698 if (logger.isDebugEnabled()) { 699 try { 700 logger.debug(method.name() + " request for \"" + url + "\" resulted in " + 701 response.getRawStatusCode() + " (" + response.getStatusText() + ")" + 702 (hasError ? "; invoking error handler" : "")); 703 } 704 catch (IOException ex) { 705 // ignore 706 } 707 } 708 if (hasError) { 709 errorHandler.handleError(response); 710 } 711 } 712 713 /** 714 * Returns a request callback implementation that prepares the request {@code Accept} 715 * headers based on the given response type and configured 716 * {@linkplain #getMessageConverters() message converters}. 717 */ 718 protected <T> RequestCallback acceptHeaderRequestCallback(Class<T> responseType) { 719 return new AcceptHeaderRequestCallback(responseType); 720 } 721 722 /** 723 * Returns a request callback implementation that writes the given object to the 724 * request stream. 725 */ 726 protected <T> RequestCallback httpEntityCallback(Object requestBody) { 727 return new HttpEntityRequestCallback(requestBody); 728 } 729 730 /** 731 * Returns a request callback implementation that writes the given object to the 732 * request stream. 733 */ 734 protected <T> RequestCallback httpEntityCallback(Object requestBody, Type responseType) { 735 return new HttpEntityRequestCallback(requestBody, responseType); 736 } 737 738 /** 739 * Returns a response extractor for {@link ResponseEntity}. 740 */ 741 protected <T> ResponseExtractor<ResponseEntity<T>> responseEntityExtractor(Type responseType) { 742 return new ResponseEntityResponseExtractor<T>(responseType); 743 } 744 745 /** 746 * Returns a response extractor for {@link HttpHeaders}. 747 */ 748 protected ResponseExtractor<HttpHeaders> headersExtractor() { 749 return this.headersExtractor; 750 } 751 752 753 /** 754 * Request callback implementation that prepares the request's accept headers. 755 */ 756 private class AcceptHeaderRequestCallback implements RequestCallback { 757 758 private final Type responseType; 759 760 private AcceptHeaderRequestCallback(Type responseType) { 761 this.responseType = responseType; 762 } 763 764 @Override 765 public void doWithRequest(ClientHttpRequest request) throws IOException { 766 if (this.responseType != null) { 767 Class<?> responseClass = null; 768 if (this.responseType instanceof Class) { 769 responseClass = (Class<?>) this.responseType; 770 } 771 Set<MediaType> allSupportedMediaTypes = new LinkedHashSet<MediaType>(); 772 for (HttpMessageConverter<?> converter : getMessageConverters()) { 773 if (responseClass != null) { 774 if (converter.canRead(responseClass, null)) { 775 allSupportedMediaTypes.addAll(getSupportedMediaTypes(converter)); 776 } 777 } 778 else if (converter instanceof GenericHttpMessageConverter) { 779 GenericHttpMessageConverter<?> genericConverter = (GenericHttpMessageConverter<?>) converter; 780 if (genericConverter.canRead(this.responseType, null, null)) { 781 allSupportedMediaTypes.addAll(getSupportedMediaTypes(converter)); 782 } 783 } 784 } 785 if (!allSupportedMediaTypes.isEmpty()) { 786 List<MediaType> result = new ArrayList<MediaType>(allSupportedMediaTypes); 787 MediaType.sortBySpecificity(result); 788 if (logger.isDebugEnabled()) { 789 logger.debug("Setting request Accept header to " + allSupportedMediaTypes); 790 } 791 request.getHeaders().setAccept(result); 792 } 793 } 794 } 795 796 private List<MediaType> getSupportedMediaTypes(HttpMessageConverter<?> messageConverter) { 797 List<MediaType> supportedMediaTypes = messageConverter.getSupportedMediaTypes(); 798 List<MediaType> result = new ArrayList<MediaType>(supportedMediaTypes.size()); 799 for (MediaType supportedMediaType : supportedMediaTypes) { 800 if (supportedMediaType.getCharset() != null) { 801 supportedMediaType = 802 new MediaType(supportedMediaType.getType(), supportedMediaType.getSubtype()); 803 } 804 result.add(supportedMediaType); 805 } 806 return result; 807 } 808 } 809 810 811 /** 812 * Request callback implementation that writes the given object to the request stream. 813 */ 814 private class HttpEntityRequestCallback extends AcceptHeaderRequestCallback { 815 816 private final HttpEntity<?> requestEntity; 817 818 private HttpEntityRequestCallback(Object requestBody) { 819 this(requestBody, null); 820 } 821 822 private HttpEntityRequestCallback(Object requestBody, Type responseType) { 823 super(responseType); 824 if (requestBody instanceof HttpEntity) { 825 this.requestEntity = (HttpEntity<?>) requestBody; 826 } 827 else if (requestBody != null) { 828 this.requestEntity = new HttpEntity<Object>(requestBody); 829 } 830 else { 831 this.requestEntity = HttpEntity.EMPTY; 832 } 833 } 834 835 @Override 836 @SuppressWarnings("unchecked") 837 public void doWithRequest(ClientHttpRequest httpRequest) throws IOException { 838 super.doWithRequest(httpRequest); 839 if (!this.requestEntity.hasBody()) { 840 HttpHeaders httpHeaders = httpRequest.getHeaders(); 841 HttpHeaders requestHeaders = this.requestEntity.getHeaders(); 842 if (!requestHeaders.isEmpty()) { 843 for (Map.Entry<String, List<String>> entry : requestHeaders.entrySet()) { 844 httpHeaders.put(entry.getKey(), new LinkedList<String>(entry.getValue())); 845 } 846 } 847 if (httpHeaders.getContentLength() < 0) { 848 httpHeaders.setContentLength(0L); 849 } 850 } 851 else { 852 Object requestBody = this.requestEntity.getBody(); 853 Class<?> requestBodyClass = requestBody.getClass(); 854 Type requestBodyType = (this.requestEntity instanceof RequestEntity ? 855 ((RequestEntity<?>)this.requestEntity).getType() : requestBodyClass); 856 HttpHeaders httpHeaders = httpRequest.getHeaders(); 857 HttpHeaders requestHeaders = this.requestEntity.getHeaders(); 858 MediaType requestContentType = requestHeaders.getContentType(); 859 for (HttpMessageConverter<?> messageConverter : getMessageConverters()) { 860 if (messageConverter instanceof GenericHttpMessageConverter) { 861 GenericHttpMessageConverter<Object> genericMessageConverter = (GenericHttpMessageConverter<Object>) messageConverter; 862 if (genericMessageConverter.canWrite(requestBodyType, requestBodyClass, requestContentType)) { 863 if (!requestHeaders.isEmpty()) { 864 for (Map.Entry<String, List<String>> entry : requestHeaders.entrySet()) { 865 httpHeaders.put(entry.getKey(), new LinkedList<String>(entry.getValue())); 866 } 867 } 868 if (logger.isDebugEnabled()) { 869 if (requestContentType != null) { 870 logger.debug("Writing [" + requestBody + "] as \"" + requestContentType + 871 "\" using [" + messageConverter + "]"); 872 } 873 else { 874 logger.debug("Writing [" + requestBody + "] using [" + messageConverter + "]"); 875 } 876 877 } 878 genericMessageConverter.write( 879 requestBody, requestBodyType, requestContentType, httpRequest); 880 return; 881 } 882 } 883 else if (messageConverter.canWrite(requestBodyClass, requestContentType)) { 884 if (!requestHeaders.isEmpty()) { 885 for (Map.Entry<String, List<String>> entry : requestHeaders.entrySet()) { 886 httpHeaders.put(entry.getKey(), new LinkedList<String>(entry.getValue())); 887 } 888 } 889 if (logger.isDebugEnabled()) { 890 if (requestContentType != null) { 891 logger.debug("Writing [" + requestBody + "] as \"" + requestContentType + 892 "\" using [" + messageConverter + "]"); 893 } 894 else { 895 logger.debug("Writing [" + requestBody + "] using [" + messageConverter + "]"); 896 } 897 898 } 899 ((HttpMessageConverter<Object>) messageConverter).write( 900 requestBody, requestContentType, httpRequest); 901 return; 902 } 903 } 904 String message = "Could not write request: no suitable HttpMessageConverter found for request type [" + 905 requestBodyClass.getName() + "]"; 906 if (requestContentType != null) { 907 message += " and content type [" + requestContentType + "]"; 908 } 909 throw new RestClientException(message); 910 } 911 } 912 } 913 914 915 /** 916 * Response extractor for {@link HttpEntity}. 917 */ 918 private class ResponseEntityResponseExtractor<T> implements ResponseExtractor<ResponseEntity<T>> { 919 920 private final HttpMessageConverterExtractor<T> delegate; 921 922 public ResponseEntityResponseExtractor(Type responseType) { 923 if (responseType != null && Void.class != responseType) { 924 this.delegate = new HttpMessageConverterExtractor<T>(responseType, getMessageConverters(), logger); 925 } 926 else { 927 this.delegate = null; 928 } 929 } 930 931 @Override 932 public ResponseEntity<T> extractData(ClientHttpResponse response) throws IOException { 933 if (this.delegate != null) { 934 T body = this.delegate.extractData(response); 935 return ResponseEntity.status(response.getRawStatusCode()).headers(response.getHeaders()).body(body); 936 } 937 else { 938 return ResponseEntity.status(response.getRawStatusCode()).headers(response.getHeaders()).build(); 939 } 940 } 941 } 942 943 944 /** 945 * Response extractor that extracts the response {@link HttpHeaders}. 946 */ 947 private static class HeadersExtractor implements ResponseExtractor<HttpHeaders> { 948 949 @Override 950 public HttpHeaders extractData(ClientHttpResponse response) throws IOException { 951 return response.getHeaders(); 952 } 953 } 954 955}