IFeignAutoConfiguration
@Configuration
@ConditionalOnClass({
Feign.class})
@Order(Ordered.LOWEST_PRECEDENCE - 10000)
public class IFeignAutoConfiguration {
private final ObjectMapper objectMapper;
public IFeignAutoConfiguration(ObjectMapper objectMapper) {
this.objectMapper = objectMapper;
}
/**
* 用户信息传递,feign发起请求时,将当前用户信息放到请求头
* @return RequestInterceptor
*/
@Bean
public RequestInterceptor headerUserFeignRequestInterceptor() {
return new HeaderUserFeignRequestInterceptor();
}
/**
* feign 不支持 GET 方法传 POJO, json body转query
* 名为charlesRequestInterceptor的bean不存在时才装配
* @return RequestInterceptor
*/
@Bean
@ConditionalOnMissingBean(name = "charlesRequestInterceptor")
public RequestInterceptor charlesRequestInterceptor() {
return new CharlesRequestInterceptor(objectMapper);
}
}
CharlesRequestInterceptor
/**
* feign 不支持 GET 方法传 POJO, json body转query
*/
public class CharlesRequestInterceptor implements RequestInterceptor {
private static Logger log = LoggerFactory.getLogger(CharlesRequestInterceptor.class);
private ObjectMapper objectMapper;
public CharlesRequestInterceptor(ObjectMapper objectMapper) {
this.objectMapper = objectMapper;
}
@Override
public void apply(RequestTemplate template) {
// feign 不支持 GET 方法传 POJO, json body转query
if (template.method().equals("GET") && template.body() != null) {
try {
JsonNode jsonNode = objectMapper.readTree(template.body());
template.body(null);
Map<String, Collection<String>> queries = new HashMap<>();
buildQuery(jsonNode, "", queries);
template.queries(queries);
} catch (IOException e) {
// e.printStackTrace();
log.error(e.getMessage());
}
}
}
private void buildQuery(JsonNode jsonNode, String path, Map<String, Collection<String>> queries) {
if (!jsonNode.isContainerNode()) {
// 叶子节点
if (jsonNode.isNull()) {
return;
}
Collection<String> values = queries.get(path);
if (null == values) {
values = new ArrayList<>();
queries.put(path, values);
}
values.add(jsonNode.asText());
return;
}
if (jsonNode.isArray()) {
// 数组节点
Iterator<JsonNode> it = jsonNode.elements();
while (it.hasNext()) {
buildQuery(it.next(), path, queries);
}
} else {
Iterator<Map.Entry<String, JsonNode>> it = jsonNode.fields();
while (it.hasNext()) {
Map.Entry<String, JsonNode> entry = it.next();
if (StringUtils.hasText(path)) {
buildQuery(entry.getValue(), path + "." + entry.getKey(), queries);
} else {
// 根节点
buildQuery(entry.getValue(), entry.getKey(), queries);
}
}
}
}
}
HeaderUserFeignRequestInterceptor
/**
* 用户信息传递,feign发起请求时,将当前用户信息放到请求头
**/
public class HeaderUserFeignRequestInterceptor implements RequestInterceptor {
@Override
public void apply(RequestTemplate requestTemplate) {
String userInfo = ExtractUserInfoUtil.getUserInfoHeader();
//feign请求时将当前的用户信息回填到request中,前提是当前请求不包含"user-info"这个header的情况下
Map<String, Collection<String>> headers = requestTemplate.headers();
if(!headers.containsKey(ExtractUserInfoUtil.USER_INFO_HEADER) || CollectionUtils.isEmpty(headers.get(ExtractUserInfoUtil.USER_INFO_HEADER))) {
//填充请求头
requestTemplate.header(ExtractUserInfoUtil.USER_INFO_HEADER, userInfo);
}
}
}
留下个思考题:假设我需要在某些指定的feign客户端接口方法(使用注解标注),才需要应用上述的请求拦截器该如何实现(也就是不要让它们应用到全局,而只在调用具体的方法才决定是否使用拦截器)?
可以继承子SpringMvcContract,复用并重写解析的方法;使用@RequestHeader注解(这个需要试试)