Maintainable error handling with Feign clients? Not a dream anymore

Over the last couple of years, I’ve been using Feign to invoke HTTP APIs, let it be external or internal. If you are not familiar with Feign, here’s a very brief intro. Feign is a declarative HTTP client. You define an interface, take some magical annotations and you have yourself a fully functioning client that you can use to communicate via HTTP.

Feign is a standalone library, anybody can use it on a project. It comes with its own annotations, types and configuration. Here’s an example client from the docs:

Having a tool to define APIs like this is a great way to reduce application complexity. Think about writing the same thing with Apache HttpComponents. Let me give you an idea:

I hope the difference is obvious. There’s tons of boilerplate code in the latter example.

Since Spring is one of the most used base frameworks in any Java project, there’s another level of abstraction that the Spring ecosystem provides. What if you don’t need to rely on the custom Feign annotations but you use the same Spring annotations just like when a controller is defined, e.g. @RequestMapping, @PathVariable and so on.

Well, Spring Cloud adds this capability to your application. The former example with Spring annotations looks the following:

Quite neat compared to the previous examples.

Although, there’s one downside to any abstraction. Or maybe downside is not even the best word to describe it, its rather a trade-off that we – engineers – often forget.

One of the points of an abstraction is to hide details from its users to ease development, which is absolutely spot on in case of Feign. However, the trade-off you are going to make is less control over that particular piece of code. For normal use-cases it’s often perfectly fine but as soon as you hit a wall and you need some specific behavior, you have to start digging. Where? I guess most of us just Google for some time, hoping that somebody has asked a similar question on Stackoverflow. Sometimes it’s just not the case, and you have to jump right into the code to figure out how to work-around the framework.

Error handling in Feign

Fortunately the framework creators have thought about having some way of reacting to errors during an API call. The ErrorDecoder interface is used for that purpose.

Such an interface implementation can be tied to creating a particular Feign client. In the standard Feign world, you can specify it during the Builder calls, like:

So you can customize the Decoder you’d like to use on a per client basis, but not on a method basis. That’s just a limitation of the capabilities the library is providing.

How does this look in the Spring world? If you’d like to use an ErrorDecoder, you can just simply register it as a bean and the framework will automatically pick it up and assign it to every Feign client.

Very neat, but since within an application there could be several Feign clients used, does it make sense to use a single decoder for all clients? Probably not. But even if you go one level deeper, you might need to have different error handling logic for different API calls within the same client.

The junior implementation

So if you were carefully reading, you might have noticed a parameter in the signature of the ErrorDecoder, methodKey. The methodKey is automatically generated by the Feign library whenever an error response is received from the downstream API. An example methodKey looks the following:

It starts with the Feign client class name, then a hash symbol and then the name of the method, followed by its parameter types within parentheses. The key generation can be found here: Feign#configKey

The first implementation one might think would be some kind of string magic on the methodKey:

Obviously this is going to work, but it won’t scale and as soon as you rename a class/method, you’ll end up in some functional problems in your application since the String renaming might be easily messed up (even though IDEs are very smart these days).

Testing is going to be hell with an implementation like this, cyclomatic complexity is going to increase with the number of clients and API methods. There must be a solution out there and somebody must have figured it out already.

Well, I thought the same, but so far I haven’t found anything on this.

The proper solution

First of all, this solution is aiming to address Spring only, when using the bare Feign client library, there are some minor tweaks required.

For a Spring Cloud based Feign client, you need to use the @FeignClient annotation on the interface like this:

Pretty easy I’d say. So what happens when there’s an error, for example 404 returned by the API?

As you can see, the original API I was calling resulted in a 500 Internal Server Error because the downstream user-service was responding with a 404. Not good.

So what can I do if I want to translate this 404 response into a UserNotFoundException within the service? And what if I’d like to do something else for another method within the same client?

Well, let’s create a generic ErrorDecoder that can defer the error handling to other classes, similarly how we do with a @ControllerAdvice and @ExceptionHandler class in the Sprint MVC world.

I’m going to use a custom annotation to mark methods or the client class which needs special treatment on its error handling:

FeignHttpExceptionHandler is a simple interface with a single method:

The usage is going to look the following:

The implementation for UserServiceClientExceptionHandler is very simple:

Of course you can make it more sophisticated, this is just an example.

So how does the annotation work? As I said, we are going to use a special ErrorDecoder. First of all, we have to understand the signature of the ErrorDecoder interface. Since there’s no information within the decoder which client’s which method was called, somehow we have to figure it out so we can invoke the corresponding error handler.

One way to do it is to utilize the methodKey parameter and build a map based on that with the error handlers. But before that, we need to somehow get a reference to all the Feign clients registered within the application:

First off, it’s loading all the Spring beans with the @FeignClient annotation. Since Feign is based on interfaces, there are JDK proxies involved, that’s why we need to call aClass.getInterfaces()[0] to get the actual interface with its methods.

Then the only trick is to calculate the methodKey which I’m doing using the Feign.configKey method call (that’s the one Feign is also using). And as well we’re searching for the HandleFeignError annotation on method and on class level in the same order.

So as soon as the Spring context is set up, we’ll have a map of methodKeys to actual exception handlers.

The second thing we need to do is to make sure this class implements the ErrorDecoder interface.

So the decode method is very simple. If the map contains the methodKey with its corresponding exception handler, we’ll use that for resolving the proper Exception, otherwise the solution is falling back to the Default ErrorDecoder.

Using the Feign client afterwards is quite easy:

Conclusion

With this solution in place, you can easily define proper error handling on the level of Feign client methods with any custom logic/custom exceptions you want to use. It allows you to build a more maintainable and robust application.

As usual, if you are interested in more, follow me on Twitter for updates.

Leave a Reply

Your email address will not be published. Required fields are marked *