• 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

  • 相关阅读:
    小小知识点(二)——如何修改win10 的C盘中用户下的文件夹名称
    Day15 Python基础之logging模块(十三)
    Day14 Python基础之os/sys/hashlib模块(十二)
    Day13 Python基础之time/datetime/random模块一(十一)
    Day12 Python基础之生成器、迭代器(高级函数)(十)
    Day11 Python基础之装饰器(高级函数)(九)
    火狐HACK
    javascript操作cookie
    <!DOCTYPE>标签的定义与用法
    前端性能优化
  • 原文地址:https://www.cnblogs.com/qq503665965/p/9865881.html
Copyright © 2020-2023  润新知