在 Feign RequestInterceptor / RequestTemplate 中访问 URITemplate 或 RequestLine 值
Access URITemplate or RequestLine value in Feign RequestInterceptor / RequestTemplate
我正在开发一个针对具有严格 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
)
有办法吗?
谢谢!
也许您可以尝试使用自定义 feign InvocationHandlerFactory。
我已经使用如下代码成功记录了 RequestInterceptor:
更改 EnableFeignClients 并添加默认配置
@EnableFeignClients(defaultConfiguration = FeignConfig.class)
添加默认伪装配置
@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();
}
}
小心并检查你是否假装配置不更复杂,在这种情况下根据需要扩展 类。
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 Logger。
假设我们有一个假客户,
@FeignClient(name = "demo-client", url = "http://localhost:8080/api", configuration = FeignConfig.class)
public interface DemoClient {
@GetMapping(value = "/test/{id}")
void test(@PathVariable(name = "id") Integer id) {
}
}
import feign.Logger;
import feign.Request;
import feign.Response;
import java.io.IOException;
public class CustomFeignRequestLogging extends Logger {
@Override
protected void logRequest(String configKey, Level logLevel, Request request) {
super.logRequest(configKey, logLevel, request);
// targetUrl = http://localhost:8080/api
String targetUrl = request.requestTemplate().feignTarget().url();
// path = /test/{id}
String path = request.requestTemplate().methodMetadata().template().path();
}
}
我正在开发一个针对具有严格 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
)
有办法吗?
谢谢!
也许您可以尝试使用自定义 feign InvocationHandlerFactory。
我已经使用如下代码成功记录了 RequestInterceptor:
更改 EnableFeignClients 并添加默认配置
@EnableFeignClients(defaultConfiguration = FeignConfig.class)
添加默认伪装配置
@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(); } }
小心并检查你是否假装配置不更复杂,在这种情况下根据需要扩展 类。
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 Logger。
假设我们有一个假客户,
@FeignClient(name = "demo-client", url = "http://localhost:8080/api", configuration = FeignConfig.class)
public interface DemoClient {
@GetMapping(value = "/test/{id}")
void test(@PathVariable(name = "id") Integer id) {
}
}
import feign.Logger;
import feign.Request;
import feign.Response;
import java.io.IOException;
public class CustomFeignRequestLogging extends Logger {
@Override
protected void logRequest(String configKey, Level logLevel, Request request) {
super.logRequest(configKey, logLevel, request);
// targetUrl = http://localhost:8080/api
String targetUrl = request.requestTemplate().feignTarget().url();
// path = /test/{id}
String path = request.requestTemplate().methodMetadata().template().path();
}
}