填坑系列之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-api、user-provider、user-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请求。
如果想使用指定的提交方式,需要替换底层实现,替换方法如下:
- 导入依赖
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-httpclient</artifactId>
</dependency>
- 在api中指明提交方式
@RequestMapping(value = "/xxx", method = RequestMethod.GET)