I want to get the header value from the reply of http call, store it in cache and use this value in next requests by passing it to header of the http requests in order to achieve sticky session. My project is in Java 8.
I have feign http client builder like this which decode using JacksonDecoder.
public <T> T buildClient(Class<T> type, String url) {
return Feign.builder()
.contract(new JAXRSContract())
.encoder(new JacksonEncoder(mapper))
.decoder(new CustomDecoder(mapper))
.logger(new Slf4jLogger(Constants.FFA_LOGGER_NAME))
.logLevel(Level.FULL)
.options(new Options(connectTimeout, readTimeout))
.retryer(Retryer.NEVER_RETRY)
.requestInterceptor(auth2FeignRequestInterceptor)
.invocationHandlerFactory(factory)
.target(type, url);
}
The default decoder from jackson decode only body not header so I am implementing my own CustomDecoder.
What I want to achieve is get the value of the resonse.headers and map it to body or add the property in the object responseBodyObject dynamically after it gets the value from the mapper.
public final class CustomDecoder extends JacksonDecoder implements Decoder {
@Override
public Object decode(Response response, Type type) throws IOException {
//Here the default decoder only decoding body.
Reader reader = response.body().asReader();
Object responseBodyObject = mapper.readValue(reader, mapper.constructType(type));
If I understand it right, this is a case of constructed type having additional properties that should be populated from header. Then, there are several approaches here. First, the one I would prefer, is having a special interface kinds that your decoder knows about, and recoded objects can implement to receive the values:
public interface SessionTokenAware {
void setSessionToken(String value);
}
And then in your decoder:
@Override
public Object decode(Response response, Type type) throws IOException {
Object parsedResponse = mapper.readValue(
response.body().asReader(),
mapper.constructType(type)
);
if (parsedResponse instanceof SessionTokenAware) {
SessionTokenAware sessionAware = (SessionTokenAware) parsedResponse;
Collection<String> sessionHeader = response.headers().get("sessionId");
if (sessionHeader != null && !sessionHeader.isEmpty()) {
sessionAware.setSessionToken(sessionHeader.iterator().next());
}
}
return parsedResponse;
}
When your response implements the interface, then it will get the session token value set by decoder without having to hack the body stream and adding anything to it:
public class MyResponseBody implements SesionTokenAware {
private String token;
@Override
public void setSessionToken(String value) {
token = value;
}
public String getSessionToken() {
return token;
}
}
Altenatively, if you don't like having separate interfaces for every kind of possible headers you would want to receive, you can make a catch-all interface and let every response implementation sort out headers itself:
public interface HeadersAware {
void onHeaders(Map<String, Collection<String>> headerValues);
}
public Object decode(Response response, Type type) throws IOException {
Object parsedResponse = ....
if (parsedResponse instanceof HeadersAware) {
HeaderAware headerAware = (HeaderAware) parsedResponse;
headerAware.onHeaders(response.headers());
}
return parsedResponse;
}
public class MyResponse implements HeaderAware {
private String token;
public void onHeaders(Map<String, Collection<String>> headers) {
Collection<String> sessionHeader = headers.getOrDefault("sessionId", emptySet());
if (!sessionHeader.isEmpty()) {
token = sessionHeader.iterator().next();
}
}
}
I don't like the latter approach for two reasons:
CustomDecoder
class.