前提
使用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)