spring-cloud/spring-cloud-openfeign

Provide a way to specify static headers per request basis

mipo256 opened this issue ยท 7 comments

We have a feign client, like this (very roughly):

@FeignClient(name = "service", url = "http://somehost")
interface MediaClient {
   
    @PostMapping(path = "/v1/upload")
    void uploadFile(@RequestBody byte[] data);

    @GetMapping(path = "/v1/files/{fileId}")
    FileData requestFile(String fileId);
}

The thing is - we want to add static header for uploadFile() request - Content-Type: multipart/form-data. This header will not change from invocation to invocation, so we do not want to go for @RequestHeader.

We also do not want to create a RequetsInterceptor, since in this case it will be applied for entier feign client, but the requestFile() or any other method that we have in this method in reality - we do not want Content-Type: multipart/form-data header to be in the request for obvious reasons.

We have tried produces or headers annotation arguments in @PostMapping - did not work.
My proposal is to have something like @feign.Headers, that can be put on the method. Or maybe it would be a good option to support feign native annotation.

We have tried produces or headers annotation arguments in @PostMapping - did not work.

I think you should use consumes instead of produces of @PostMapping.

The idea is that the request mapping method signatures in your Feign client interfaces should match the target endpoints of the upstream services.

Therefore, we cannot think of the produces attribute as what this microservice produces as request payload/body in this HTTP call. Instead, we should think of the produces attribute as what our microservice expects the target service to produce. Therefore, produces in Feign clients converts to the Accept request header when sending the HTTP calls.

  • consumes โžœ Content-Type
  • produces โžœ Accept

You may check the Javadoc of @RequestMapping:

/**
 * Narrows the primary mapping by media types that can be consumed by the
 * mapped handler. Consists of one or more media types one of which must
 * match to the request {@code Content-Type} header.
 * ...
 */
String[] consumes() default {};

/**
 * Narrows the primary mapping by media types that can be produced by the
 * mapped handler. Consists of one or more media types one of which must
 * be chosen via content negotiation against the "acceptable" media types
 * of the request. Typically those are extracted from the {@code "Accept"}
 * header but may be derived from query parameters, or other.
 * ...
 */
String[] produces() default {};

To be honest, I was also confused when I start using it.
Hope this helps. ๐Ÿ˜„

Hello @mikhail2048, accept/produces in SC OF works exactly as @blackr1234 has explained. Does this solve your issue?

If you would like us to look at this issue, please provide the requested information. If the information is not provided within the next 7 days this issue will be closed.

I'm facing similar issue. In my case I need to send a custom header from the feign client, and none of the approaches seem to work ( using @Headers or using headers=)

Relevant code:

package com.example.demo;

import feign.Headers;
import feign.RequestLine;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;

@FeignClient(
        name = "express",
        url = "http://localhost:3000/something",
        configuration = FeignConfiguation.class
)
@Headers("customHeader:1234")
interface ExpressFiegnClient {
    @RequestLine("GET")
    @GetMapping(value = "/",headers = "customHeader:1111")
    @Headers("customHeader:9999")
    public String getName();
}

Headers as received by the client:

{
  accept: '*/*',
  'user-agent': 'Java/17.0.7',
  host: 'localhost:3000',
  connection: 'keep-alive'
}

Is this a bug or there's another way to include the custom header with every request?

You can specify defaultRequestHeaders per FeignClient. See the docs. Does this solve your issue?

Yes. Thanks much. Any reason why @Headers don't work though?

Mixing up Spring MVC style annoations with core Feign annotations may cause issues. The annotation we support in SC OpenFeign for headers is (@RequestHeader passed at method parameter level).