feign icon indicating copy to clipboard operation
feign copied to clipboard

Provide RequestTemplate.getParams() to access template variables (@Param) within an interceptor

Open d0x opened this issue 8 years ago • 4 comments

It would be nice having a method to access @Param annotated parameters within an Interceptor. Currently @Param template variables are only applied to header, requestline or the body.

As example this kind of RequestTemplate.getParams() would really help us.

public class MyServce {
   private final MyClient client;

   public void doSmth() {
      // Set the codec to some value. e.g. MP3. The key thing is that only the class "MyService"
      // knows which codec to choose.
      client.getSomething("MP3");
   }
}

public interface MyClient {
    // define the template variable "codec".
    // codec is not used in RequestLine, Body or Header
    @RequestLine("GET /")
    Object getSomething(@Param("codec") String codec);
}

public class CodecInterceptor implements RequestInterceptor {
    @Override
    public void apply(RequestTemplate requestTemplate) {
        // use params.get("codec") to get the codec and generate the headers
        Map<String, Object> params = requestTemplate.getParams();
        // ... 
    }
}

Since the business logic to detect which "codec" is required, is and should be placed in MyService, there is currently no way to pass this information (in a thread safe manner) to the interceptor. If there would be a RequestTemplate.getParams(), the business logic to detect the codec could stay in MyService and the logic to translate the codec into headers could be done within the interceptor.

That would help to accomplish separation of concerns (Service=BusinessLogic, Interceptor=HttpMagic).

d0x avatar Nov 13 '17 20:11 d0x

Another example would be that MyService finds the current UserId and then passes it to the interceptor which will use the UserId to generate a valid token.

The same is true for the BasicAuthRequestInterceptor.java . The Interceptor works only because everytime the same User:Password is applied. With the current API it wouldn't be possible to write an Interceptor which gets an new User:Password for every request.

d0x avatar Nov 13 '17 21:11 d0x

This could be solved using @CustomParam annotation if #667 gets pulled in.

The caveat here being that interceptors are run again on every retry, while param encoders are only run once on initial template generation. So if you don't need a new pass on every retry, this should work for you. Would this help for your use case?

rage-shadowman avatar Mar 24 '18 03:03 rage-shadowman

@d0x

In this situation, you may be able to use a custom Parameter Expander to resolve the value. For example:

public interface MyClient {
   
    @RequestLine("GET /")
    @Headers("Codec-Header: {codec}")
    Object getSomething(@Param("codec", expander=CodecExpander.class) String codec);
}

The CodecExpander can process the codec value and return the expanded version of the parameter. This would allow you to completely change what codec resolved to on-demand. Now, this only works if you need to resolve a single template expression, but it may be enough to help you, without requiring a new feature.

kdavisk6 avatar Apr 10 '19 12:04 kdavisk6

I need 2 clients:

  • a client that uses a dynamic basic auth (change username and password at runtime), but I haven't found a feature for that.
  • a client that uses a custom auth that uses URL path, user and auth as parameters.

An interceptor that receives parameters as username and password can be used for these 2 scenarios.

nicolasmafra avatar May 18 '22 19:05 nicolasmafra