Feign 可以在服务消费者和服务提供者之间进行GET和Post多参数传递的。springmvc中是支持GET方法绑定pojo的,但是Feign 并未覆盖springmvc中的所有方法,目前解决方式很多,常见的有如下方式:
- 把pojo拆分成一个个单独的属性做为方法参数
- 方法参数作为map传递
如果不这么解决当使用对象做为参数时会报以下错误:
当然以下错误的原因还有可能是通过Feign 进行服务间调用时,GET方法的参数没有加@PathVariable或@RequestParam注解
feign.FeignException$MethodNotAllowed: status 405 reading UserProviderClientService#save(UserEntity)
at feign.FeignException.clientErrorStatus(FeignException.java:167)
at feign.FeignException.errorStatus(FeignException.java:141)
at feign.FeignException.errorStatus(FeignException.java:133)
at feign.codec.ErrorDecoder$Default.decode(ErrorDecoder.java:92)
at feign.SynchronousMethodHandler.executeAndDecode(SynchronousMethodHandler.java:151)
at feign.SynchronousMethodHandler.invoke(SynchronousMethodHandler.java:80)
at feign.hystrix.HystrixInvocationHandler$1.run(HystrixInvocationHandler.java:109)
at com.netflix.hystrix.HystrixCommand$2.call(HystrixCommand.java:302)
at com.netflix.hystrix.HystrixCommand$2.call(HystrixCommand.java:298)
at rx.internal.operators.OnSubscribeDefer.call(OnSubscribeDefer.java:46)
at rx.internal.operators.OnSubscribeDefer.call(OnSubscribeDefer.java:35)
at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:48)
at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:30)
at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:48)
at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:30)
at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:48)
at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:30)
at rx.Observable.unsafeSubscribe(Observable.java:10327)
at rx.internal.operators.OnSubscribeDefer.call(OnSubscribeDefer.java:51)
at rx.internal.operators.OnSubscribeDefer.call(OnSubscribeDefer.java:35)
at rx.Observable.unsafeSubscribe(Observable.java:10327)
at rx.internal.operators.OnSubscribeDoOnEach.call(OnSubscribeDoOnEach.java:41)
at rx.internal.operators.OnSubscribeDoOnEach.call(OnSubscribeDoOnEach.java:30)
at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:48)
at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:30)
at rx.Observable.unsafeSubscribe(Observable.java:10327)
at rx.internal.operators.OperatorSubscribeOn$SubscribeOnSubscriber.call(OperatorSubscribeOn.java:100)
at com.netflix.hystrix.strategy.concurrency.HystrixContexSchedulerAction$1.call(HystrixContexSchedulerAction.java:56)
at com.netflix.hystrix.strategy.concurrency.HystrixContexSchedulerAction$1.call(HystrixContexSchedulerAction.java:47)
at org.springframework.cloud.sleuth.instrument.async.TraceCallable.call(TraceCallable.java:70)
at com.netflix.hystrix.strategy.concurrency.HystrixContexSchedulerAction.call(HystrixContexSchedulerAction.java:69)
at rx.internal.schedulers.ScheduledAction.run(ScheduledAction.java:55)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:748)
上面的问题还可以通过拦截器去解决,Feign 提供了一个请求拦截器RequestInterceptor,可以在请求之前给请求加请求头等功能,有点类似于spring cloud gateway中的断言工厂,有关spring cloud gateway可以查看springcloud 入门 之网关 springcloud gateway
代码如下:
@Component
public class FeignRequestInterceptor implements RequestInterceptor {
@Autowired
private ObjectMapper objectMapper;
/**
* Called for every request. Add data using methods on the supplied {@link RequestTemplate}.
*
* @param template
*/
@Override
public void apply(RequestTemplate template) {
final String method = template.method();
final Request.Body requestBody = template.requestBody();
if ("GET".equals(method) && requestBody != null && requestBody.bodyTemplate() != null) {
final String requestParam = requestBody.asString();
try {
//把之前的请求体清空
template.body(Request.Body.empty());
final JsonNode jsonNode = objectMapper.readTree(requestParam);
Map<String, Collection<String>> params = new HashMap<>(jsonNode.size());
buildQuery(jsonNode , "" , params);
template.queries(params);
} catch (IOException e) {
//可根据项目需求处理异常
e.printStackTrace();
}
}
}
private void buildQuery(JsonNode jsonNode, String path, Map<String, Collection<String>> queries) {
if (!jsonNode.isContainerNode()) { // 叶子节点
if (jsonNode.isNull()) {
return;
}
Collection<String> values = queries.computeIfAbsent(path, k -> new ArrayList<>());
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);
}
}
}
}
}
通过FeignRequestInterceptor拦截器就能解决GET方法无法传递pojo的问题了。
有关feign的学习可以参考springcloud 入门(3) 声明式调用 Feign
学习更多关于springcloud的可以关注我的 springcloud 专栏
GitHub地址:
https://github.com/ArronSun/micro-services-practice.git
参考:
《重新定义springcloud 实战》
能力一般,水平有限,如有错误,请多指出。
如果对你有用点个关注给个赞呗