1. 程式人生 > >spring cloud——feign為GET請求時的對象參數傳遞

spring cloud——feign為GET請求時的對象參數傳遞

puts 但是 代碼片段 cor stc 請求 implement stat java

一、問題重現


樓主在使用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 }

大功告成:

技術分享圖片

源代碼請各位大佬移步:https://github.com/LJunChina/cloud-solution-staging

spring cloud——feign為GET請求時的對象參數傳遞