17
17
package org .springframework .web .client ;
18
18
19
19
import java .io .IOException ;
20
+ import java .io .InputStream ;
21
+ import java .io .UncheckedIOException ;
20
22
import java .lang .reflect .ParameterizedType ;
21
23
import java .lang .reflect .Type ;
22
24
import java .net .URI ;
@@ -185,6 +187,61 @@ public Builder mutate() {
185
187
return new DefaultRestClientBuilder (this .builder );
186
188
}
187
189
190
+ @ SuppressWarnings ({"rawtypes" , "unchecked" })
191
+ private <T > T readWithMessageConverters (ClientHttpResponse clientResponse , Runnable callback , Type bodyType , Class <T > bodyClass ) {
192
+ MediaType contentType = getContentType (clientResponse );
193
+
194
+ try (clientResponse ) {
195
+ callback .run ();
196
+
197
+ for (HttpMessageConverter <?> messageConverter : this .messageConverters ) {
198
+ if (messageConverter instanceof GenericHttpMessageConverter genericHttpMessageConverter ) {
199
+ if (genericHttpMessageConverter .canRead (bodyType , null , contentType )) {
200
+ if (logger .isDebugEnabled ()) {
201
+ logger .debug ("Reading to [" + ResolvableType .forType (bodyType ) + "]" );
202
+ }
203
+ return (T ) genericHttpMessageConverter .read (bodyType , null , clientResponse );
204
+ }
205
+ }
206
+ if (messageConverter .canRead (bodyClass , contentType )) {
207
+ if (logger .isDebugEnabled ()) {
208
+ logger .debug ("Reading to [" + bodyClass .getName () + "] as \" " + contentType + "\" " );
209
+ }
210
+ return (T ) messageConverter .read ((Class )bodyClass , clientResponse );
211
+ }
212
+ }
213
+ throw new UnknownContentTypeException (bodyType , contentType ,
214
+ clientResponse .getStatusCode (), clientResponse .getStatusText (),
215
+ clientResponse .getHeaders (), RestClientUtils .getBody (clientResponse ));
216
+ }
217
+ catch (UncheckedIOException | IOException | HttpMessageNotReadableException ex ) {
218
+ throw new RestClientException ("Error while extracting response for type [" +
219
+ ResolvableType .forType (bodyType ) + "] and content type [" + contentType + "]" , ex );
220
+ }
221
+ }
222
+
223
+ private static MediaType getContentType (ClientHttpResponse clientResponse ) {
224
+ MediaType contentType = clientResponse .getHeaders ().getContentType ();
225
+ if (contentType == null ) {
226
+ contentType = MediaType .APPLICATION_OCTET_STREAM ;
227
+ }
228
+ return contentType ;
229
+ }
230
+
231
+ @ SuppressWarnings ("unchecked" )
232
+ private static <T > Class <T > bodyClass (Type type ) {
233
+ if (type instanceof Class <?> clazz ) {
234
+ return (Class <T >) clazz ;
235
+ }
236
+ if (type instanceof ParameterizedType parameterizedType &&
237
+ parameterizedType .getRawType () instanceof Class <?> rawType ) {
238
+ return (Class <T >) rawType ;
239
+ }
240
+ return (Class <T >) Object .class ;
241
+ }
242
+
243
+
244
+
188
245
189
246
private class DefaultRequestBodyUriSpec implements RequestBodyUriSpec {
190
247
@@ -409,7 +466,8 @@ private <T> T exchangeInternal(ExchangeFunction<T> exchangeFunction, boolean clo
409
466
}
410
467
clientResponse = clientRequest .execute ();
411
468
observationContext .setResponse (clientResponse );
412
- return exchangeFunction .exchange (clientRequest , clientResponse );
469
+ ConvertibleClientHttpResponse convertibleWrapper = new DefaultConvertibleClientHttpResponse (clientResponse );
470
+ return exchangeFunction .exchange (clientRequest , convertibleWrapper );
413
471
}
414
472
catch (IOException ex ) {
415
473
ResourceAccessException resourceAccessException = createResourceAccessException (uri , this .httpMethod , ex );
@@ -542,14 +600,14 @@ private ResponseSpec onStatusInternal(StatusHandler statusHandler) {
542
600
543
601
@ Override
544
602
public <T > T body (Class <T > bodyType ) {
545
- return readWithMessageConverters (bodyType , bodyType );
603
+ return readBody (bodyType , bodyType );
546
604
}
547
605
548
606
@ Override
549
607
public <T > T body (ParameterizedTypeReference <T > bodyType ) {
550
608
Type type = bodyType .getType ();
551
609
Class <T > bodyClass = bodyClass (type );
552
- return readWithMessageConverters (type , bodyClass );
610
+ return readBody (type , bodyClass );
553
611
}
554
612
555
613
@ Override
@@ -565,7 +623,7 @@ public <T> ResponseEntity<T> toEntity(ParameterizedTypeReference<T> bodyType) {
565
623
}
566
624
567
625
private <T > ResponseEntity <T > toEntityInternal (Type bodyType , Class <T > bodyClass ) {
568
- T body = readWithMessageConverters (bodyType , bodyClass );
626
+ T body = readBody (bodyType , bodyClass );
569
627
try {
570
628
return ResponseEntity .status (this .clientResponse .getStatusCode ())
571
629
.headers (this .clientResponse .getHeaders ())
@@ -579,77 +637,96 @@ private <T> ResponseEntity<T> toEntityInternal(Type bodyType, Class<T> bodyClass
579
637
@ Override
580
638
public ResponseEntity <Void > toBodilessEntity () {
581
639
try (this .clientResponse ) {
582
- applyStatusHandlers (this . clientRequest , this . clientResponse );
640
+ applyStatusHandlers ();
583
641
return ResponseEntity .status (this .clientResponse .getStatusCode ())
584
642
.headers (this .clientResponse .getHeaders ())
585
643
.build ();
586
644
}
645
+ catch (UncheckedIOException ex ) {
646
+ throw new ResourceAccessException ("Could not retrieve response status code: " + ex .getMessage (), ex .getCause ());
647
+ }
587
648
catch (IOException ex ) {
588
649
throw new ResourceAccessException ("Could not retrieve response status code: " + ex .getMessage (), ex );
589
650
}
590
651
}
591
652
592
- @ SuppressWarnings ("unchecked" )
593
- private static <T > Class <T > bodyClass (Type type ) {
594
- if (type instanceof Class <?> clazz ) {
595
- return (Class <T >) clazz ;
596
- }
597
- if (type instanceof ParameterizedType parameterizedType &&
598
- parameterizedType .getRawType () instanceof Class <?> rawType ) {
599
- return (Class <T >) rawType ;
600
- }
601
- return (Class <T >) Object .class ;
602
- }
603
653
604
- @ SuppressWarnings ({ "rawtypes" , "unchecked" })
605
- private < T > T readWithMessageConverters (Type bodyType , Class < T > bodyClass ) {
606
- MediaType contentType = getContentType ( );
654
+ private < T > T readBody ( Type bodyType , Class < T > bodyClass ) {
655
+ return DefaultRestClient . this . readWithMessageConverters (this . clientResponse , this :: applyStatusHandlers ,
656
+ bodyType , bodyClass );
607
657
608
- try (this .clientResponse ) {
609
- applyStatusHandlers (this .clientRequest , this .clientResponse );
610
-
611
- for (HttpMessageConverter <?> messageConverter : DefaultRestClient .this .messageConverters ) {
612
- if (messageConverter instanceof GenericHttpMessageConverter genericHttpMessageConverter ) {
613
- if (genericHttpMessageConverter .canRead (bodyType , null , contentType )) {
614
- if (logger .isDebugEnabled ()) {
615
- logger .debug ("Reading to [" + ResolvableType .forType (bodyType ) + "]" );
616
- }
617
- return (T ) genericHttpMessageConverter .read (bodyType , null , this .clientResponse );
618
- }
619
- }
620
- if (messageConverter .canRead (bodyClass , contentType )) {
621
- if (logger .isDebugEnabled ()) {
622
- logger .debug ("Reading to [" + bodyClass .getName () + "] as \" " + contentType + "\" " );
623
- }
624
- return (T ) messageConverter .read ((Class )bodyClass , this .clientResponse );
658
+ }
659
+
660
+ private void applyStatusHandlers () {
661
+ try {
662
+ ClientHttpResponse response = this .clientResponse ;
663
+ if (response instanceof DefaultConvertibleClientHttpResponse convertibleResponse ) {
664
+ response = convertibleResponse .delegate ;
665
+ }
666
+ for (StatusHandler handler : this .statusHandlers ) {
667
+ if (handler .test (response )) {
668
+ handler .handle (this .clientRequest , response );
669
+ return ;
625
670
}
626
671
}
627
- throw new UnknownContentTypeException (bodyType , contentType ,
628
- this .clientResponse .getStatusCode (), this .clientResponse .getStatusText (),
629
- this .clientResponse .getHeaders (), RestClientUtils .getBody (this .clientResponse ));
630
672
}
631
- catch (IOException | HttpMessageNotReadableException ex ) {
632
- throw new RestClientException ("Error while extracting response for type [" +
633
- ResolvableType .forType (bodyType ) + "] and content type [" + contentType + "]" , ex );
673
+ catch (IOException ex ) {
674
+ throw new UncheckedIOException (ex );
634
675
}
635
676
}
677
+ }
636
678
637
- private MediaType getContentType () {
638
- MediaType contentType = this .clientResponse .getHeaders ().getContentType ();
639
- if (contentType == null ) {
640
- contentType = MediaType .APPLICATION_OCTET_STREAM ;
641
- }
642
- return contentType ;
679
+
680
+ private class DefaultConvertibleClientHttpResponse implements RequestHeadersSpec .ConvertibleClientHttpResponse {
681
+
682
+ private final ClientHttpResponse delegate ;
683
+
684
+
685
+ public DefaultConvertibleClientHttpResponse (ClientHttpResponse delegate ) {
686
+ this .delegate = delegate ;
643
687
}
644
688
645
- private void applyStatusHandlers (HttpRequest request , ClientHttpResponse response ) throws IOException {
646
- for (StatusHandler handler : this .statusHandlers ) {
647
- if (handler .test (response )) {
648
- handler .handle (request , response );
649
- return ;
650
- }
651
- }
689
+
690
+ @ Nullable
691
+ @ Override
692
+ public <T > T bodyTo (Class <T > bodyType ) {
693
+ return readWithMessageConverters (this .delegate , () -> {} , bodyType , bodyType );
694
+ }
695
+
696
+ @ Nullable
697
+ @ Override
698
+ public <T > T bodyTo (ParameterizedTypeReference <T > bodyType ) {
699
+ Type type = bodyType .getType ();
700
+ Class <T > bodyClass = bodyClass (type );
701
+ return readWithMessageConverters (this .delegate , () -> {} , type , bodyClass );
702
+ }
703
+
704
+ @ Override
705
+ public InputStream getBody () throws IOException {
706
+ return this .delegate .getBody ();
707
+ }
708
+
709
+ @ Override
710
+ public HttpHeaders getHeaders () {
711
+ return this .delegate .getHeaders ();
712
+ }
713
+
714
+ @ Override
715
+ public HttpStatusCode getStatusCode () throws IOException {
716
+ return this .delegate .getStatusCode ();
652
717
}
718
+
719
+ @ Override
720
+ public String getStatusText () throws IOException {
721
+ return this .delegate .getStatusText ();
722
+ }
723
+
724
+ @ Override
725
+ public void close () {
726
+ this .delegate .close ();
727
+ }
728
+
653
729
}
654
730
731
+
655
732
}
0 commit comments