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依赖。

org.apache.httpcomponents

httpclient

4.5.3

com.netflix.feign

feign-httpclient

8.17.0

方案原理: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依赖

org.asynchttpclient

async-http-client

2.2.0

2.解决方法demo

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

// 构建请求

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

headers.forEach(requestBuilder::addHeader);

List 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.参考资料

httpcliet发送body体_解决HTTP GET方法调用带有body问题相关推荐

  1. 解决HTTP GET方法调用带有body问题

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

  2. axios创建实例对象发送ajax请求_解决一个网页请求多个服务器场景---axios工作笔记009

    然后我们再去看看,我们利用 axios去创建实例对象来发送ajax请求 可以看到上面我们创建了一个duanzi的axios对象. 然后我们在这个duanzi的axios对象中,指定默认的baseURL ...

  3. linux socket 结构定义 send,Linux下Socket编程中用send发送结构体

    Linux网络通信 Linux下多客户端聊天软件 最近在开发一个Linux下的聊天软件,好久没有做C语言的开发了,感觉到很多东西已经生疏了,这下又碰到用Socket传递结构体的问题,google了一下 ...

  4. c语言send发送结构体,Socket编程中用send发送结构体

    Socket编程中用send发送结构体 原创 2010年04月28日 19:17:00 标签:socket /编程 /struct /google /string /input 11868 最近在开发 ...

  5. nginx请求转发被拒绝_解决nginx反向代理proxy不能转发header报头

    做了一个德国高防plesk卖虚拟主机,奈何地理位置太过于遥远,控制台使用上速度难以接受.用户站点可以使用cloudflare等等的加速手段,控制台能否也这么干呢?理论是完全可以的,那么时间上手看吧.安 ...

  6. Linux下Socket编程中用send发送结构体

    转自:http://tech.ddvip.com/2008-10/122543769687623.html Linux网络通信 Linux下多客户端聊天软件 Linux程序设计 Linux socke ...

  7. ashx文件与ajax,ashx文件猎取$.ajax()方法发送的数据_

    <ashx文件猎取$.ajax()方法发送的数据_>由会员分享,可在线阅读,更多相关<ashx文件猎取$.ajax()方法发送的数据_(7页珍藏版)>请在人人文库网上搜索. 1 ...

  8. c语言发送结构体 文件

    Linux C Socket编程发送结构体.文件详解及实例 利用Socket发送文件.结构体.数字等,是在Socket编程中经常需要用到的.由于Socket只能发送字符串,所以可以使用发送字符串的方式 ...

  9. python3发送https请求_关于python 3.x:如何在不引起python3的SSL证书错误的情况下将POST请求发送到https...

    事情是这样的,我一直在尝试从python内部将POST请求发送到LOGIN到我的大学wifi页面,但是却收到SSL证书错误. POST请求在chrome的POSTMAN扩展中可以正常工作. 这是我使用 ...

最新文章

  1. Nginx配置https,反向代理多实例tomcat的操作记录
  2. etcd — 安装部署
  3. [Music]A Place Nearby
  4. usleep延时0.毫秒_LabVIEW从0到1系列视频培训_第4讲全集_操作例程说明
  5. vue里碰到 $refs 的问题
  6. [蓝桥杯2016初赛]方格填数
  7. 浙大计算机基础知识题1,浙大作业1计算机基础知识题.docx
  8. MiniGui移植详解1【转】
  9. 区块链爆史诗级漏洞,可完全控制虚拟货币交易!
  10. Python pandas使用
  11. 微信购物商城网站定制需要多少钱?电商网站建设开发方案(一)
  12. PreparedStatement enum
  13. m2增长率曲线_中国m2历年数据曲线图_中国m2历年数据
  14. 按键消抖(并联0.1uf电容)
  15. YIT-CTF—社工类
  16. 关于 Unicode 每个程序员应该知道的 5 件事
  17. 邮件个性签名html,iphone发邮件添加个性签名方法
  18. 关于微信小程序--授权弹窗
  19. matlab怎么根据图像求职,图像处理求职简历模板
  20. mpvue+mpvueWeUI搭建小程序

热门文章

  1. 【报告分享】中国城市人工智能发展指数报告.pdf(附下载链接)
  2. HR面必知黑话!错过后悔!
  3. 【广告技术】揭秘!腾讯广告是如何有效划分用户群体的
  4. chartxy 柱状图_关于Chart柱状图的使用,有问题
  5. 游戏王计算机兽,游戏王星杯卡——迅猛龙,再生圣经,入侵蠕虫,鼹鼠,幽世之血樱...
  6. 455 periodic strings
  7. 网站开发中敏感信息加密
  8. servlet中filter的的异步问题以及JSP与servlet配合时filter的注意
  9. win定时关机_电脑定时关机,你造吗?
  10. python selenium 如何选中iframe中的元素?