Do you know Feign? It is a Java library that allows you to write a REST client with minimal code. In my current work project, we are currently using it and as we had no experience with it, we are continually improving our knowledge on this library.
We recently encountered a particular use case, that although we could not find an example on the Internet, we were able to find a working solution that you might be interested in. π₯°
Context
We are currently using an external REST API which seems a bit special: for their GET endpoints, we are able to filter the returned ressources with their API, but we cannot pass these filters as normal GET parameters. We have to pass them directly in the URL with a special syntax! π²
Let's see an example. If we want to filter the /activities
endpoint to get the activity with ID 65, we need to call the /activities/id;65
endpoint. A little weird, right? π€
OR filter
If we want to get two separate activities that have the ID 65 OR 76, the syntax is similar. We must add ;76
to the previous endpoint: /activities/id;65;76
.
AND filter
Unfortunately, the filters can be more complicated. If we want to have the activities for 2021 (therefore from January 1 to December 31), we must have an AND filter. And what do you think the syntax looks like? π
The URL would be /activities/startDate;012F%012F%2021/endDate;122F%312F%2021
. I am pretty sure you did not guess it, right? π€£ The 2F%
is the encoded version of a slash (/
) character. And yes, the dates must be in the MM/DD/YYYY
format... π
Toward the solution
Since we did not want to manually create the filters for all endpoints before calling them, we started looking for a better solution. So we start with a single @GetMapping
endpoint:
But although the API returns results when we call it from our code, we found that the filters were not applied and the API returned all activities instead of an error... π€―
Encoding
It turns out that Feign automatically encodes the URL path variable, so the final URI looks like /activities/startDate%3B04%252F01%252F2021
instead of /activities/startDate;012F%012F%2021
that their application was expecting (I removed the endDate
filter for clarity).
Thus, semicolons (;
) and forward slashes (/
) were encoded, as they are generally not expected in a path variable. Fortunately, we quickly found this answer from StackOverflow that steers us on the concept of RequestInterceptor. We were able to decode the encoded values with the code below. π
Now that this issue is resolved, let's get back to creating filters.
Final solution
Use a custom class as @PathVariable
Our original solution was to create a custom builder class that would build a String based on certain values passed as parameters.
But going through the code created by my colleague, I wonder if we could just pass a custom class to Feign as a @PathVariable
instead. So we quickly test the following code, where FilterField
is our custom class:
Let's see the final URL that Feign calls in the output:
Overriding the toString function
Interesting, isn't it? This looks like the default implementation of an object's toString function. And what happens when we override it into our class?
You guessed it, our URL looks like exactly as we want! π
Using a List
But since we want to have the possibility to use a list of FilterField
, let's update our client t0 allow it:
This is almost correct, the separator between the filters should be a slash (/
) instead of a comma (,
)! Guess how we solved this problem? By creating a new class! π
Creation of a FilterFieldList
First, let's replace our code in our Feign client:
And now, the implementation of our FilterFieldList
class:
With this final class, we now have a complete working solution for the weird external API that we are using! π₯³
We initially try to extend List<FilterField>
with the overridden function, but somehow, Feign did not call our toString
function... Maybe Feign is doing something different if the custom class is an instance of List
? π€
Conclusion
So, in this post, you learned about two Feign concepts: RequestInterceptor and PathVariable. Hope this would help you as we wish we had found this post before! π
Note : part of the code displayed was made by my colleague, Alexandre CΓ΄tΓ©, and I could have found this solution without its help. I think that together we managed to find a better solution, so thanks Alex! π
Top comments (0)