or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

tessl/maven-com-netflix-feign--feign-jaxrs

JAX-RS annotation integration module for Netflix's Feign HTTP client library

Workspace
tessl
Visibility
Public
Created
Last updated
Describes
mavenpkg:maven/com.netflix.feign/feign-jaxrs@7.6.x

To install, run

npx @tessl/cli install tessl/maven-com-netflix-feign--feign-jaxrs@7.6.0

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

```