填坑系列之OpenFeign(一)

前提

使用OpenFeign构建微服务消费端,发送带参请求时,控制台报错:

Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is feign.FeignException$InternalServerError: [500 ] during [GET] to [http://user-provider/user/isConsumerId] [UserConsumerService#isID(Integer)]: [{"timestamp":"2020-12-10T06:44:07.262+00:00","status":500,"error":"Internal Server Error","message":"","path":"/user/isConsumerId"}]] with root cause

方法

spring-cloud-openfeign-core 3.0.0版本以及旧版本中,必须在consumer的api需要传的参数前添加@RequestParam(“paramName”)(当然也可使用@PathVariable("id"),使用这个注解时,需要在consumer的controller层、api都要使用@PathVariable("id"),才能接收到参数,或者使用@RequestBody),参数名不可省略。

实例

以三个服务,分别为user-apiuser-provideruser-consumer,这里将api抽取出来,好处是可以将api打包发给用户,而且写起来也挺爽的...0.0。坏处是损失了平台异构性,即必须要java实现的平台才能调,不过也没啥关系,等有异构的需求时,在写另外一套就是了,互不干扰。

user-api:

@RequestMapping("/user")
public interface RegisterApi {

    @GetMapping("/isConsumerId")
    String isID(Integer id);
}

user-provider:

@RestController
public class UserController implements RegisterApi {

    @Override
    public String isID(Integer id) {
        return id == 1 ? "success" : "not find";
    }
}

user-consumer:

package com.yiming.user_consumer.service;

@FeignClient(name = "user-provider")
public interface UserConsumerService extends RegisterApi {

    @GetMapping("/isConsumerId")
    String isID(@RequestParam("id") Integer id);
}
package com.yiming.user_consumer.controller;

@RestController
public class ConsumerFindIDController {

    @Autowired
    UserConsumerService consumerService;

    @GetMapping("/findById")
    public String findId(Integer id) {
        return consumerService.isID(id);
    }
}

原因

String isID(@RequestParam("id") Integer id),实际上是交由Feign组装POST请求 http://user-provider/isConsumerId?id={1},此时@RequestParam("id")是强制要求的,否则报错。当然使用@PathVariable("id")也可,或者使用@RequestBody。总而言之,如果需要传参,必须要保证Feign能正确的获取到相应的参数,才能组装正确的Http请求。


扩展:

若需动态传参,可将参数放入map中,如下所示:

user_api:

@RequestMapping("/user")
public interface RegisterApi {

    @GetMapping("/map")
    Map<String, Object> getMap(Map<String, Object> map);
}

user-provider:

@RestController
public class UserController implements RegisterApi {

    @Override
    public Map<String, Object> getMap(@RequestParam Map<String, Object> map) {
        System.out.println(map);
        return Collections.singletonMap(map.get("name").toString(), Integer.parseInt(map.get("age").toString()));
    }
}

user-consumer:

package com.yiming.user_consumer.service;

@FeignClient(name = "user-provider")
public interface UserConsumerService extends RegisterApi {

    @GetMapping("/map")
    Map<String, Object> getMap(@RequestParam Map<String, Object> map);
}
package com.yiming.user_consumer.controller;

@RestController
public class ConsumerFindIDController {

    @Autowired
    UserConsumerService consumerService;

    @GetMapping("/getMap")
    public Map<String, Object> getMap(@RequestParam Map<String, Object> map) {
        return consumerService.getMap(map);
    }
}

后记

稍等,还有一个坑:

由于OpenFeign默认所有带参数的请求都是POST请求,当发送GET请求时,Feign会将其转为POST请求。

如果想使用指定的提交方式,需要替换底层实现,替换方法如下:

  1. 导入依赖
<dependency>
    <groupId>io.github.openfeign</groupId>
    <artifactId>feign-httpclient</artifactId>
</dependency>
  1. 在api中指明提交方式
@RequestMapping(value = "/xxx", method = RequestMethod.GET)
(0)

相关推荐