spring cloud——feign為GET請求時的物件引數傳遞
阿新 • • 發佈:2018-12-18
一、問題重現
樓主在使用feign進行宣告式服務呼叫的時候發現,當GET請求為多引數時,為方便改用DTO物件進行引數傳遞。但是,在介面呼叫時feign會丟擲一個405的請求方式錯誤:
{"timestamp":1540713334390,"status":405,"error":"Method Not Allowed", "exception":"org.springframework.web.HttpRequestMethodNotSupportedException","message":"Request method 'POST' not supported","path":"/role/get"}
API介面層程式碼如下:
@RequestMapping(value = "/role") public interface RoleService { @GetMapping(value = "/get") Result<RuleInfoVO> getRoleInfo(RoleInfoRequest request); }
服務端實現:
@Slf4j @RestController public class RoleInfoResource implements RoleService { @Override public Result<RuleInfoVO> getRoleInfo(RoleInfoRequest request) { log.info("params of getRoleInfo:{}",request); return new Result<>(); } }
feign客戶端呼叫:
@Slf4j @RestController @RequestMapping(value = "/role") public class RoleController { @Autowired private RoleInfoApi roleInfoApi; @GetMapping(value = "/get") public Result<RuleInfoVO> getRuleInfo(RoleInfoRequest request){ log.info("params of getRuleInfo:{}",request); Result<RuleInfoVO> result = this.roleInfoApi.getRoleInfo(request); log.info("result of getRuleInfo:{}",result); return result; } }
檢查feign呼叫方式與服務端所宣告的方式一致,但是為什麼為丟擲405異常?帶著該疑問稍微跟了一下原始碼,發現feign預設的遠端呼叫使用的是jdk底層的HttpURLConnection,這在feign-core包下的Client介面中的convertAndSend方法可看到:
if (request.body() != null) { if (contentLength != null) { connection.setFixedLengthStreamingMode(contentLength); } else { connection.setChunkedStreamingMode(8196); } connection.setDoOutput(true); OutputStream out = connection.getOutputStream(); if (gzipEncodedRequest) { out = new GZIPOutputStream(out); } else if (deflateEncodedRequest) { out = new DeflaterOutputStream(out); } try { out.write(request.body()); } finally { try { out.close(); } catch (IOException suppressed) { // NOPMD } } }
該段程式碼片段會判斷requestBody是否為空,我們知道GET請求預設是不會有requestBody的,因此該段程式碼會執行到HttpURLConnection中的 private synchronized OutputStream getOutputStream0() throws IOException; 方法:
1 if (this.method.equals("GET")) { 2 this.method = "POST"; 3 }
最關鍵的程式碼片段已顯示當請求方式為GET請求,會將該GET請求修改為POST請求,這也就是4返回05狀態的根本原因。
二、解決方案
幸運的是,feign為我們提供了相應的配置解決方案。我們只需將feign底層的遠端呼叫由HttpURLConnection修改為其他遠端呼叫方式即可。而且基本不需要修改太多的程式碼,只需再依賴中加入feign-httpclient包的依賴,並在@RequestMapping註解中加入consumes的屬性即可:
1 compile 'io.github.openfeign:feign-httpclient:9.5.1'
樓主用的gradle,使用maven的大佬請自行更改為maven的配置方式。
增加@GetMapping註解的consumes屬性,使用@RequestMapping的大佬也一樣:
1 @RequestMapping(value = "/role") 2 public interface RoleService { 3 4 5 @GetMapping(value = "/get",consumes = MediaType.APPLICATION_JSON_UTF8_VALUE) 6 Result<RuleInfoVO> getRoleInfo(@RequestBody RoleInfoRequest request); 7 8 }
大功告成: