Java中HTTP GET方法调用带有body的问题解决

 更新时间:2024年02月02日 08:50:00   作者:wy_df18  
这篇文章主要为大家详细介绍了Java如何解决HTTP GET方法调用带有body的问题,文中的示例代码讲解详细,感兴趣的小伙伴可以参考一下

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之前,请试验一下,证明方法是正确的,减少给需要同学的误导。

到此这篇关于Java中HTTP GET方法调用带有body的问题解决的文章就介绍到这了,更多相关Java HTTP GET方法内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • java -jar启动原理详解(附实操验证和注意事项)

    java -jar启动原理详解(附实操验证和注意事项)

    java -jar是Java中运行可执行JAR包(Executable JAR)的核心命令,其底层依托JVM类加载机制、JAR包规范和程序入口约定实现,这篇文章主要介绍了java -jar启动原理的相关资料,需要的朋友可以参考下
    2026-02-02
  • Maven之分析剔除无用的jar引用问题

    Maven之分析剔除无用的jar引用问题

    这篇文章主要介绍了Maven之分析剔除无用的jar引用问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2023-09-09
  • java stream中Collectors的用法实例精讲

    java stream中Collectors的用法实例精讲

    这篇文章主要为大家介绍了java stream中Collectors的用法实例精讲,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-09-09
  • mybaties plus实体类设置typeHandler不生效的解决

    mybaties plus实体类设置typeHandler不生效的解决

    这篇文章主要介绍了mybaties plus实体类设置typeHandler不生效的解决方案,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-08-08
  • 一文详解如何使用Java获取PDF页面信息

    一文详解如何使用Java获取PDF页面信息

    了解 PDF 页面属性是我们在处理文档、内容提取、打印设置或页面重组等任务时不可或缺的一环,下面我们就来看看如何使用Java语言获取这些信息吧
    2025-07-07
  • 使用 Apache POI 在 Java 中写入 Excel 文件的方法

    使用 Apache POI 在 Java 中写入 Excel

    这篇文章详细介绍了如何使用ApachePOI在Java中编写Excel文件的技巧,包括创建工作簿、工作表、行和单元格,以及如何处理不同版本的Excel文件,通过详细的步骤和代码示例,读者可以快速掌握ApachePOI的基本使用方法,感兴趣的朋友一起看看吧
    2025-02-02
  • java实现大文件导出的实现与优化

    java实现大文件导出的实现与优化

    这篇文章主要为大家详细介绍了java实现大文件导出的实现与优化的相关资料,文中的示例代码讲解详细,对我们深入了解java有一定的帮助,感兴趣的小伙伴可以了解下
    2023-11-11
  • SpringBoot集成MyBatis中SQL拦截器的实战指南

    SpringBoot集成MyBatis中SQL拦截器的实战指南

    这篇文章主要为大家详细介绍了SpringBoot集成MyBatis中SQL拦截器的相关知识,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下
    2025-07-07
  • Java中List根据条件删除元素的几种方式

    Java中List根据条件删除元素的几种方式

    java List删除指定元素有四种方法,分别是普通for循环,增强for循环,CopyOnWriteArrayList以及原生的Iterator迭代器循环来删除list中指定的某个元素,非常的简单,并通过代码示例讲解的非常详细,需要的朋友可以参考下
    2024-04-04
  • Java二叉搜索树遍历操作详解【前序、中序、后序、层次、广度优先遍历】

    Java二叉搜索树遍历操作详解【前序、中序、后序、层次、广度优先遍历】

    这篇文章主要介绍了Java二叉搜索树遍历操作,结合实例形式详细分析了Java二叉搜索树前序、中序、后序、层次、广度优先遍历等相关原理与操作技巧,需要的朋友可以参考下
    2020-03-03

最新评论