1.背景描述

上游服务提供的方法非常比较奇特,查询接口,定义的GET方法,参数通过request body传递的,在使用Feign Client封装GET方法调用时,会遇到一个报错,“405 Method Not Allowed”。通过查询,知道这个错误原因是HTTP调用方法错误,比如:定义的API是GET方法,通过POST方法(非GET方法)调用,就会返回这个错误。

@RequestLine("GET /api/user/get/")

Object getUser(@HeaderMap Map headers, UserRequest request);

2.原因分析

奇怪代码明明写得是使用GET方法啊,进一步查资料,得知原因是Feign client框架本身有一个坑:Feign client框架,默认情况下使用的是HttpURLConnection完成实际的http请求调用,但是HttpURLConnection本身不支持GET方法调用时带有body,带有body的调用方法,只能是POST方法。

// sun.net.www.protocol.http.HttpURLConnection

private synchronized OutputStream getOutputStream0() throws IOException {

try {

if(!this.doOutput) {

throw new ProtocolException("cannot write to a URLConnection if doOutput=false - call setDoOutput(true)");

} else {

if(this.method.equals("GET")) {

this.method = "POST";

}

// ........

}

}

}

HTTP GET方法调用,到底支不支持带有body呢,HTTP协议是支持的,没有禁止,但是呢,不建议这么做,不是一个良好的习惯,因为有些浏览器

啥的可能不支持,这个时候,你写的方法就尴尬了。

Stackoverflow解释如下:

In other words, any HTTP request message is allowed to contain a message body, and thus must parse messages with that in mind. Server semantics for GET, however,

are restricted such that a body, if any, has no semantic meaning to the request. The requirements on parsing are separate from the requirements on method semantics.

So, yes, you can send a body with GET, and no, it is never useful to do so.

This is part of the layered design of HTTP/1.1 that will become clear again once the spec is partitioned (work in progress).

3.解决方法

方案一:

网上可以很容易搜索到这个解决方法,相关博客非常多,直接copy的情况,太严重了。但是实际验证,没有生效,具体原因待排查。

1.yml配置文件中,加入feign的配置项:feign.httpclient.enabled: true

2.增加如下maven依赖。

<dependency>

<groupId>org.apache.httpcomponents</groupId>

<artifactId>httpclient</artifactId>

<version>4.5.3</version>

</dependency>

<dependency>

<groupId>com.netflix.feign</groupId>

<artifactId>feign-httpclient</artifactId>

<version>8.17.0</version>

</dependency>

方案原理:HttpURLConnection不支持GET方法带有body的调用,ApacheHttpClient支持GET方法带有body的调用。这个配置,就是将feign client默认使用的HTTP调用方式,从HttpURLConnection切换到ApacheHttpClient方式。

方法不生效原因:

