github.com/phrase/openapi@v0.0.0-20240514140800-49e8a106740e/openapi-generator/templates/java/libraries/webclient/ApiClient.mustache (about) 1 package {{invokerPackage}}; 2 3 import com.fasterxml.jackson.databind.DeserializationFeature; 4 import com.fasterxml.jackson.databind.ObjectMapper; 5 import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; 6 import org.openapitools.jackson.nullable.JsonNullableModule; 7 import org.apache.commons.logging.Log; 8 import org.apache.commons.logging.LogFactory; 9 import org.springframework.core.ParameterizedTypeReference; 10 import org.springframework.http.HttpHeaders; 11 import org.springframework.http.HttpMethod; 12 import org.springframework.http.HttpRequest; 13 import org.springframework.http.HttpStatus; 14 import org.springframework.http.InvalidMediaTypeException; 15 import org.springframework.http.MediaType; 16 import org.springframework.http.RequestEntity; 17 import org.springframework.http.RequestEntity.BodyBuilder; 18 import org.springframework.http.ResponseEntity; 19 import org.springframework.http.client.BufferingClientHttpRequestFactory; 20 import org.springframework.http.client.ClientHttpRequestExecution; 21 import org.springframework.http.client.ClientHttpRequestInterceptor; 22 import org.springframework.http.client.ClientHttpResponse; 23 import org.springframework.http.codec.json.Jackson2JsonDecoder; 24 import org.springframework.http.codec.json.Jackson2JsonEncoder; 25 import org.springframework.util.LinkedMultiValueMap; 26 import org.springframework.util.MultiValueMap; 27 import org.springframework.util.StringUtils; 28 import org.springframework.http.client.reactive.ClientHttpRequest; 29 import org.springframework.web.client.RestClientException; 30 import org.springframework.web.util.UriComponentsBuilder; 31 import org.springframework.web.reactive.function.client.WebClient; 32 import org.springframework.web.reactive.function.client.ClientResponse; 33 import org.springframework.web.reactive.function.BodyInserter; 34 import org.springframework.web.reactive.function.BodyInserters; 35 import org.springframework.web.reactive.function.client.ExchangeStrategies; 36 import reactor.core.publisher.Mono; 37 import reactor.core.publisher.Flux; 38 import java.util.Optional; 39 40 import java.io.BufferedReader; 41 import java.io.IOException; 42 import java.io.InputStream; 43 import java.io.InputStreamReader; 44 import java.io.UnsupportedEncodingException; 45 import java.net.URLEncoder; 46 import java.nio.charset.StandardCharsets; 47 import java.text.DateFormat; 48 import java.text.ParseException; 49 import java.util.Arrays; 50 import java.util.ArrayList; 51 import java.util.Collection; 52 import java.util.Collections; 53 import java.util.Date; 54 import java.util.HashMap; 55 import java.util.Iterator; 56 import java.util.List; 57 import java.util.Map; 58 import java.util.Map.Entry; 59 import java.util.TimeZone; 60 61 import {{invokerPackage}}.auth.Authentication; 62 import {{invokerPackage}}.auth.HttpBasicAuth; 63 import {{invokerPackage}}.auth.HttpBearerAuth; 64 import {{invokerPackage}}.auth.ApiKeyAuth; 65 {{#hasOAuthMethods}} 66 import {{invokerPackage}}.auth.OAuth; 67 {{/hasOAuthMethods}} 68 69 {{>generatedAnnotation}} 70 public class ApiClient { 71 public enum CollectionFormat { 72 CSV(","), TSV("\t"), SSV(" "), PIPES("|"), MULTI(null); 73 74 private final String separator; 75 private CollectionFormat(String separator) { 76 this.separator = separator; 77 } 78 79 private String collectionToString(Collection<?> collection) { 80 return StringUtils.collectionToDelimitedString(collection, separator); 81 } 82 } 83 84 private HttpHeaders defaultHeaders = new HttpHeaders(); 85 private MultiValueMap<String, String> defaultCookies = new LinkedMultiValueMap<String, String>(); 86 87 private String basePath = "{{basePath}}"; 88 89 private final WebClient webClient; 90 private final DateFormat dateFormat; 91 92 private Map<String, Authentication> authentications; 93 94 95 public ApiClient() { 96 this.dateFormat = createDefaultDateFormat(); 97 ObjectMapper mapper = new ObjectMapper(); 98 mapper.setDateFormat(dateFormat); 99 mapper.registerModule(new JavaTimeModule()); 100 mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); 101 JsonNullableModule jnm = new JsonNullableModule(); 102 mapper.registerModule(jnm); 103 104 this.webClient = buildWebClient(mapper); 105 this.init(); 106 } 107 108 public ApiClient(ObjectMapper mapper, DateFormat format) { 109 this(buildWebClient(mapper.copy()), format); 110 } 111 112 public ApiClient(WebClient webClient, ObjectMapper mapper, DateFormat format) { 113 this(Optional.ofNullable(webClient).orElseGet(() ->buildWebClient(mapper.copy())), format); 114 } 115 116 private ApiClient(WebClient webClient, DateFormat format) { 117 this.webClient = webClient; 118 this.dateFormat = format; 119 this.init(); 120 } 121 122 public DateFormat createDefaultDateFormat() { 123 DateFormat dateFormat = new RFC3339DateFormat(); 124 dateFormat.setTimeZone(TimeZone.getTimeZone("UTC")); 125 return dateFormat; 126 } 127 128 protected void init() { 129 // Setup authentications (key: authentication name, value: authentication). 130 authentications = new HashMap<String, Authentication>();{{#authMethods}}{{#isBasic}}{{#isBasicBasic}} 131 authentications.put("{{name}}", new HttpBasicAuth());{{/isBasicBasic}}{{^isBasicBasic}} 132 authentications.put("{{name}}", new HttpBearerAuth("{{scheme}}"));{{/isBasicBasic}}{{/isBasic}}{{#isApiKey}} 133 authentications.put("{{name}}", new ApiKeyAuth({{#isKeyInHeader}}"header"{{/isKeyInHeader}}{{#isKeyInQuery}}"query"{{/isKeyInQuery}}{{#isKeyInCookie}}"cookie"{{/isKeyInCookie}}, "{{keyParamName}}"));{{/isApiKey}}{{#isOAuth}} 134 authentications.put("{{name}}", new OAuth());{{/isOAuth}}{{/authMethods}} 135 // Prevent the authentications from being modified. 136 authentications = Collections.unmodifiableMap(authentications); 137 } 138 139 /** 140 * Build the RestTemplate used to make HTTP requests. 141 * @return RestTemplate 142 */ 143 public static WebClient buildWebClient(ObjectMapper mapper) { 144 ExchangeStrategies strategies = ExchangeStrategies 145 .builder() 146 .codecs(clientDefaultCodecsConfigurer -> { 147 clientDefaultCodecsConfigurer.defaultCodecs().jackson2JsonEncoder(new Jackson2JsonEncoder(mapper, MediaType.APPLICATION_JSON)); 148 clientDefaultCodecsConfigurer.defaultCodecs().jackson2JsonDecoder(new Jackson2JsonDecoder(mapper, MediaType.APPLICATION_JSON)); 149 }).build(); 150 WebClient.Builder webClient = WebClient.builder().exchangeStrategies(strategies); 151 return webClient.build(); 152 } 153 154 155 /** 156 * Get the current base path 157 * @return String the base path 158 */ 159 public String getBasePath() { 160 return basePath; 161 } 162 163 /** 164 * Set the base path, which should include the host 165 * @param basePath the base path 166 * @return ApiClient this client 167 */ 168 public ApiClient setBasePath(String basePath) { 169 this.basePath = basePath; 170 return this; 171 } 172 173 /** 174 * Get authentications (key: authentication name, value: authentication). 175 * @return Map the currently configured authentication types 176 */ 177 public Map<String, Authentication> getAuthentications() { 178 return authentications; 179 } 180 181 /** 182 * Get authentication for the given name. 183 * 184 * @param authName The authentication name 185 * @return The authentication, null if not found 186 */ 187 public Authentication getAuthentication(String authName) { 188 return authentications.get(authName); 189 } 190 191 /** 192 * Helper method to set access token for the first Bearer authentication. 193 * @param bearerToken Bearer token 194 */ 195 public void setBearerToken(String bearerToken) { 196 for (Authentication auth : authentications.values()) { 197 if (auth instanceof HttpBearerAuth) { 198 ((HttpBearerAuth) auth).setBearerToken(bearerToken); 199 return; 200 } 201 } 202 throw new RuntimeException("No Bearer authentication configured!"); 203 } 204 205 /** 206 * Helper method to set username for the first HTTP basic authentication. 207 * @param username the username 208 */ 209 public void setUsername(String username) { 210 for (Authentication auth : authentications.values()) { 211 if (auth instanceof HttpBasicAuth) { 212 ((HttpBasicAuth) auth).setUsername(username); 213 return; 214 } 215 } 216 throw new RuntimeException("No HTTP basic authentication configured!"); 217 } 218 219 /** 220 * Helper method to set password for the first HTTP basic authentication. 221 * @param password the password 222 */ 223 public void setPassword(String password) { 224 for (Authentication auth : authentications.values()) { 225 if (auth instanceof HttpBasicAuth) { 226 ((HttpBasicAuth) auth).setPassword(password); 227 return; 228 } 229 } 230 throw new RuntimeException("No HTTP basic authentication configured!"); 231 } 232 233 /** 234 * Helper method to set API key value for the first API key authentication. 235 * @param apiKey the API key 236 */ 237 public void setApiKey(String apiKey) { 238 for (Authentication auth : authentications.values()) { 239 if (auth instanceof ApiKeyAuth) { 240 ((ApiKeyAuth) auth).setApiKey(apiKey); 241 return; 242 } 243 } 244 throw new RuntimeException("No API key authentication configured!"); 245 } 246 247 /** 248 * Helper method to set API key prefix for the first API key authentication. 249 * @param apiKeyPrefix the API key prefix 250 */ 251 public void setApiKeyPrefix(String apiKeyPrefix) { 252 for (Authentication auth : authentications.values()) { 253 if (auth instanceof ApiKeyAuth) { 254 ((ApiKeyAuth) auth).setApiKeyPrefix(apiKeyPrefix); 255 return; 256 } 257 } 258 throw new RuntimeException("No API key authentication configured!"); 259 } 260 261 {{#hasOAuthMethods}} 262 /** 263 * Helper method to set access token for the first OAuth2 authentication. 264 * @param accessToken the access token 265 */ 266 public void setAccessToken(String accessToken) { 267 for (Authentication auth : authentications.values()) { 268 if (auth instanceof OAuth) { 269 ((OAuth) auth).setAccessToken(accessToken); 270 return; 271 } 272 } 273 throw new RuntimeException("No OAuth2 authentication configured!"); 274 } 275 276 {{/hasOAuthMethods}} 277 /** 278 * Set the User-Agent header's value (by adding to the default header map). 279 * @param userAgent the user agent string 280 * @return ApiClient this client 281 */ 282 public ApiClient setUserAgent(String userAgent) { 283 addDefaultHeader("User-Agent", userAgent); 284 return this; 285 } 286 287 /** 288 * Add a default header. 289 * 290 * @param name The header's name 291 * @param value The header's value 292 * @return ApiClient this client 293 */ 294 public ApiClient addDefaultHeader(String name, String value) { 295 if (defaultHeaders.containsKey(name)) { 296 defaultHeaders.remove(name); 297 } 298 defaultHeaders.add(name, value); 299 return this; 300 } 301 302 /** 303 * Add a default cookie. 304 * 305 * @param name The cookie's name 306 * @param value The cookie's value 307 * @return ApiClient this client 308 */ 309 public ApiClient addDefaultCookie(String name, String value) { 310 if (defaultCookies.containsKey(name)) { 311 defaultCookies.remove(name); 312 } 313 defaultCookies.add(name, value); 314 return this; 315 } 316 317 /** 318 * Get the date format used to parse/format date parameters. 319 * @return DateFormat format 320 */ 321 public DateFormat getDateFormat() { 322 return dateFormat; 323 } 324 325 /** 326 * Parse the given string into Date object. 327 */ 328 public Date parseDate(String str) { 329 try { 330 return dateFormat.parse(str); 331 } catch (ParseException e) { 332 throw new RuntimeException(e); 333 } 334 } 335 336 /** 337 * Format the given Date object into string. 338 */ 339 public String formatDate(Date date) { 340 return dateFormat.format(date); 341 } 342 343 /** 344 * Format the given parameter object into string. 345 * @param param the object to convert 346 * @return String the parameter represented as a String 347 */ 348 public String parameterToString(Object param) { 349 if (param == null) { 350 return ""; 351 } else if (param instanceof Date) { 352 return formatDate( (Date) param); 353 } else if (param instanceof Collection) { 354 StringBuilder b = new StringBuilder(); 355 for(Object o : (Collection<?>) param) { 356 if(b.length() > 0) { 357 b.append(","); 358 } 359 b.append(String.valueOf(o)); 360 } 361 return b.toString(); 362 } else { 363 return String.valueOf(param); 364 } 365 } 366 367 /** 368 * Converts a parameter to a {@link MultiValueMap} for use in REST requests 369 * @param collectionFormat The format to convert to 370 * @param name The name of the parameter 371 * @param value The parameter's value 372 * @return a Map containing the String value(s) of the input parameter 373 */ 374 public MultiValueMap<String, String> parameterToMultiValueMap(CollectionFormat collectionFormat, String name, Object value) { 375 final MultiValueMap<String, String> params = new LinkedMultiValueMap<String, String>(); 376 377 if (name == null || name.isEmpty() || value == null) { 378 return params; 379 } 380 381 if(collectionFormat == null) { 382 collectionFormat = CollectionFormat.CSV; 383 } 384 385 Collection<?> valueCollection = null; 386 if (value instanceof Collection) { 387 valueCollection = (Collection<?>) value; 388 } else { 389 params.add(name, parameterToString(value)); 390 return params; 391 } 392 393 if (valueCollection.isEmpty()){ 394 return params; 395 } 396 397 if (collectionFormat.equals(CollectionFormat.MULTI)) { 398 for (Object item : valueCollection) { 399 params.add(name, parameterToString(item)); 400 } 401 return params; 402 } 403 404 List<String> values = new ArrayList<String>(); 405 for(Object o : valueCollection) { 406 values.add(parameterToString(o)); 407 } 408 params.add(name, collectionFormat.collectionToString(values)); 409 410 return params; 411 } 412 413 /** 414 * Check if the given {@code String} is a JSON MIME. 415 * @param mediaType the input MediaType 416 * @return boolean true if the MediaType represents JSON, false otherwise 417 */ 418 public boolean isJsonMime(String mediaType) { 419 // "* / *" is default to JSON 420 if ("*/*".equals(mediaType)) { 421 return true; 422 } 423 424 try { 425 return isJsonMime(MediaType.parseMediaType(mediaType)); 426 } catch (InvalidMediaTypeException e) { 427 } 428 return false; 429 } 430 431 /** 432 * Check if the given MIME is a JSON MIME. 433 * JSON MIME examples: 434 * application/json 435 * application/json; charset=UTF8 436 * APPLICATION/JSON 437 * @param mediaType the input MediaType 438 * @return boolean true if the MediaType represents JSON, false otherwise 439 */ 440 public boolean isJsonMime(MediaType mediaType) { 441 return mediaType != null && (MediaType.APPLICATION_JSON.isCompatibleWith(mediaType) || mediaType.getSubtype().matches("^.*\\+json[;]?\\s*$")); 442 } 443 444 /** 445 * Select the Accept header's value from the given accepts array: 446 * if JSON exists in the given array, use it; 447 * otherwise use all of them (joining into a string) 448 * 449 * @param accepts The accepts array to select from 450 * @return List The list of MediaTypes to use for the Accept header 451 */ 452 public List<MediaType> selectHeaderAccept(String[] accepts) { 453 if (accepts.length == 0) { 454 return null; 455 } 456 for (String accept : accepts) { 457 MediaType mediaType = MediaType.parseMediaType(accept); 458 if (isJsonMime(mediaType)) { 459 return Collections.singletonList(mediaType); 460 } 461 } 462 return MediaType.parseMediaTypes(StringUtils.arrayToCommaDelimitedString(accepts)); 463 } 464 465 /** 466 * Select the Content-Type header's value from the given array: 467 * if JSON exists in the given array, use it; 468 * otherwise use the first one of the array. 469 * 470 * @param contentTypes The Content-Type array to select from 471 * @return MediaType The Content-Type header to use. If the given array is empty, JSON will be used. 472 */ 473 public MediaType selectHeaderContentType(String[] contentTypes) { 474 if (contentTypes.length == 0) { 475 return MediaType.APPLICATION_JSON; 476 } 477 for (String contentType : contentTypes) { 478 MediaType mediaType = MediaType.parseMediaType(contentType); 479 if (isJsonMime(mediaType)) { 480 return mediaType; 481 } 482 } 483 return MediaType.parseMediaType(contentTypes[0]); 484 } 485 486 /** 487 * Select the body to use for the request 488 * @param obj the body object 489 * @param formParams the form parameters 490 * @param contentType the content type of the request 491 * @return Object the selected body 492 */ 493 protected BodyInserter<?, ? super ClientHttpRequest> selectBody(Object obj, MultiValueMap<String, Object> formParams, MediaType contentType) { 494 if(MediaType.APPLICATION_FORM_URLENCODED.equals(contentType)) { 495 MultiValueMap<String, String> map = new LinkedMultiValueMap(); 496 497 formParams 498 .toSingleValueMap() 499 .entrySet() 500 .forEach(es -> map.add(es.getKey(), String.valueOf(es.getValue()))); 501 502 return BodyInserters.fromFormData(map); 503 } else if(MediaType.MULTIPART_FORM_DATA.equals(contentType)) { 504 return BodyInserters.fromMultipartData(formParams); 505 } else { 506 return obj != null ? BodyInserters.fromObject(obj) : null; 507 } 508 } 509 510 /** 511 * Invoke API by sending HTTP request with the given options. 512 * 513 * @param <T> the return type to use 514 * @param path The sub-path of the HTTP URL 515 * @param method The request method 516 * @param pathParams The path parameters 517 * @param queryParams The query parameters 518 * @param body The request body object 519 * @param headerParams The header parameters 520 * @param formParams The form parameters 521 * @param accept The request's Accept header 522 * @param contentType The request's Content-Type header 523 * @param authNames The authentications to apply 524 * @param returnType The return type into which to deserialize the response 525 * @return The response body in chosen type 526 */ 527 public <T> Mono<T> invokeAPI(String path, HttpMethod method, Map<String, Object> pathParams, MultiValueMap<String, String> queryParams, Object body, HttpHeaders headerParams, MultiValueMap<String, String> cookieParams, MultiValueMap<String, Object> formParams, List<MediaType> accept, MediaType contentType, String[] authNames, ParameterizedTypeReference<T> returnType) throws RestClientException { 528 final WebClient.RequestBodySpec requestBuilder = prepareRequest(path, method, pathParams, queryParams, body, headerParams, cookieParams, formParams, accept, contentType, authNames); 529 return requestBuilder.retrieve().bodyToMono(returnType); 530 } 531 532 /** 533 * Invoke API by sending HTTP request with the given options. 534 * 535 * @param <T> the return type to use 536 * @param path The sub-path of the HTTP URL 537 * @param method The request method 538 * @param pathParams The path parameters 539 * @param queryParams The query parameters 540 * @param body The request body object 541 * @param headerParams The header parameters 542 * @param formParams The form parameters 543 * @param accept The request's Accept header 544 * @param contentType The request's Content-Type header 545 * @param authNames The authentications to apply 546 * @param returnType The return type into which to deserialize the response 547 * @return The response body in chosen type 548 */ 549 public <T> Flux<T> invokeFluxAPI(String path, HttpMethod method, Map<String, Object> pathParams, MultiValueMap<String, String> queryParams, Object body, HttpHeaders headerParams, MultiValueMap<String, String> cookieParams, MultiValueMap<String, Object> formParams, List<MediaType> accept, MediaType contentType, String[] authNames, ParameterizedTypeReference<T> returnType) throws RestClientException { 550 final WebClient.RequestBodySpec requestBuilder = prepareRequest(path, method, pathParams, queryParams, body, headerParams, cookieParams, formParams, accept, contentType, authNames); 551 return requestBuilder.retrieve().bodyToFlux(returnType); 552 } 553 554 private WebClient.RequestBodySpec prepareRequest(String path, HttpMethod method, Map<String, Object> pathParams, MultiValueMap<String, String> queryParams, Object body, HttpHeaders headerParams, MultiValueMap<String, String> cookieParams, MultiValueMap<String, Object> formParams, List<MediaType> accept, MediaType contentType, String[] authNames) { 555 updateParamsForAuth(authNames, queryParams, headerParams, cookieParams); 556 557 final UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(basePath).path(path); 558 if (queryParams != null) { 559 builder.queryParams(queryParams); 560 } 561 562 final WebClient.RequestBodySpec requestBuilder = webClient.method(method).uri(builder.build(false).toUriString(), pathParams); 563 if(accept != null) { 564 requestBuilder.accept(accept.toArray(new MediaType[accept.size()])); 565 } 566 if(contentType != null) { 567 requestBuilder.contentType(contentType); 568 } 569 570 addHeadersToRequest(headerParams, requestBuilder); 571 addHeadersToRequest(defaultHeaders, requestBuilder); 572 addCookiesToRequest(cookieParams, requestBuilder); 573 addCookiesToRequest(defaultCookies, requestBuilder); 574 575 requestBuilder.body(selectBody(body, formParams, contentType)); 576 return requestBuilder; 577 } 578 579 /** 580 * Add headers to the request that is being built 581 * @param headers The headers to add 582 * @param requestBuilder The current request 583 */ 584 protected void addHeadersToRequest(HttpHeaders headers, WebClient.RequestBodySpec requestBuilder) { 585 for (Entry<String, List<String>> entry : headers.entrySet()) { 586 List<String> values = entry.getValue(); 587 for(String value : values) { 588 if (value != null) { 589 requestBuilder.header(entry.getKey(), value); 590 } 591 } 592 } 593 } 594 595 /** 596 * Add cookies to the request that is being built 597 * @param cookies The cookies to add 598 * @param requestBuilder The current request 599 */ 600 protected void addCookiesToRequest(MultiValueMap<String, String> cookies, WebClient.RequestBodySpec requestBuilder) { 601 for (Entry<String, List<String>> entry : cookies.entrySet()) { 602 List<String> values = entry.getValue(); 603 for(String value : values) { 604 if (value != null) { 605 requestBuilder.cookie(entry.getKey(), value); 606 } 607 } 608 } 609 } 610 611 /** 612 * Update query and header parameters based on authentication settings. 613 * 614 * @param authNames The authentications to apply 615 * @param queryParams The query parameters 616 * @param headerParams The header parameters 617 * @param cookieParams the cookie parameters 618 */ 619 private void updateParamsForAuth(String[] authNames, MultiValueMap<String, String> queryParams, HttpHeaders headerParams, MultiValueMap<String, String> cookieParams) { 620 for (String authName : authNames) { 621 Authentication auth = authentications.get(authName); 622 if (auth == null) { 623 throw new RestClientException("Authentication undefined: " + authName); 624 } 625 auth.applyToParams(queryParams, headerParams, cookieParams); 626 } 627 } 628 629 /** 630 * Formats the specified collection path parameter to a string value. 631 * 632 * @param collectionFormat The collection format of the parameter. 633 * @param values The values of the parameter. 634 * @return String representation of the parameter 635 */ 636 public String collectionPathParameterToString(CollectionFormat collectionFormat, Collection<?> values) { 637 // create the value based on the collection format 638 if (CollectionFormat.MULTI.equals(collectionFormat)) { 639 // not valid for path params 640 return parameterToString(values); 641 } 642 643 // collectionFormat is assumed to be "csv" by default 644 if(collectionFormat == null) { 645 collectionFormat = CollectionFormat.CSV; 646 } 647 648 return collectionFormat.collectionToString(values); 649 } 650 651 private class ApiClientHttpRequestInterceptor implements ClientHttpRequestInterceptor { 652 private final Log log = LogFactory.getLog(ApiClientHttpRequestInterceptor.class); 653 654 @Override 655 public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException { 656 logRequest(request, body); 657 ClientHttpResponse response = execution.execute(request, body); 658 logResponse(response); 659 return response; 660 } 661 662 private void logRequest(HttpRequest request, byte[] body) throws UnsupportedEncodingException { 663 log.info("URI: " + request.getURI()); 664 log.info("HTTP Method: " + request.getMethod()); 665 log.info("HTTP Headers: " + headersToString(request.getHeaders())); 666 log.info("Request Body: " + new String(body, StandardCharsets.UTF_8)); 667 } 668 669 private void logResponse(ClientHttpResponse response) throws IOException { 670 log.info("HTTP Status Code: " + response.getRawStatusCode()); 671 log.info("Status Text: " + response.getStatusText()); 672 log.info("HTTP Headers: " + headersToString(response.getHeaders())); 673 log.info("Response Body: " + bodyToString(response.getBody())); 674 } 675 676 private String headersToString(HttpHeaders headers) { 677 StringBuilder builder = new StringBuilder(); 678 for(Entry<String, List<String>> entry : headers.entrySet()) { 679 builder.append(entry.getKey()).append("=["); 680 for(String value : entry.getValue()) { 681 builder.append(value).append(","); 682 } 683 builder.setLength(builder.length() - 1); // Get rid of trailing comma 684 builder.append("],"); 685 } 686 builder.setLength(builder.length() - 1); // Get rid of trailing comma 687 return builder.toString(); 688 } 689 690 private String bodyToString(InputStream body) throws IOException { 691 StringBuilder builder = new StringBuilder(); 692 BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(body, StandardCharsets.UTF_8)); 693 String line = bufferedReader.readLine(); 694 while (line != null) { 695 builder.append(line).append(System.lineSeparator()); 696 line = bufferedReader.readLine(); 697 } 698 bufferedReader.close(); 699 return builder.toString(); 700 } 701 } 702 }