Feign Client with Api Gateway

Deepu George Jacob
6 min readDec 7, 2023

--

Many members of our springboot microservice community are encountering issues when communicating with microservices using Feign client. A common problem they face is the 401 Unauthorized error. In this blog post, we will explore how to resolve this issue.

Why your Feign Client API is failed

The above diagram provides a clear insight into why your API is encountering issues. When a service is requested from either your computer or mobile device through the gateway, with the appropriate credentials, the API directs this request to the relevant microservice — in this case, the school-library microservice. All the necessary credentials for this transaction are stored within the school-library microservice.

The complication arises when the school-library microservice realizes it needs additional information from the student microservice. In response, the school-library microservice initiates an API call to the student microservice through the gateway. However, the problem surfaces at this point.

The core issue lies in our attempt to retrieve data from the student microservice through the gateway, which requires proper authorization. Unfortunately, our request lacks the necessary authorization information, leading to the failure of our application and the issuance of a 401 error code. This error stems from the absence of crucial authorization data in our communication with the gateway, disrupting the intended functionality of the API call.

How to resolve this issue

To address this issue, the recommended solution is to include authorization information in the API call initiated from the school-library microservice to the student microservice through the API gateway. By incorporating the necessary authorization details, we ensure that the communication between these microservices via the gateway is authenticated, subsequently preventing the occurrence of the 401 error. This corrective action establishes a secure and authorized channel for the exchange of information, resolving the underlying problem in the microservice communication flow.

In the depicted scenario, it is evident that all communication channels between microservices and the gateway are now authenticated. This implementation ensures the security and legitimacy of the interactions. As a result, instead of encountering a 401 error response, we observe successful communication, indicated by a 200 response. The inclusion of authentication in the microservices-to-gateway communication has effectively resolved the previous issue, establishing a robust and secure framework for data exchange.

How to programmatically implement a solution for this problem?

First, ensure that you have properly implemented the Feign client in your application. Visit the below blog post where I have outlined how to implement the Feign client in your project.

As per my knowledge two ways you can fix this issue.

Solution 1 : Passing http headers to Feign Client
Solution 2 : Use of Request Interceptor (Recommended)

Passing http headers to Feign Client

Step 1 : In your controller get the HttpServletRequest object like this.

@RestController
@RequestMapping(value = "/api/membership")
public class SchoolLibraryController {

@GetMapping(value = "/feign/{id}")
public ResponseEntity<ApiResponse<MembershipDto>> getStudentMembershipFeign(HttpServletRequest request, @PathVariable int id) {
return membershipService.getLibraryMembershipFeign(request, id);
}

}

Step 2: Create a method like below in your service layer to create header object.

private HttpHeaders getHeaders(final HttpServletRequest httpServletRequest) {
var iterator = httpServletRequest.getHeaderNames().asIterator();
final HttpHeaders headers = new HttpHeaders();
while (iterator.hasNext()) {
var key = iterator.next();
headers.add(key, httpServletRequest.getHeader(key));
}
return headers;
}

So the function inside your service layer look like this.

public ResponseEntity<ApiResponse<MembershipDto>> getLibraryMembership(HttpServletRequest request, int id) {
final HttpHeaders httpHeaders = getHeaders(request);
return null;
}

Step 3 : Now we have to call Feign client with this header.

See the sample code I have created for the Feign client.

@FeignClient(value = "api-gateway", path = "student-service/api/students/")
public interface StudentFeignClient {

@GetMapping("/{id}")
ApiResponse<Map<String,Object>> getStudentById(@RequestHeader HttpHeaders headers, @PathVariable int id);

}

Step 3: Now create a an another service layer to implement Circuit breaker, like below.

@Service
public class StudentService {

private static final Logger logger = LoggerFactory.getLogger(StudentService.class);

private final StudentFeignClient studentFeignClient;

@Autowired
public StudentService(final StudentFeignClient studentFeignClient) {
this.studentFeignClient = studentFeignClient;
}

@CircuitBreaker(name = "studentService", fallbackMethod = "getStudentByIdFallbackMethod")
public Object getStudentById(final HttpHeaders headers, final int id) {
logger.info("Student feign client to get student data");
return studentFeignClient.getStudentById(headers, id).getData().get("student");
}

public Object getStudentByIdFallbackMethod(final HttpHeaders headers, final int id, final Throwable th) {
logger.error("Failed to get student details", th);
return "Student details not found for student with id.. " + id + " No response from student server";
}


}

