zoukankan      html  css  js  c++  java
  • spring cloud——feign为GET请求时的对象参数传递

    一、问题重现


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

  • 相关阅读:
    WikiPedia技术架构学习笔记
    MySQL 架构设计篇 (十二) 可扩展设计的基本原则
    php前端控制器二
    php前端控制器三
    构建可扩展的WEB站点读书笔记
    发布脚本开发框架代码
    改良dbgrideh的文字过滤
    cxgrid在当前View插入记录
    生成不重复单据编号
    cxgrid按条件计算合计值
  • 原文地址:https://www.cnblogs.com/qq503665965/p/9865881.html
Copyright © 2011-2022 走看看