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 varBuilder = new LinkedHashMap(); for (Entry> entry : metadata.indexToName().entrySet()) { int i = entry.getKey(); Object value = argv[entry.getKey()]; if (value != null) { // Null values are skipped. if (indexToExpander.containsKey(i)) { value = expandElements(indexToExpander.get(i), value); } for (String name : entry.getValue()) { varBuilder.put(name, value); } } } ...

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('}');
}