1.可能HTTP调用方式没有切换成功,也就是配置没有生效。(确定是这个原因,因为我使用的Feign方式:Feign.builder()默认生成的就是HttpURLConnection方式的http请求调用。相关源码如下:

// feign.Feign.Builder

private Client client = new Client.Default(null, null);

// feign.Client.Default

final HttpURLConnection connection = (HttpURLConnection) new URL(request.url()).openConnection();

因此相关配置修改是不生效的,需要重新生成一个client才行,比如:ApacheHttpClient

2.可能ApacheHttpClient本身也不支持GET方法带有body的请求。(因为直接使用ApacheHttpClient,发现没有支持GET方法带有body的调用方式)

补充:(待验证,证明)

feign分别尝试了Java原生URLConnection,OkHttp,ApacheHttpClient三种方式:

1.URLConnection 报405错误,说明http方法不对,但是feign配置是GET方法,查feign的日志也是用的GET方法。后来发现原因是URLConnection在的

原因:对于有request body的GET方法,自动改为POST方法了。

2.OkHttp 直接报错:method GET must not have a request body.

3.ApacheHttpClient完美支持。

方案二:

使用AsyncHttpClient,因为AsyncHttpClient支持GET方法带有Body的调用。

网上也可以很容易搜索到这个解决方法,感觉都是复制粘贴的,没有经过验证和实证,内容完全一样,但是都缺少最关键的信息,没有给出需要引用的jar包,怎么使用测试呢?而需要引用的jar包还不好找到,实在是大坑。

1.引入maven依赖

<dependency>

<groupId>org.asynchttpclient</groupId>

<artifactId>async-http-client</artifactId>

<version>2.2.0</version>

</dependency>

2.解决方法demo

public static String get(String url, String bodyData, Map<String, String> headers) throws Exception {

// 构建请求

BoundRequestBuilder requestBuilder = asyncHttpClient.prepareGet(url).setBody(bodyData);

headers.forEach(requestBuilder::addHeader);

List<Response> list = new ArrayList<>();

requestBuilder.execute()

.toCompletableFuture()

.thenAccept(list::add)

.join();

if (list.isEmpty()) {

return null;

}

Response response = list.get(0);

if (response.getStatusCode() != 200) {

return null;

}

return response.getResponseBody();

}

备注1:

1.方法可以返回map,增加:new ObjectMapper().readValue(response.getResponseBody(), Map.class);

2.方法本身必须返回json 对象的string才行,不能是非json对象的string,否则解析异常。

备注2:

1.没有body的get方法,去掉.setBody(bodyData)即可。

2.没有header的get方法调用,去掉headers.forEach(requestBuilder::addHeader);即可。

4.总结

网上资源很多、很丰富,各种问题解决方案很多,但是也存在很多缺陷,不去验证、实践,根本不知道里面有问题,因此不要随便copy别人的博客,往往copy的博客本身就存在潜在的问题,copy之前,请试验一下,证明方法是正确的,减少给需要同学的误导。

5.参考资料

1.https://yanbin.blog/why-http-get-cannot-sent-data-with-reuqest-body/(http://www.programmersought.com/article/752346261/)

2.https://blog.csdn.net/f641385712/article/details/82431502

3.https://segmentfault.com/q/1010000011958034

4.https://stackoverflow.com/questions/978061/http-get-with-request-body

update by 20200617:

提供另一种方式:

使用Apache的HttpClient,调用Post方法; 
关键点: 重写Post方法的参数HttpEntity;

import org.apache.http.client.methods.HttpEntityEnclosingRequestBase;public class HttpGetWithEntity extends HttpEntityEnclosingRequestBase {public final static String METHOD_NAME = "GET";@Overridepublic String getMethod() {return METHOD_NAME;}
}=================================================调用HttpClient
HttpGetWithEntity e = new HttpGetWithEntity();
...
e.setEntity(yourEntity);
...
response = httpclient.execute(e);

解决HTTP GET方法调用带有body问题相关推荐

  1. httpcliet发送body体_解决HTTP GET方法调用带有body问题

    1.背景描述 上游服务提供的方法非常比较奇特,查询接口,定义的GET方法,参数通过request body传递的,在使用Feign Client封装GET方法调用时,会遇到一个报错,"405 ...

  2. 使用JavaSymbolSolver解决Java代码中的方法调用

    为什么创建java-symbol-solver? 几年前,我开始使用JavaParser ,然后开始做出贡献. 不久之后,我意识到我们想对Java代码执行的许多操作不能仅通过使用解析器生成的抽象语法树 ...

  3. 在同一个类中,一个方法调用另外一个有注解(比如@Async,@Transational)的方法,注解失效的原因和解决方法

    在同一个类中,一个方法调用另外一个有注解(比如@Async,@Transational)的方法,注解失效的原因和解决方法 参考文章: (1)在同一个类中,一个方法调用另外一个有注解(比如@Async, ...

  4. 解决Spring事务注解@Transactional在类内部方法调用不生效的问题

    问题现象: package cn.sw.study.web.service.impl; import cn.sw.study.web.dao.UserMapper; import cn.sw.stud ...

  5. springboot 调用方法事物_SpringBoot 内部方法调用,事务不起作用的原因及解决办法...

    在做业务开发时,遇到了一个事务不起作用的问题.大概流程是这样的,方法内部的定时任务调用了一个带事务的方法,失败后事务没有回滚.查阅资料后,问题得到解决,记录下来分享给大家. 场景 我在这里模拟一个场景 ...

  6. JVM学习笔记之-运行时数据区概述及线程概述,程序计数器(PC寄存器),虚拟机栈(栈,局部变量表,操作数栈,动态连接,方法调用,方法返回地址等),本地方法接口,本地方法栈

    运行时数据区概述及线程概述 内存是非常重要的系统资源,是硬盘和CPU的中间仓库及桥梁,承载着操作系统和应用程序的实时运行.JVM内存布局规定了Java在运行过程中内存申请.分配.管理的策略,保证了JV ...

  7. java中调用数组参数_java中如何调用带有数组类型参数的存储过程

    java中如何调用带有数组类型参数的存储过程 关注:95  答案:3  mip版 解决时间 2021-01-28 00:39 提问者万丈深渊 2021-01-27 14:00 不知道java中java ...

  8. JVM:方法调用之动态类型语言支持。

    Java虚拟机的字节码指令集的数量从Sun公司的第一款Java虚拟机问世至JDK 7来临之前的十余年时间里,一致没有发生任何变化.随着JDK 7的发布,字节码指令集终于迎来了第一位新成员--invok ...

  9. Java高级——方法调用

    方法调用 概述 解析 分派 静态分派(重载) 动态分派(重写) 动态分派实现 单分派和多分派 动态类型语言 invokedynamic和java.lang.invoke java.lang.invok ...

最新文章

  1. java 程序片段_20个非常有用的Java程序片段
  2. 计算数组的逆序对个数
  3. Electric device abnormal detection based on IoT and knowledge graph-学习笔记
  4. rxjs里tap操作符的使用单步调试
  5. js判断数组里是否有重复元素的方法
  6. java实现语音聊天_java 语音聊天核心代码
  7. 南海区行政审批管理系统接口规范v0.3(规划) 2.业务申报API 2.1.businessApply【业务申报】...
  8. Docker的使用(未完待续)
  9. 八皇后问题程序及注解
  10. PHP 实现文件上传 php配置
  11. paip.C#.NET多线程访问 toolStripStatusLabel VC421
  12. EDA技术实用教程 | 复习四 | 标识符、关键字和规范的书写格式
  13. 启明星Zynq7010制作PYNQ V2.4镜像过程记录
  14. 2.struts2 Actions动作 - ActionSupport
  15. 计算机最新一区sci,人工智能容易发的SCI期刊_2019中科院jcr期刊分区_2019中科院最新分区...
  16. 数字图像处理学习路线
  17. matlab符号函数绘图法_matlab中怎么创建符号函数?
  18. 文件上传2-搭建uploads靶场
  19. iOS 15提示“此App的开发者需要更新APP以在此IOS版本上正常工作”
  20. python cv2改变图片亮度

热门文章

  1. 云呐|固定资产盘点中,支持多种盘点方式(资产清查盘点)
  2. for in遍历对象时break,continue,return尝试
  3. 3、HTTP请求头与响应头
  4. 190亿像素的全世界最长全景照片
  5. 软件项目管理MOOC(北邮)——第七章测试答案
  6. 如何用python处理pdf_用Python处理PDF
  7. rust 局域网联机_Steam上有哪些值得推荐的可以多人局域网联机的游戏?
  8. matlab期末数字图像处理小工具
  9. 如何在群里引流?如何通过别人的群引流?建一个群怎么快速裂变引流
  10. 华硕EeePC: 没有光驱重装系统(用U盘作为电脑启动盘)