0
# Feign JAX-RS
1
2
Feign JAX-RS is a module that enables the use of standard JAX-RS annotations (like @GET, @POST, @Path, @PathParam) with Netflix's Feign HTTP client library instead of Feign's native annotations. This allows developers familiar with JAX-RS to leverage Feign's declarative HTTP client capabilities while maintaining consistency with existing JAX-RS-based code.
3
4
## Package Information
5
6
- **Package Name**: feign-jaxrs
7
- **Package Type**: Maven (Java)
8
- **Group ID**: com.netflix.feign
9
- **Artifact ID**: feign-jaxrs
10
- **Language**: Java
11
- **Installation**: Add to Maven dependencies:
12
13
```xml
14
<dependency>
15
<groupId>com.netflix.feign</groupId>
16
<artifactId>feign-jaxrs</artifactId>
17
<version>7.6.0</version>
18
</dependency>
19
```
20
21
Or Gradle:
22
```gradle
23
compile 'com.netflix.feign:feign-jaxrs:7.6.0'
24
```
25
26
## Core Imports
27
28
```java
29
import feign.jaxrs.JAXRSModule;
30
import feign.Contract;
31
```
32
33
## Basic Usage
34
35
```java
36
import feign.Feign;
37
import feign.jaxrs.JAXRSModule;
38
import javax.ws.rs.*;
39
import java.util.List;
40
41
// Define your API interface using JAX-RS annotations
42
@Path("/repos")
43
interface GitHubAPI {
44
@GET
45
@Path("/{owner}/{repo}/contributors")
46
List<Contributor> getContributors(@PathParam("owner") String owner,
47
@PathParam("repo") String repo);
48
}
49
50
// Configure Feign with JAX-RS module
51
@Module(includes = JAXRSModule.class)
52
class MyModule {
53
// Additional configuration
54
}
55
56
// Create client instance
57
GitHubAPI github = Feign.create(GitHubAPI.class, "https://api.github.com", new MyModule());
58
59
// Use the client
60
List<Contributor> contributors = github.getContributors("netflix", "feign");
61
```
62
63
## Architecture
64
65
The feign-jaxrs module consists of two main components:
66
67
1. **JAXRSModule**: A Dagger module that provides the JAX-RS contract implementation
68
2. **JAXRSContract**: The core implementation that processes JAX-RS annotations into Feign request templates
69
70
The module acts as a bridge between JAX-RS annotation semantics and Feign's internal request building mechanism, translating standard JAX-RS annotations into Feign's MethodMetadata format.
71
72
## Capabilities
73
74
### Dagger Module Configuration
75
76
The JAXRSModule class provides dependency injection configuration for integrating JAX-RS annotation processing with Feign.
77
78
```java { .api }
79
@dagger.Module(library = true, overrides = true)
80
public final class JAXRSModule {
81
@Provides
82
Contract provideContract();
83
}
84
```
85
86
**Module constants:**
87
88
```java { .api }
89
static final String ACCEPT = "Accept";
90
static final String CONTENT_TYPE = "Content-Type";
91
```
92
93
### JAX-RS Contract Implementation
94
95
The JAXRSContract processes JAX-RS annotations and converts them into Feign request metadata.
96
97
```java { .api }
98
public static final class JAXRSContract extends Contract.BaseContract {
99
@Override
100
public MethodMetadata parseAndValidatateMetadata(Method method);
101
102
@Override
103
protected void processAnnotationOnMethod(MethodMetadata data,
104
Annotation methodAnnotation,
105
Method method);
106
107
@Override
108
protected boolean processAnnotationsOnParameter(MethodMetadata data,
109
Annotation[] annotations,
110
int paramIndex);
111
}
112
```
113
114
**Core contract methods:**
115
116
- **parseAndValidatateMetadata()**: Parses class-level @Path annotations and builds method metadata
117
- **processAnnotationOnMethod()**: Handles method-level annotations (@GET, @POST, @Path, @Produces, @Consumes)
118
- **processAnnotationsOnParameter()**: Processes parameter annotations (@PathParam, @QueryParam, @HeaderParam, @FormParam)
119
120
### Supported JAX-RS Annotations
121
122
#### Type-Level Annotations
123
124
**@Path**: Defines the base path for all methods in the interface.
125
126
```java
127
@Path("/api/v1/users")
128
interface UserAPI {
129
// All methods inherit /api/v1/users as base path
130
}
131
```
132
133
- Appends value to Target.url()
134
- Can contain path parameter tokens
135
- Must not be empty or null
136
- Leading slash is added automatically if missing
137
138
#### Method-Level Annotations
139
140
**HTTP Method Annotations**: Define the HTTP method for requests.
141
142
```java { .api }
143
@GET // HTTP GET method
144
@POST // HTTP POST method
145
@PUT // HTTP PUT method
146
@DELETE // HTTP DELETE method
147
```
148
149
**@Path**: Defines method-specific path segments.
150
151
```java
152
@GET
153
@Path("/profile") // Appends to base path
154
User getProfile();
155
```
156
157
- Appends to class-level @Path
158
- Can contain path parameter tokens
159
- Leading slash added automatically when needed
160
- Must not be empty or null
161
162
**@Produces**: Sets the Accept header for content negotiation.
163
164
```java
165
@GET
166
@Produces("application/json") // Sets Accept: application/json
167
User getUser();
168
```
169
170
- Uses first value in array as Accept header
171
- Must not be empty or null
172
- Only first value is used if multiple provided
173
174
**@Consumes**: Sets the Content-Type header for request body.
175
176
```java
177
@POST
178
@Consumes("application/json") // Sets Content-Type: application/json
179
void createUser(User user);
180
```
181
182
- Uses first value in array as Content-Type header
183
- Must not be empty or null
184
- Only first value is used if multiple provided
185
186
#### Parameter-Level Annotations
187
188
**@PathParam**: Links method parameters to path template variables.
189
190
```java
191
@GET
192
@Path("/users/{userId}")
193
User getUser(@PathParam("userId") String userId);
194
```
195
196
- Parameter name must not be empty or null
197
- Links parameter value to path template variable
198
- Used for URL path parameter substitution
199
200
**@QueryParam**: Links parameters to HTTP query parameters.
201
202
```java
203
@GET
204
User searchUsers(@QueryParam("name") String name,
205
@QueryParam("limit") Integer limit);
206
```
207
208
- Parameter name must not be empty or null
209
- Null parameter values are omitted from query string
210
- Multiple parameters create multiple query parameters
211
212
**@HeaderParam**: Links parameters to custom HTTP headers.
213
214
```java
215
@GET
216
User getUser(@HeaderParam("Authorization") String authToken,
217
@HeaderParam("X-Request-ID") String requestId);
218
```
219
220
- Parameter name must not be empty or null
221
- Used for custom HTTP headers
222
- Header name used exactly as specified
223
224
**@FormParam**: Links parameters to form data fields.
225
226
```java
227
@POST
228
@Consumes("application/x-www-form-urlencoded")
229
void login(@FormParam("username") String username,
230
@FormParam("password") String password);
231
```
232
233
- Parameter name must not be empty or null
234
- Used with form-encoded request bodies
235
- Processed by Feign's form encoder
236
237
### Error Handling
238
239
The JAX-RS contract throws IllegalStateException in the following cases:
240
241
- **Empty or null annotation values**: When @Path, @PathParam, @QueryParam, @HeaderParam, @FormParam, @Produces, or @Consumes have empty or null values
242
- **Multiple HTTP methods**: When a single method has multiple HTTP method annotations
243
- **Invalid annotation combinations**: When annotations are used incorrectly
244
245
```java
246
// These will throw IllegalStateException:
247
@Path("") // Empty path
248
@PathParam("") String param // Empty path param name
249
@QueryParam(null) String query // Null query param name
250
@Produces({}) Response get() // Empty produces array
251
@GET @POST void invalidMethod() // Multiple HTTP methods
252
```
253
254
## Limitations
255
256
- **JAX-RS 1.1 compatibility**: Targets JAX-RS 1.1 specification, not 2.0+
257
- **Interface-only support**: Only supports Java interfaces, not abstract or concrete classes
258
- **Best-effort implementation**: Not 100% JAX-RS compliant, focuses on common client-side patterns
259
- **Single value support**: @Produces and @Consumes only use the first value in arrays
260
- **No server-side features**: Designed for client-side HTTP mapping only
261
262
## Dependencies
263
264
- **javax.ws.rs:jsr311-api:1.1.1**: JAX-RS 1.1 API specification
265
- **feign-core**: Core Feign HTTP client library
266
- **dagger**: Dependency injection framework
267
268
## Types
269
270
```java { .api }
271
// Main module class
272
@dagger.Module(library = true, overrides = true)
273
public final class JAXRSModule {
274
static final String ACCEPT = "Accept";
275
static final String CONTENT_TYPE = "Content-Type";
276
277
@Provides
278
Contract provideContract();
279
}
280
281
// Contract implementation
282
public static final class JAXRSContract extends Contract.BaseContract {
283
@Override
284
public MethodMetadata parseAndValidatateMetadata(Method method);
285
286
@Override
287
protected void processAnnotationOnMethod(MethodMetadata data,
288
Annotation methodAnnotation,
289
Method method);
290
291
@Override
292
protected boolean processAnnotationsOnParameter(MethodMetadata data,
293
Annotation[] annotations,
294
int paramIndex);
295
}
296
```
297
298
## Usage Examples
299
300
### Complete API Interface Example
301
302
```java
303
import javax.ws.rs.*;
304
import java.util.List;
305
306
@Path("/api/v1")
307
@Produces("application/json")
308
public interface UserService {
309
310
@GET
311
@Path("/users")
312
List<User> getAllUsers(@QueryParam("limit") Integer limit,
313
@QueryParam("offset") Integer offset);
314
315
@GET
316
@Path("/users/{id}")
317
User getUserById(@PathParam("id") String userId);
318
319
@POST
320
@Path("/users")
321
@Consumes("application/json")
322
User createUser(User user);
323
324
@PUT
325
@Path("/users/{id}")
326
@Consumes("application/json")
327
User updateUser(@PathParam("id") String userId, User user);
328
329
@DELETE
330
@Path("/users/{id}")
331
void deleteUser(@PathParam("id") String userId);
332
333
@POST
334
@Path("/auth/login")
335
@Consumes("application/x-www-form-urlencoded")
336
AuthToken login(@FormParam("username") String username,
337
@FormParam("password") String password);
338
339
@GET
340
@Path("/users/me")
341
User getCurrentUser(@HeaderParam("Authorization") String bearerToken);
342
}
343
```
344
345
### Dagger Module Configuration
346
347
```java
348
import dagger.Module;
349
import dagger.Provides;
350
import feign.Logger;
351
import feign.jaxrs.JAXRSModule;
352
353
@Module(includes = {JAXRSModule.class, GsonModule.class})
354
public class ApiModule {
355
356
@Provides
357
Logger.Level provideLogLevel() {
358
return Logger.Level.BASIC;
359
}
360
361
@Provides
362
Logger provideLogger() {
363
return new Logger.ErrorLogger();
364
}
365
}
366
```
367
368
### Client Creation and Usage
369
370
```java
371
import feign.Feign;
372
373
public class ApiClient {
374
private final UserService userService;
375
376
public ApiClient(String baseUrl) {
377
this.userService = Feign.create(UserService.class, baseUrl, new ApiModule());
378
}
379
380
public void demonstrateUsage() {
381
// Get all users with pagination
382
List<User> users = userService.getAllUsers(10, 0);
383
384
// Get specific user
385
User user = userService.getUserById("123");
386
387
// Create new user
388
User newUser = new User("john", "john@example.com");
389
User created = userService.createUser(newUser);
390
391
// Update user
392
created.setEmail("newemail@example.com");
393
User updated = userService.updateUser(created.getId(), created);
394
395
// Authenticate
396
AuthToken token = userService.login("john", "password123");
397
398
// Get current user with auth header
399
User currentUser = userService.getCurrentUser("Bearer " + token.getAccessToken());
400
401
// Delete user
402
userService.deleteUser(created.getId());
403
}
404
}
405
```