0
# HTTP Clients
1
2
Comprehensive HTTP client support for consuming REST APIs and web services. Spring Web provides both traditional template-based clients (RestTemplate) and modern fluent clients (RestClient), along with configurable client infrastructure.
3
4
## Capabilities
5
6
### RestClient - Modern Fluent API
7
8
Modern synchronous HTTP client with a fluent API design for type-safe and readable HTTP operations.
9
10
```java { .api }
11
/**
12
* Fluent synchronous HTTP client interface
13
*/
14
interface RestClient {
15
// HTTP method specifications
16
RequestHeadersUriSpec<?> get();
17
RequestBodyUriSpec post();
18
RequestBodyUriSpec put();
19
RequestBodyUriSpec patch();
20
RequestHeadersUriSpec<?> delete();
21
RequestHeadersUriSpec<?> head();
22
RequestHeadersUriSpec<?> options();
23
RequestBodyUriSpec method(HttpMethod method);
24
25
// Factory methods
26
static RestClient create();
27
static RestClient create(String baseUrl);
28
static RestClient create(RestTemplate restTemplate);
29
static RestClient.Builder builder();
30
static RestClient.Builder builder(RestTemplate restTemplate);
31
}
32
33
/**
34
* RestClient builder for configuration
35
*/
36
interface RestClient.Builder {
37
RestClient.Builder baseUrl(String baseUrl);
38
RestClient.Builder defaultUriVariables(Map<String, ?> defaultUriVariables);
39
RestClient.Builder defaultHeader(String header, String... values);
40
RestClient.Builder defaultHeaders(Consumer<HttpHeaders> headersConsumer);
41
RestClient.Builder defaultRequest(Consumer<RequestHeadersSpec<?>> defaultRequest);
42
RestClient.Builder requestFactory(ClientHttpRequestFactory requestFactory);
43
RestClient.Builder messageConverters(Consumer<List<HttpMessageConverter<?>>> configurer);
44
RestClient.Builder requestInterceptor(ClientHttpRequestInterceptor interceptor);
45
RestClient.Builder requestInterceptors(Consumer<List<ClientHttpRequestInterceptor>> interceptorsConsumer);
46
RestClient.Builder requestInitializer(ClientHttpRequestInitializer initializer);
47
RestClient.Builder requestInitializers(Consumer<List<ClientHttpRequestInitializer>> initializersConsumer);
48
RestClient.Builder statusHandler(Predicate<HttpStatusCode> statusPredicate, ResponseErrorHandler errorHandler);
49
RestClient.Builder defaultStatusHandler(ResponseErrorHandler errorHandler);
50
RestClient.Builder observationRegistry(ObservationRegistry observationRegistry);
51
RestClient.Builder observationConvention(ClientRequestObservationConvention observationConvention);
52
RestClient build();
53
}
54
55
/**
56
* URI specification for requests without body
57
*/
58
interface RequestHeadersUriSpec<S extends RequestHeadersSpec<S>> extends UriSpec<S> {
59
// Inherited from UriSpec: uri methods
60
}
61
62
/**
63
* URI specification for requests with body
64
*/
65
interface RequestBodyUriSpec extends RequestBodySpec, RequestHeadersUriSpec<RequestBodySpec> {
66
// Combines URI and body specification
67
}
68
69
/**
70
* Headers specification for all request types
71
*/
72
interface RequestHeadersSpec<S extends RequestHeadersSpec<S>> {
73
S header(String headerName, String... headerValues);
74
S headers(Consumer<HttpHeaders> headersConsumer);
75
S accept(MediaType... acceptableMediaTypes);
76
S acceptCharset(Charset... acceptableCharsets);
77
S ifModifiedSince(ZonedDateTime ifModifiedSince);
78
S ifNoneMatch(String... ifNoneMatches);
79
80
// Execute and retrieve response
81
ResponseEntity<Void> retrieve();
82
<T> ResponseEntity<T> retrieve(Class<T> bodyType);
83
<T> ResponseEntity<T> retrieve(ParameterizedTypeReference<T> bodyType);
84
85
// Exchange for full control
86
<T> T exchange(ExchangeFunction<T> exchangeFunction);
87
}
88
89
/**
90
* Body specification for requests with content
91
*/
92
interface RequestBodySpec extends RequestHeadersSpec<RequestBodySpec> {
93
RequestBodySpec contentType(MediaType contentType);
94
RequestBodySpec contentLength(long contentLength);
95
RequestBodySpec body(Object body);
96
<T> RequestBodySpec body(T body, Class<T> bodyType);
97
<T> RequestBodySpec body(T body, ParameterizedTypeReference<T> bodyType);
98
}
99
100
/**
101
* URI specification methods
102
*/
103
interface UriSpec<S extends RequestHeadersSpec<S>> {
104
S uri(String uri, Object... uriVariables);
105
S uri(String uri, Map<String, ?> uriVariables);
106
S uri(URI uri);
107
S uri(Function<UriBuilder, URI> uriFunction);
108
}
109
```
110
111
**Usage Examples:**
112
113
```java
114
// Create RestClient
115
RestClient restClient = RestClient.create("https://api.example.com");
116
117
// Simple GET request
118
String response = restClient.get()
119
.uri("/users/{id}", 123)
120
.retrieve()
121
.body(String.class);
122
123
// POST request with JSON body
124
User user = new User("John", "john@example.com");
125
User createdUser = restClient.post()
126
.uri("/users")
127
.contentType(MediaType.APPLICATION_JSON)
128
.body(user)
129
.retrieve()
130
.body(User.class);
131
132
// GET with custom headers
133
List<User> users = restClient.get()
134
.uri("/users")
135
.header("Authorization", "Bearer token")
136
.accept(MediaType.APPLICATION_JSON)
137
.retrieve()
138
.body(new ParameterizedTypeReference<List<User>>() {});
139
140
// Error handling with exchange
141
String result = restClient.get()
142
.uri("/users/{id}", 404)
143
.exchange((request, response) -> {
144
if (response.getStatusCode().is4xxClientError()) {
145
return "User not found";
146
}
147
return response.bodyTo(String.class);
148
});
149
150
// Builder configuration
151
RestClient configuredClient = RestClient.builder()
152
.baseUrl("https://api.example.com")
153
.defaultHeader("Authorization", "Bearer token")
154
.defaultHeader("User-Agent", "MyApp/1.0")
155
.statusHandler(HttpStatus::is4xxClientError, (request, response) -> {
156
throw new ClientErrorException(response.getStatusCode());
157
})
158
.build();
159
```
160
161
### RestTemplate - Traditional Template API
162
163
Traditional synchronous HTTP client providing template method patterns for HTTP operations.
164
165
```java { .api }
166
/**
167
* Traditional synchronous client to perform HTTP requests
168
*/
169
class RestTemplate extends InterceptingHttpAccessor implements RestOperations {
170
RestTemplate();
171
RestTemplate(ClientHttpRequestFactory requestFactory);
172
RestTemplate(List<HttpMessageConverter<?>> messageConverters);
173
174
// GET operations
175
<T> T getForObject(String url, Class<T> responseType, Object... uriVariables);
176
<T> T getForObject(String url, Class<T> responseType, Map<String, ?> uriVariables);
177
<T> T getForObject(URI url, Class<T> responseType);
178
179
<T> ResponseEntity<T> getForEntity(String url, Class<T> responseType, Object... uriVariables);
180
<T> ResponseEntity<T> getForEntity(String url, Class<T> responseType, Map<String, ?> uriVariables);
181
<T> ResponseEntity<T> getForEntity(URI url, Class<T> responseType);
182
183
// HEAD operations
184
HttpHeaders headForHeaders(String url, Object... uriVariables);
185
HttpHeaders headForHeaders(String url, Map<String, ?> uriVariables);
186
HttpHeaders headForHeaders(URI url);
187
188
// POST operations
189
<T> T postForObject(String url, Object request, Class<T> responseType, Object... uriVariables);
190
<T> T postForObject(String url, Object request, Class<T> responseType, Map<String, ?> uriVariables);
191
<T> T postForObject(URI url, Object request, Class<T> responseType);
192
193
<T> ResponseEntity<T> postForEntity(String url, Object request, Class<T> responseType, Object... uriVariables);
194
<T> ResponseEntity<T> postForEntity(String url, Object request, Class<T> responseType, Map<String, ?> uriVariables);
195
<T> ResponseEntity<T> postForEntity(URI url, Object request, Class<T> responseType);
196
197
URI postForLocation(String url, Object request, Object... uriVariables);
198
URI postForLocation(String url, Object request, Map<String, ?> uriVariables);
199
URI postForLocation(URI url, Object request);
200
201
// PUT operations
202
void put(String url, Object request, Object... uriVariables);
203
void put(String url, Object request, Map<String, ?> uriVariables);
204
void put(URI url, Object request);
205
206
// PATCH operations
207
<T> T patchForObject(String url, Object request, Class<T> responseType, Object... uriVariables);
208
<T> T patchForObject(String url, Object request, Class<T> responseType, Map<String, ?> uriVariables);
209
<T> T patchForObject(URI url, Object request, Class<T> responseType);
210
211
// DELETE operations
212
void delete(String url, Object... uriVariables);
213
void delete(String url, Map<String, ?> uriVariables);
214
void delete(URI url);
215
216
// OPTIONS operations
217
Set<HttpMethod> optionsForAllow(String url, Object... uriVariables);
218
Set<HttpMethod> optionsForAllow(String url, Map<String, ?> uriVariables);
219
Set<HttpMethod> optionsForAllow(URI url);
220
221
// Generic exchange operations
222
<T> ResponseEntity<T> exchange(String url, HttpMethod method, HttpEntity<?> requestEntity,
223
Class<T> responseType, Object... uriVariables);
224
<T> ResponseEntity<T> exchange(String url, HttpMethod method, HttpEntity<?> requestEntity,
225
Class<T> responseType, Map<String, ?> uriVariables);
226
<T> ResponseEntity<T> exchange(URI url, HttpMethod method, HttpEntity<?> requestEntity,
227
Class<T> responseType);
228
<T> ResponseEntity<T> exchange(RequestEntity<?> requestEntity, Class<T> responseType);
229
<T> ResponseEntity<T> exchange(RequestEntity<?> requestEntity, ParameterizedTypeReference<T> responseType);
230
231
// Async execute methods
232
<T> ListenableFuture<ResponseEntity<T>> execute(String url, HttpMethod method,
233
RequestCallback requestCallback,
234
ResponseExtractor<ResponseEntity<T>> responseExtractor,
235
Object... uriVariables);
236
}
237
238
/**
239
* RestOperations interface defining the template contract
240
*/
241
interface RestOperations {
242
// All the same methods as RestTemplate class
243
// This interface is implemented by RestTemplate
244
}
245
```
246
247
**Usage Examples:**
248
249
```java
250
RestTemplate restTemplate = new RestTemplate();
251
252
// Simple GET operations
253
String result = restTemplate.getForObject("https://api.example.com/users/{id}", String.class, 123);
254
ResponseEntity<User> userResponse = restTemplate.getForEntity("https://api.example.com/users/{id}", User.class, 123);
255
256
// POST operations
257
User newUser = new User("John", "john@example.com");
258
User createdUser = restTemplate.postForObject("https://api.example.com/users", newUser, User.class);
259
URI location = restTemplate.postForLocation("https://api.example.com/users", newUser);
260
261
// Exchange for full control
262
HttpHeaders headers = new HttpHeaders();
263
headers.setBearerAuth("token");
264
HttpEntity<User> requestEntity = new HttpEntity<>(newUser, headers);
265
266
ResponseEntity<User> response = restTemplate.exchange(
267
"https://api.example.com/users",
268
HttpMethod.POST,
269
requestEntity,
270
User.class
271
);
272
273
// Using RequestEntity
274
RequestEntity<User> request = RequestEntity.post("https://api.example.com/users")
275
.contentType(MediaType.APPLICATION_JSON)
276
.body(newUser);
277
278
ResponseEntity<User> response = restTemplate.exchange(request, User.class);
279
```
280
281
### HTTP Client Infrastructure
282
283
Low-level HTTP client infrastructure for creating and configuring HTTP requests and responses.
284
285
```java { .api }
286
/**
287
* Factory for creating ClientHttpRequest objects
288
*/
289
interface ClientHttpRequestFactory {
290
/** Create a new request for the given URI and HTTP method */
291
ClientHttpRequest createRequest(URI uri, HttpMethod httpMethod) throws IOException;
292
}
293
294
/**
295
* Client-side HTTP request
296
*/
297
interface ClientHttpRequest extends HttpRequest, HttpOutputMessage {
298
/** Execute this request and return the response */
299
ClientHttpResponse execute() throws IOException;
300
}
301
302
/**
303
* Client-side HTTP response
304
*/
305
interface ClientHttpResponse extends HttpInputMessage, Closeable {
306
/** Get the HTTP status code */
307
HttpStatusCode getStatusCode() throws IOException;
308
/** Get the status text */
309
String getStatusText() throws IOException;
310
/** Close this response */
311
void close();
312
}
313
314
/**
315
* Execution context for client HTTP requests
316
*/
317
interface ClientHttpRequestExecution {
318
/** Execute the request with the given body */
319
ClientHttpResponse execute(HttpRequest request, byte[] body) throws IOException;
320
}
321
322
/**
323
* Interceptor for client-side HTTP requests
324
*/
325
interface ClientHttpRequestInterceptor {
326
/** Intercept the request before execution */
327
ClientHttpResponse intercept(HttpRequest request, byte[] body,
328
ClientHttpRequestExecution execution) throws IOException;
329
}
330
331
/**
332
* Initializer for client HTTP requests
333
*/
334
interface ClientHttpRequestInitializer {
335
/** Initialize the request before execution */
336
void initialize(ClientHttpRequest request);
337
}
338
```
339
340
### HTTP Client Factory Implementations
341
342
Concrete implementations of ClientHttpRequestFactory for different HTTP client libraries.
343
344
```java { .api }
345
/**
346
* Simple factory based on standard JDK HTTP facilities
347
*/
348
class SimpleClientHttpRequestFactory implements ClientHttpRequestFactory {
349
SimpleClientHttpRequestFactory();
350
351
/** Set the proxy to use for connections */
352
void setProxy(Proxy proxy);
353
/** Set whether to buffer request body in memory */
354
void setBufferRequestBody(boolean bufferRequestBody);
355
/** Set the chunk size for streaming */
356
void setChunkSize(int chunkSize);
357
/** Set the connection timeout */
358
void setConnectTimeout(int connectTimeout);
359
/** Set the read timeout */
360
void setReadTimeout(int readTimeout);
361
362
ClientHttpRequest createRequest(URI uri, HttpMethod httpMethod) throws IOException;
363
}
364
365
/**
366
* Factory based on Apache HttpComponents HttpClient
367
*/
368
class HttpComponentsClientHttpRequestFactory implements ClientHttpRequestFactory, DisposableBean {
369
HttpComponentsClientHttpRequestFactory();
370
HttpComponentsClientHttpRequestFactory(HttpClient httpClient);
371
372
/** Set the HttpClient instance to use */
373
void setHttpClient(HttpClient httpClient);
374
/** Set connection timeout */
375
void setConnectTimeout(int connectTimeout);
376
/** Set connection request timeout */
377
void setConnectionRequestTimeout(int connectionRequestTimeout);
378
/** Set socket read timeout */
379
void setReadTimeout(int readTimeout);
380
/** Set buffer request body */
381
void setBufferRequestBody(boolean bufferRequestBody);
382
383
ClientHttpRequest createRequest(URI uri, HttpMethod httpMethod) throws IOException;
384
void destroy() throws Exception;
385
}
386
387
/**
388
* Factory based on OkHttp 3.x
389
*/
390
class OkHttp3ClientHttpRequestFactory implements ClientHttpRequestFactory, DisposableBean {
391
OkHttp3ClientHttpRequestFactory();
392
OkHttp3ClientHttpRequestFactory(OkHttpClient client);
393
394
/** Set the OkHttpClient instance to use */
395
void setClient(OkHttpClient client);
396
/** Set connection timeout */
397
void setConnectTimeout(int connectTimeout);
398
/** Set read timeout */
399
void setReadTimeout(int readTimeout);
400
/** Set write timeout */
401
void setWriteTimeout(int writeTimeout);
402
403
ClientHttpRequest createRequest(URI uri, HttpMethod httpMethod) throws IOException;
404
void destroy() throws Exception;
405
}
406
407
/**
408
* Factory based on JDK 11+ HTTP Client
409
*/
410
class JdkClientHttpRequestFactory implements ClientHttpRequestFactory {
411
JdkClientHttpRequestFactory();
412
JdkClientHttpRequestFactory(HttpClient httpClient);
413
JdkClientHttpRequestFactory(HttpClient httpClient, Executor executor);
414
415
/** Set the HttpClient instance to use */
416
void setHttpClient(HttpClient httpClient);
417
/** Set executor for async operations */
418
void setExecutor(Executor executor);
419
/** Set read timeout */
420
void setReadTimeout(Duration readTimeout);
421
422
ClientHttpRequest createRequest(URI uri, HttpMethod httpMethod) throws IOException;
423
}
424
425
/**
426
* Factory wrapper that buffers all outgoing and incoming streams
427
*/
428
class BufferingClientHttpRequestFactory extends AbstractClientHttpRequestFactoryWrapper {
429
BufferingClientHttpRequestFactory(ClientHttpRequestFactory requestFactory);
430
431
ClientHttpRequest createRequest(URI uri, HttpMethod httpMethod, ClientHttpRequestFactory requestFactory) throws IOException;
432
}
433
434
/**
435
* Factory wrapper with support for request interceptors
436
*/
437
class InterceptingClientHttpRequestFactory extends AbstractClientHttpRequestFactoryWrapper {
438
InterceptingClientHttpRequestFactory(ClientHttpRequestFactory requestFactory,
439
List<ClientHttpRequestInterceptor> interceptors);
440
441
ClientHttpRequest createRequest(URI uri, HttpMethod httpMethod, ClientHttpRequestFactory requestFactory) throws IOException;
442
}
443
```
444
445
**Usage Examples:**
446
447
```java
448
// Configure different HTTP client factories
449
SimpleClientHttpRequestFactory simpleFactory = new SimpleClientHttpRequestFactory();
450
simpleFactory.setConnectTimeout(5000);
451
simpleFactory.setReadTimeout(10000);
452
453
HttpComponentsClientHttpRequestFactory httpComponentsFactory = new HttpComponentsClientHttpRequestFactory();
454
httpComponentsFactory.setConnectTimeout(5000);
455
httpComponentsFactory.setReadTimeout(10000);
456
457
// Use with RestTemplate
458
RestTemplate restTemplate = new RestTemplate(simpleFactory);
459
460
// Configure with interceptors
461
List<ClientHttpRequestInterceptor> interceptors = Arrays.asList(
462
new BasicAuthenticationInterceptor("username", "password"),
463
new LoggingClientHttpRequestInterceptor()
464
);
465
466
restTemplate.setInterceptors(interceptors);
467
```
468
469
### Request Interceptors and Authentication
470
471
Built-in interceptors for common cross-cutting concerns like authentication and logging.
472
473
```java { .api }
474
/**
475
* Interceptor to apply Basic Authentication headers
476
*/
477
class BasicAuthenticationInterceptor implements ClientHttpRequestInterceptor {
478
BasicAuthenticationInterceptor(String username, String password);
479
BasicAuthenticationInterceptor(String username, String password, Charset charset);
480
481
ClientHttpResponse intercept(HttpRequest request, byte[] body,
482
ClientHttpRequestExecution execution) throws IOException;
483
}
484
485
/**
486
* Interceptor to apply Bearer token authentication
487
*/
488
class BearerTokenAuthenticationInterceptor implements ClientHttpRequestInterceptor {
489
BearerTokenAuthenticationInterceptor(String token);
490
491
ClientHttpResponse intercept(HttpRequest request, byte[] body,
492
ClientHttpRequestExecution execution) throws IOException;
493
}
494
```
495
496
**Usage Examples:**
497
498
```java
499
// Basic authentication interceptor
500
BasicAuthenticationInterceptor basicAuth = new BasicAuthenticationInterceptor("user", "pass");
501
502
// Bearer token interceptor
503
BearerTokenAuthenticationInterceptor bearerAuth = new BearerTokenAuthenticationInterceptor("jwt-token");
504
505
// Custom interceptor
506
ClientHttpRequestInterceptor customInterceptor = (request, body, execution) -> {
507
request.getHeaders().add("X-Custom-Header", "custom-value");
508
ClientHttpResponse response = execution.execute(request, body);
509
// Log response or modify headers
510
return response;
511
};
512
513
// Apply interceptors to RestTemplate
514
RestTemplate restTemplate = new RestTemplate();
515
restTemplate.setInterceptors(Arrays.asList(basicAuth, customInterceptor));
516
517
// Apply interceptors to RestClient
518
RestClient restClient = RestClient.builder()
519
.requestInterceptor(bearerAuth)
520
.requestInterceptor(customInterceptor)
521
.build();
522
```
523
524
### Error Handling
525
526
Configure custom error handling for HTTP client responses.
527
528
```java { .api }
529
/**
530
* Strategy interface used by RestTemplate to determine whether a response has an error
531
*/
532
interface ResponseErrorHandler {
533
/** Determine if the given response has an error */
534
boolean hasError(ClientHttpResponse response) throws IOException;
535
/** Handle the error in the given response */
536
void handleError(ClientHttpResponse response) throws IOException;
537
/** Handle the error for the given request and response */
538
default void handleError(URI url, HttpMethod method, ClientHttpResponse response) throws IOException {
539
handleError(response);
540
}
541
}
542
543
/**
544
* Default implementation of ResponseErrorHandler
545
*/
546
class DefaultResponseErrorHandler implements ResponseErrorHandler {
547
DefaultResponseErrorHandler();
548
549
/** Check if response has client (4xx) or server (5xx) error */
550
boolean hasError(ClientHttpResponse response) throws IOException;
551
/** Throw appropriate exception based on status code */
552
void handleError(ClientHttpResponse response) throws IOException;
553
void handleError(URI url, HttpMethod method, ClientHttpResponse response) throws IOException;
554
555
// Template methods for customization
556
protected boolean hasError(HttpStatusCode statusCode);
557
protected void handleError(ClientHttpResponse response, HttpStatusCode statusCode) throws IOException;
558
}
559
```
560
561
**Usage Examples:**
562
563
```java
564
// Custom error handler
565
ResponseErrorHandler customErrorHandler = new ResponseErrorHandler() {
566
@Override
567
public boolean hasError(ClientHttpResponse response) throws IOException {
568
return response.getStatusCode().is4xxClientError() ||
569
response.getStatusCode().is5xxServerError();
570
}
571
572
@Override
573
public void handleError(ClientHttpResponse response) throws IOException {
574
HttpStatusCode statusCode = response.getStatusCode();
575
if (statusCode.is4xxClientError()) {
576
throw new ClientErrorException("Client error: " + statusCode);
577
}
578
if (statusCode.is5xxServerError()) {
579
throw new ServerErrorException("Server error: " + statusCode);
580
}
581
}
582
};
583
584
// Apply to RestTemplate
585
RestTemplate restTemplate = new RestTemplate();
586
restTemplate.setErrorHandler(customErrorHandler);
587
588
// Apply to RestClient
589
RestClient restClient = RestClient.builder()
590
.defaultStatusHandler(HttpStatus::is4xxClientError,
591
(request, response) -> {
592
throw new ClientErrorException(response.getStatusCode());
593
})
594
.build();
595
```