Hi,
I'm facing an issue about @RequestHeader request parameter with SpringMVCContract. Here is my feign client service :
@RequestMapping(method = RequestMethod.GET
, value = "/{param1}/{param2}"
, produces = { "application/json" }
)
ResponseEntity<MyResponsePojo> myResource(@PathVariable("param1") String param1, @PathVariable("param2") String param2, @RequestHeader(name="my-header-specified") String myHeaderSpecified);
And my test method which call my feign client
@Test(expected=HystrixBadRequestException.class)
public void should_throw_an_hystrix_bad_request(){
//given
ResponseEntity<MyResponsePojo> response = myFeignClient.myResource("noComptePayeur", "noReference", null);
}
When I look the log of the mock which receives the request, I see that my-header-specified is set with the templated name whereas I expect either it set no value or doesn't set the 'my-header-specified' header. '
2017-01-06 10:29:32.850 INFO [1250212479@qtp-1103243338-3] WireMock - Request received: GET /noComptePayeur/noReference HTTP/1.1 Accept: application/json my-header-specified: {my-header-specified} User-Agent: Java/1.8.0_72 Host: localhost:3222 Connection: keep-alive
Even If I set , required=false, defaultValue="" as attribute value to my @RequestHeader it doesn't fix it.
After some investigation I noticed in ReflectiveFeign$BuildTemplateByResolvingArgs class the method 'create' simply ignores null values
Override
public RequestTemplate create(Object[] argv) {
RequestTemplate mutable = new RequestTemplate(metadata.template());
...
Map
The problem is the class RequestHeaderParameterProcessor which process @RequestHeader set the data template header with templated name automatically
@Override public boolean processArgument(AnnotatedParameterContext context, Annotation annotation) { ... context.setParameterName(name);
MethodMetadata data = context.getMethodMetadata();
Collection<String> header = context.setTemplateParameter(name, data.template().headers().get(name));
data.template().header(name, header);
return true;
}
So when create method is called it see there is a no value and doesn't try to resolve it. So the templated name is kept.
Do you know any workaround ?
Thanks Regards
Comment From: RaphC
My temporary workaround was to set an interceptor which clean all header values matching {.*}
@Override
public void apply(RequestTemplate template) {
Map<String, Collection<String>> headers= template.headers();
Map<String, Collection<String>> sanitizedHeaders = Maps.newConcurrentMap();
for(Map.Entry<String, Collection<String>> entry : headers.entrySet()) {
List<String> santizedValues = Lists.newArrayList();
for(String entryValue : entry.getValue()) {
if(entryValue.matches("\\{.*\\}")){
LOG.debug("Ignoring value '{}' for header '{}' ...", entryValue, entry.getKey());
} else {
santizedValues.add(entryValue);
}
}
sanitizedHeaders.put(entry.getKey(), santizedValues);
}
// On reinjecte
template.headers(sanitizedHeaders);
}
Comment From: ryanjbaxter
Thanks @RaphC. Would it be possible to share the code that reproduces the problem.
Comment From: RaphC
Hi @ryanjbaxter
I created the gist https://gist.github.com/RaphC/c2a6a2cfda8d37d0cdf7795011f139ab . Let me know if you need more. I modified pom.xml to remove unneeded artifact.
Comment From: ryanjbaxter
Could you perhaps put this all in a repo?
Comment From: RaphC
@ryanjbaxter I'll do it ASAP
Comment From: RaphC
@ryanjbaxter You'll a simple unit test reproducinf the issue in this repo https://github.com/RaphC/spring-cloud-feign-examples
As you will see in the log th e header my-header-specified is set with "{my-header-specified}" whereas I set null on the parameter myFeignClient.myResource("noComptePayeur", "noReference", null);
2017-01-11 15:09:06.587 INFO 8560 --- [tp1092004553-29] WireMock : Request received:
127.0.0.1 - GET /noComptePayeur/noReference
User-Agent: [Java/1.8.0_72]
Connection: [keep-alive]
my-header-specified: [{my-header-specified}]
Host: [localhost:3222]
Accept: [application/json]
Regards
Comment From: ryanjbaxter
Thanks, I will take a look.
Just as a side note, maybe you could create a method that doesn't have the header parameter in your Feign Client and call that method when you have no header value to set. Have you tried that? It might be a simpler workaround then what you have today.
Comment From: RaphC
Thanks for the workaround. I'll try it.
Comment From: ryanjbaxter
Please let me know if it works, thanks!
Comment From: jmiddleton
Hi,
I'm facing the same problem, if I don't set the header a template with the name of the header is inserted. Is it possible to blank it in case of null value?
Thanks, Jorge
Comment From: ryanjbaxter
@jmiddleton we havent made any changes so no. Like I suggested above you can create a method in your client without the parameter and it shouldnt be included.
Comment From: RaphC
Hi @ryanjbaxter
I found an issue on feign about this problem (https://github.com/OpenFeign/feign/issues/503.) Perhaps help you.
Comment From: ryanjbaxter
It seems similar but in your case it was a header that was null in the Feign issue it is a parameterized body, so there are some differences. I havent looking into it to know for sure.
Comment From: tjuchniewicz
Looks like bug or feature in RequestTemplate from https://github.com/OpenFeign/feign
Object value = variables.get(var.toString());
if (value != null) {
builder.append(value);
} else {
builder.append('{').append(key).append('}');
}