Albert Waninge
Albert Waninge

Reputation: 357

How to create a custom decoder for a Spring Cloud OpenFeign Client

I have a FeignClient which needs to call an endpoint, which returns the data wrapped in a PageView object. I don't want to bother any service with this object, so I am looking for a way to get it done during the processing of the response.

The Feign client looks like this (Kotlin):

@FeignClient(
    value = "myFeignClient",
    url = "\${controller.baseUrl}",
    configuration = [CustomFeignConfiguration::class]
)
interface MyFeignClient {

@GetMapping(value = ["v1/myObjects/someProperty/{value}"])
fun getMyObjectsBySomeProperty(@PathVariable(value="value") value: String): List<MyObject>

The PageView class looks like this:

data class PageView<T>(
    val metadata: PageMetadataView,
    val content: List<T>,
)

I know that it can be done by defining a custom decoder, but the documentation on Spring Cloud Open Feign does not cover this topic.

Upvotes: 0

Views: 100

Answers (1)

Albert Waninge
Albert Waninge

Reputation: 357

I have found a way to solve this issue using a custom decoder. All the code examples are in Kotlin.

First, create a class for the PageView, which can be used for deserialization by Jackson. You only need to specify the relevant properties, which in my case is 'content'. Of course, your data class also needs to be defined somewhere such that Jackson deserializes it properly.

import com.fasterxml.jackson.annotation.JsonIgnoreProperties

@JsonIgnoreProperties(ignoreUnknown = true)
class MyObjectsPageView(val content: List<MyObject>)

Then define a decoder like this:

import feign.Response
import feign.codec.Decoder
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter
import java.lang.reflect.Type

class MyObjectsPageViewDecoder : Decoder {

    private val jacksonConverter = MappingJackson2HttpMessageConverter()

    override fun decode(response: Response?, type: Type?): List<MyObject> {
        val pageView: MyObjectPageView = jacksonConverter.objectMapper.readValue(response!!.body().asInputStream(), MyObjectsPageView::class.java)
        return pageView.content
    }
}

Notice that I did not take any effort to make this decoder generic. I was not familiar enough yet with Kotlin and generics :-P

Finally, you need your FeignConfiguration to provide this custom decoder as a bean:

class CustomFeignConfiguration{

    @Bean
    fun pageViewDecoder(): Decoder = MyObjectsPageViewDecoder ()
}

Notice that this custom decoder will be provided for all functions in your feign client.

Upvotes: 1

Related Questions