在 Feign RequestInterceptor / RequestTemplate 中访问 URITemplate 或 RequestLine 值
Posted
技术标签:
【中文标题】在 Feign RequestInterceptor / RequestTemplate 中访问 URITemplate 或 RequestLine 值【英文标题】:Access URITemplate or RequestLine value in Feign RequestInterceptor / RequestTemplate 【发布时间】:2019-08-27 03:12:58 【问题描述】:我正在针对具有硬 api 速率限制的云应用程序开发应用程序。为了让我的团队了解我们在这些限制方面有多接近,我想以一种有意义的方式统计从我们的应用发出的所有 API 调用。
我们使用 Feign 作为访问层,我希望能够使用 RequestInterceptor
来统计我们调用的不同 API 端点:
RequestInterceptor ri = rq -> addStatistics(rq.url());
现在这不起作用,因为生成的 URL 之后几乎总是计数为“1”,因为它们已经包含所有解析的路径变量,所以我得到计数
1 - /something/id1valueverycryptic/get
1 - /something/anothercrypticidkey/get
等等。
我希望以某种方式访问 @ResuqestLine
映射值 (GET /something/id/get
) 或至少 uri 模板预解析 (/somethine/id/get
)
有没有办法做到这一点?
谢谢!
【问题讨论】:
它是 Spring Boot 应用程序吗? 是的 - 添加了标签来表明这一点。 【参考方案1】:也许您可以尝试使用自定义 feign InvocationHandlerFactory。
我已经设法使用如下代码记录 RequestInterceptor:
更改 EnableFeignClients 并添加 defaultConfiguration
@EnableFeignClients(defaultConfiguration = FeignConfig.class)
添加默认的 feign 配置
@Configuration
public class FeignConfig
@Bean
@ConditionalOnMissingBean
public Retryer feignRetryer()
return Retryer.NEVER_RETRY;
@Bean
@Scope("prototype")
@ConditionalOnMissingBean
public Feign.Builder feignBuilder(Retryer retryer)
return Feign.builder()
.retryer(retryer)
.invocationHandlerFactory((target, dispatch) -> new CountingFeignInvocationHandler(target, dispatch));
创建您的调用处理程序(基于 feign.ReflectiveFeign.FeignInvocationHandler 的代码)
public class CountingFeignInvocationHandler implements InvocationHandler
private final Target target;
private final Map<Method, MethodHandler> dispatch;
public CountingFeignInvocationHandler(Target target, Map<Method, MethodHandler> dispatch)
this.target = checkNotNull(target, "target");
this.dispatch = checkNotNull(dispatch, "dispatch for %s", target);
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
if ("equals".equals(method.getName()))
try
Object otherHandler =
args.length > 0 && args[0] != null ? Proxy.getInvocationHandler(args[0]) : null;
return equals(otherHandler);
catch (IllegalArgumentException e)
return false;
else if ("hashCode".equals(method.getName()))
return hashCode();
else if ("toString".equals(method.getName()))
return toString();
RequestLine requestLine = method.getAnnotation(RequestLine.class);
addStatistics(requestLine.value());
return dispatch.get(method).invoke(args);
@Override
public boolean equals(Object obj)
if (obj instanceof CountingFeignInvocationHandler)
CountingFeignInvocationHandler other = (CountingFeignInvocationHandler) obj;
return target.equals(other.target);
return false;
@Override
public int hashCode()
return target.hashCode();
@Override
public String toString()
return target.toString();
请小心检查您是否假装配置不是更复杂,在这种情况下根据需要扩展类。
【讨论】:
【参考方案2】: If you are using spring-cloud-starter-openfeign , You could do something like below
add the a primary contract bean
@Bean("YourContract")
@Primary
public Contract springpringContract()
return (targetType) ->
List<MethodMetadata> parseAndValidatateMetadata = new SpringMvcContract().parseAndValidatateMetadata(targetType);
parseAndValidatateMetadata.forEach(metadata ->
RequestTemplate template = metadata.template();
template.header("unresolved_uri", template.path().replace("", "[").replace("", "]"));
);
return parseAndValidatateMetadata;
;
Add the contract to the feign client builder
@Bean
public <T> T feignBuilder(Class<T> feignInterface, String targetURL)
return Feign.builder().client(getClient())
.contract(contract)
.
.
Once you are done with the above you should be able to access the unresolved path in the RequestTemplate
@component
public class FeignRequestFilter implements RequestInterceptor
@Override
public void apply(RequestTemplate template)
String unresolvedUri = template.headers().getOrDefault("unresolved_uri", Collections.singleton(template.path()))
.iterator().next();
【讨论】:
以上是关于在 Feign RequestInterceptor / RequestTemplate 中访问 URITemplate 或 RequestLine 值的主要内容,如果未能解决你的问题,请参考以下文章