Spring Cloud Feign 多参数传递了解一下?

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 实战》

能力一般,水平有限,如有错误,请多指出。
如果对你有用点个关注给个赞呗

posted @ 2022-04-29 19:05  码猿笔记  阅读(337)  评论(0编辑  收藏  举报