Step 4 (Final Step):

In the main service (the service injected into the controller), inject the service created in step 3 (use @Autowired). Recommending constructor injection.

@Autowired
private StudentService studentService;

Now, you have to call the studentService from your main service and return the response, as shown below:

public ResponseEntity<ApiResponse<MembershipDto>> getLibraryMembership(HttpServletRequest request, int id) {
final HttpHeaders httpHeaders = getHeaders(request);
final Object studentData = studentService.getStudentById(httpHeaders, id);
final ApiResponse response = ApiResponse.builder().data(studentData).build();
return ResponseEntity.ok(response);
}

Sample code with documentation is available at this link

Use of RequestInterceptor

In the context of Java development and microservice communication using Feign client through an API gateway with authorization, a RequestInterceptor is a component that allows you to intercept and modify HTTP requests made by the Feign client before they are sent to the target service. This can be useful for various purposes, such as adding custom headers, modifying request parameters, or performing authentication and authorization tasks.

When working with Feign clients in a microservices architecture, using a RequestInterceptor gives you the flexibility to customize and enhance the behavior of your HTTP requests based on specific requirements, ensuring seamless communication between microservices while adhering to security and authorization protocols.

How to configure RequestInterceptor?

Step 1: Create a subclass of RequestInterceptor and configure the method as shown below.

@Component
public class AuthFeignInterceptor implements RequestInterceptor {

@Override
public void apply(RequestTemplate template) {
final RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
if (requestAttributes != null) {
final HttpServletRequest httpServletRequest = ((ServletRequestAttributes) requestAttributes).getRequest();
template.header(HttpHeaders.AUTHORIZATION, httpServletRequest.getHeader(HttpHeaders.AUTHORIZATION));
}
}
}

Step 2: You are done. No other changes are required in your Feign client (No header information). Make sure you have successfully configured the FeignClient, as shown below:

@FeignClient(value = "api-gateway", path = "student-service/api/students/")
public interface StudentFeignClient {

@GetMapping("/{id}")
ApiResponse<Map<String,Object>> getStudentById(@PathVariable int id);

}

Refer to the information in step 3 from the previous solution and create a service with a Feign client. The only difference is that you do not need to pass the header information. Like below

@Service
public class StudentService {

private static final Logger logger = LoggerFactory.getLogger(StudentService.class);

private final StudentFeignClient studentFeignClient;

@Autowired
public StudentService(final StudentFeignClient studentFeignClient) {
this.studentFeignClient = studentFeignClient;
}

@CircuitBreaker(name = "studentService", fallbackMethod = "getStudentByIdFallbackMethod")
public Object getStudentById(final int id) {
logger.info("Student feign client to get student data");
return studentFeignClient.getStudentById(id).getData().get("student");
}

public Object getStudentByIdFallbackMethod(final int id, final Throwable th) {
logger.error("Failed to get student details", th);
return "Student details not found for student with id.. " + id + " No response from student server";
}

}

Reference code

For more information about the implementation, please visit my GitHub page mentioned below.

https://github.com/DeepuGeorgeJacob/school-management/blob/main/docs/FeignClientJDBCUserSecurity.md

Conclusion

In conclusion, the decision between passing headers directly to a Feign client and utilizing a RequestInterceptor in microservice communication hinges on the level of customization and flexibility required in handling HTTP requests. When specific headers need to be included on a per-request basis, the @RequestHeader annotation offers a targeted approach within the Feign client interface. On the other hand, employing a RequestInterceptor provides a centralized mechanism for modifying requests globally, offering a more streamlined and consistent solution, especially when multiple requests share common requirements. Both approaches empower Java developers to tailor microservice communication effectively, ensuring a balance between simplicity and granular control based on the unique needs of their architecture.

Please reach out to me on LinkedIn for more queries.

--

--