0
# HTTP Client
1
2
Micronaut's HTTP client provides both programmatic and declarative APIs for making HTTP requests with reactive support, service discovery integration, and comprehensive configuration options.
3
4
## Capabilities
5
6
### Declarative Client
7
8
Create HTTP clients using interfaces with declarative annotations.
9
10
```java { .api }
11
/**
12
* Declarative HTTP client interface
13
*/
14
@Client("/api/users")
15
public interface UserClient {
16
17
@Get("/{id}")
18
Single<User> getUser(Long id);
19
20
@Get
21
Single<List<User>> getAllUsers(@QueryValue Optional<String> filter);
22
23
@Post
24
Single<User> createUser(@Body @Valid User user);
25
26
@Put("/{id}")
27
Single<User> updateUser(Long id, @Body @Valid User user);
28
29
@Delete("/{id}")
30
Completable deleteUser(Long id);
31
}
32
33
/**
34
* Client with custom headers and error handling
35
*/
36
@Client(value = "${external.api.url}",
37
configuration = ApiClientConfiguration.class)
38
@Header(name = "User-Agent", value = "MyApp/1.0")
39
public interface ExternalApiClient {
40
41
@Get("/data/{id}")
42
@Header(name = "X-API-Key", value = "${api.key}")
43
Single<ApiResponse> getData(String id);
44
45
@Post("/webhooks")
46
@Header(name = "Content-Type", value = "application/json")
47
Completable sendWebhook(@Body WebhookPayload payload);
48
49
@Get("/health")
50
@Retryable(attempts = "3", delay = "1s")
51
Single<HealthStatus> checkHealth();
52
}
53
```
54
55
### Programmatic Client
56
57
Create and use HTTP clients programmatically for dynamic requests.
58
59
```java { .api }
60
/**
61
* Programmatic HTTP client usage
62
*/
63
@Singleton
64
public class ApiService {
65
private final HttpClient httpClient;
66
67
public ApiService(@Client("${api.base-url}") HttpClient httpClient) {
68
this.httpClient = httpClient;
69
}
70
71
public Single<User> getUser(Long userId) {
72
HttpRequest<Object> request = HttpRequest.GET("/users/" + userId)
73
.header("Authorization", "Bearer " + getToken());
74
75
return httpClient.retrieve(request, User.class);
76
}
77
78
public Single<User> createUser(User user) {
79
HttpRequest<User> request = HttpRequest.POST("/users", user)
80
.contentType(MediaType.APPLICATION_JSON);
81
82
return httpClient.retrieve(request, User.class);
83
}
84
85
public Single<HttpResponse<User>> createUserWithResponse(User user) {
86
HttpRequest<User> request = HttpRequest.POST("/users", user);
87
88
return httpClient.exchange(request, User.class);
89
}
90
}
91
```
92
93
### Reactive Streaming
94
95
Stream large responses or handle real-time data with reactive types.
96
97
```java { .api }
98
/**
99
* Streaming HTTP client
100
*/
101
@Client("/api/stream")
102
public interface StreamingClient {
103
104
@Get(value = "/events", processes = MediaType.TEXT_EVENT_STREAM)
105
Flowable<ServerSentEvent<Event>> streamEvents();
106
107
@Get("/large-data")
108
Flowable<ByteBuffer<?>> streamLargeData();
109
110
@Post("/upload")
111
Single<UploadResult> uploadStream(@Body Flowable<ByteBuffer<?>> data);
112
}
113
114
/**
115
* JSON streaming
116
*/
117
@Singleton
118
public class StreamingService {
119
private final StreamingHttpClient streamingClient;
120
121
public StreamingService(@Client("${api.url}") StreamingHttpClient streamingClient) {
122
this.streamingClient = streamingClient;
123
}
124
125
public Flowable<User> streamUsers() {
126
HttpRequest<Object> request = HttpRequest.GET("/users/stream");
127
return streamingClient.jsonStream(request, User.class);
128
}
129
130
public Flowable<ByteBuffer<?>> downloadFile(String fileId) {
131
HttpRequest<Object> request = HttpRequest.GET("/files/" + fileId);
132
return streamingClient.dataStream(request);
133
}
134
}
135
```
136
137
### Client Configuration
138
139
Configure HTTP clients with timeouts, connection settings, and custom behavior.
140
141
```java { .api }
142
/**
143
* Client configuration class
144
*/
145
@ConfigurationProperties("http-client.api")
146
public class ApiClientConfiguration extends HttpClientConfiguration {
147
148
@Override
149
public Duration getReadTimeout() {
150
return Duration.ofSeconds(30);
151
}
152
153
@Override
154
public Duration getConnectTimeout() {
155
return Duration.ofSeconds(10);
156
}
157
158
@Override
159
public Integer getMaxContentLength() {
160
return 1024 * 1024 * 10; // 10MB
161
}
162
}
163
164
/**
165
* Custom client configuration factory
166
*/
167
@Factory
168
public class HttpClientFactory {
169
170
@Bean
171
@Named("secure-client")
172
public HttpClient createSecureClient() {
173
return HttpClient.create("https://secure-api.example.com")
174
.configuration(config -> {
175
config.readTimeout(Duration.ofSeconds(60));
176
config.followRedirects(true);
177
config.maxContentLength(1024 * 1024 * 50); // 50MB
178
});
179
}
180
181
@Bean
182
@Named("fast-client")
183
public HttpClient createFastClient() {
184
return HttpClient.create("https://fast-api.example.com")
185
.configuration(config -> {
186
config.readTimeout(Duration.ofSeconds(5));
187
config.connectTimeout(Duration.ofSeconds(2));
188
config.connectionPoolSize(20);
189
});
190
}
191
}
192
```
193
194
### Client Filters
195
196
Add request/response filtering for authentication, logging, and other cross-cutting concerns.
197
198
```java { .api }
199
/**
200
* Client filter for authentication
201
*/
202
@Filter("/api/**")
203
public class AuthenticationClientFilter implements HttpClientFilter {
204
205
@Override
206
public Publisher<? extends HttpResponse<?>> doFilter(MutableHttpRequest<?> request,
207
ClientFilterChain chain) {
208
String token = getAuthToken();
209
request.header("Authorization", "Bearer " + token);
210
return chain.proceed(request);
211
}
212
213
private String getAuthToken() {
214
// Get authentication token
215
return "token";
216
}
217
}
218
219
/**
220
* Logging client filter
221
*/
222
@Filter("/**")
223
public class LoggingClientFilter implements HttpClientFilter {
224
225
@Override
226
public Publisher<? extends HttpResponse<?>> doFilter(MutableHttpRequest<?> request,
227
ClientFilterChain chain) {
228
log.info("Sending request: {} {}", request.getMethod(), request.getUri());
229
230
return chain.proceed(request)
231
.doOnNext(response ->
232
log.info("Received response: {} for {} {}",
233
response.getStatus(), request.getMethod(), request.getUri()))
234
.doOnError(error ->
235
log.error("Request failed: {} {} - {}",
236
request.getMethod(), request.getUri(), error.getMessage()));
237
}
238
}
239
```
240
241
### Error Handling
242
243
Handle HTTP errors and network failures with custom error handling logic.
244
245
```java { .api }
246
/**
247
* Client with error handling
248
*/
249
@Client("/api")
250
public interface ResilientClient {
251
252
@Get("/data")
253
@Retryable(attempts = "3", delay = "2s", multiplier = "1.5")
254
Single<Data> getData();
255
256
@Get("/unreliable")
257
@CircuitBreaker(attempts = "5", openStatusTimeout = "1m", resetTimeout = "30s")
258
Single<String> getUnreliableData();
259
260
@Fallback
261
static Single<String> getUnreliableDataFallback() {
262
return Single.just("fallback-data");
263
}
264
}
265
266
/**
267
* Custom error handling
268
*/
269
@Singleton
270
public class ApiClientService {
271
private final HttpClient httpClient;
272
273
public ApiClientService(@Client("${api.url}") HttpClient httpClient) {
274
this.httpClient = httpClient;
275
}
276
277
public Single<User> getUserWithErrorHandling(Long id) {
278
HttpRequest<Object> request = HttpRequest.GET("/users/" + id);
279
280
return httpClient.exchange(request, User.class)
281
.flatMap(response -> {
282
if (response.getStatus().getCode() == 404) {
283
return Single.error(new UserNotFoundException(id));
284
} else if (response.getStatus().getCode() >= 400) {
285
return Single.error(new ApiException(response.getStatus()));
286
}
287
return Single.just(response.body());
288
})
289
.onErrorResumeNext(throwable -> {
290
if (throwable instanceof ConnectException) {
291
return Single.error(new ServiceUnavailableException());
292
}
293
return Single.error(throwable);
294
});
295
}
296
}
297
```
298
299
### Service Discovery Integration
300
301
Integrate with service discovery systems for dynamic service resolution.
302
303
```java { .api }
304
/**
305
* Client with service discovery
306
*/
307
@Client(id = "user-service", path = "/api/users")
308
public interface UserServiceClient {
309
310
@Get("/{id}")
311
Single<User> getUser(Long id);
312
313
@Get
314
Single<List<User>> getAllUsers();
315
}
316
317
/**
318
* Load balancing configuration
319
*/
320
@ConfigurationProperties("http-client.user-service")
321
public class UserServiceClientConfiguration extends HttpClientConfiguration {
322
323
@Override
324
public Duration getReadTimeout() {
325
return Duration.ofSeconds(15);
326
}
327
328
// Load balancer configuration
329
public LoadBalancer getLoadBalancer() {
330
return LoadBalancer.ROUND_ROBIN;
331
}
332
}
333
```
334
335
### File Upload and Download
336
337
Handle file operations with progress tracking and streaming support.
338
339
```java { .api }
340
/**
341
* File operations client
342
*/
343
@Client("/api/files")
344
public interface FileClient {
345
346
@Post(value = "/upload", consumes = MediaType.MULTIPART_FORM_DATA)
347
Single<FileUploadResult> uploadFile(@Part CompletedFileUpload file,
348
@Part("metadata") FileMetadata metadata);
349
350
@Get("/{fileId}")
351
Single<byte[]> downloadFile(String fileId);
352
353
@Get(value = "/{fileId}/stream")
354
Flowable<ByteBuffer<?>> streamFile(String fileId);
355
356
@Delete("/{fileId}")
357
Completable deleteFile(String fileId);
358
}
359
360
/**
361
* Large file handling
362
*/
363
@Singleton
364
public class FileService {
365
private final StreamingHttpClient streamingClient;
366
367
public FileService(@Client("${file.service.url}") StreamingHttpClient streamingClient) {
368
this.streamingClient = streamingClient;
369
}
370
371
public Single<String> uploadLargeFile(File file) {
372
HttpRequest<Object> request = HttpRequest.POST("/upload", file)
373
.contentType(MediaType.APPLICATION_OCTET_STREAM)
374
.header("Content-Length", String.valueOf(file.length()));
375
376
return streamingClient.retrieve(request, String.class);
377
}
378
379
public void downloadLargeFile(String fileId, File destination) {
380
HttpRequest<Object> request = HttpRequest.GET("/files/" + fileId);
381
382
streamingClient.dataStream(request)
383
.subscribe(buffer -> {
384
// Write buffer to file
385
writeToFile(buffer, destination);
386
});
387
}
388
}
389
```
390
391
## Types
392
393
```java { .api }
394
// Core client interfaces
395
public interface HttpClient extends Closeable, LifeCycle<HttpClient> {
396
<I, O> Publisher<HttpResponse<O>> exchange(HttpRequest<I> request,
397
Argument<O> bodyType);
398
<I, O> Publisher<HttpResponse<O>> exchange(HttpRequest<I> request,
399
Class<O> bodyType);
400
<I, O> Publisher<O> retrieve(HttpRequest<I> request, Argument<O> bodyType);
401
<I, O> Publisher<O> retrieve(HttpRequest<I> request, Class<O> bodyType);
402
403
BlockingHttpClient toBlocking();
404
static HttpClient create(URL url);
405
}
406
407
public interface StreamingHttpClient extends HttpClient {
408
<I> Publisher<ByteBuffer<?>> dataStream(HttpRequest<I> request);
409
<I> Publisher<Map<String, Object>> jsonStream(HttpRequest<I> request);
410
<I, O> Publisher<O> jsonStream(HttpRequest<I> request, Argument<O> type);
411
}
412
413
public interface BlockingHttpClient extends Closeable, LifeCycle<BlockingHttpClient> {
414
<I, O> HttpResponse<O> exchange(HttpRequest<I> request, Class<O> bodyType);
415
<I, O> O retrieve(HttpRequest<I> request, Class<O> bodyType);
416
String retrieve(String uri);
417
}
418
419
// Client configuration
420
public class HttpClientConfiguration {
421
public Duration getReadTimeout();
422
public Duration getConnectTimeout();
423
public Duration getConnectionPoolIdleTimeout();
424
public Integer getMaxContentLength();
425
public boolean isFollowRedirects();
426
public ProxyConfiguration getProxyConfiguration();
427
public HttpVersion getHttpVersion();
428
}
429
430
// Client filtering
431
public interface HttpClientFilter extends ClientFilter {
432
Publisher<? extends HttpResponse<?>> doFilter(MutableHttpRequest<?> request,
433
ClientFilterChain chain);
434
}
435
436
public interface ClientFilterChain {
437
Publisher<? extends HttpResponse<?>> proceed(MutableHttpRequest<?> request);
438
}
439
440
// Request creation
441
public final class HttpRequest {
442
public static <T> MutableHttpRequest<T> GET(String uri);
443
public static <T> MutableHttpRequest<T> POST(String uri, T body);
444
public static <T> MutableHttpRequest<T> PUT(String uri, T body);
445
public static <T> MutableHttpRequest<T> DELETE(String uri);
446
public static <T> MutableHttpRequest<T> HEAD(String uri);
447
public static <T> MutableHttpRequest<T> OPTIONS(String uri);
448
public static <T> MutableHttpRequest<T> PATCH(String uri, T body);
449
public static <T> MutableHttpRequest<T> TRACE(String uri);
450
}
451
452
public interface MutableHttpRequest<B> extends HttpRequest<B>, MutableHttpMessage<B> {
453
MutableHttpRequest<B> cookie(Cookie cookie);
454
MutableHttpRequest<B> uri(URI uri);
455
MutableHttpRequest<B> header(CharSequence name, CharSequence value);
456
MutableHttpRequest<B> headers(Map<CharSequence, CharSequence> headers);
457
MutableHttpRequest<B> contentType(MediaType mediaType);
458
MutableHttpRequest<B> accept(MediaType... mediaTypes);
459
MutableHttpRequest<B> basicAuth(String username, String password);
460
MutableHttpRequest<B> bearerAuth(String token);
461
}
462
463
// Service discovery types
464
public enum LoadBalancer {
465
ROUND_ROBIN,
466
RANDOM,
467
STICKY
468
}
469
470
public interface ServiceDiscoveryClient {
471
Publisher<List<ServiceInstance>> getInstances(String serviceId);
472
String getDescription();
473
}
474
```