java 实现带RequestBody传Json参数的GET请求

在调试Fate平台时,我遇到了一个奇葩的接口类型。该接口为Get方式,入参是一个json类型在body中传递。我非常费解使用body中传参的话为什么不用POST请求而使用了GET请求?

在本篇文章中加入了自己对这个问题的一点理解,以及通过java请求这个接口的完整可使用案例。(包括返回string类型和文件流类型)

一、接口调用方法

当我看到这个接口的时候,我是有点懵的。Get请求为什么要采用这样的参数传递方式,一般来说GET请求就直接将参数拼接在URL中了

二、传参方式的讨论

GET和POST请求和其常见的传参方式

我们知道GET和POST请求是HTTP协议中的两种最常用的请求方式,它们在处理参数和数据传输方面差距还是比较大的。

  1. 参数传递方式:GET请求的参数直接附加在URL的末尾,而POST请求的参数则包含在请求体中。

这意味着GET请求的参数是明文传输的,因此在请求过程中可能会被记录或泄露。而POST请求的参数则是加密的,相对更安全。

  1. 参数的数据类型:GET请求的参数只能发送简单的字符串,而POST请求可以发送复杂的数据类型,如表单数据、JSON数据等。

这使得POST请求相比GET请求在传递复杂数据时更为方便和灵活。

  1. 请求的语义:GET请求通常用于获取或检索数据,而POST请求则用于提交数据或执行某些操作。

  2. 缓存机制:GET请求默认开启浏览器缓存机制,因为它的主要目的是获取数据,而POST请求则默认禁用缓存机制,因为它可能对服务器上的资源进行修改。

  3. 参数长度限制:由于GET请求的参数附加在URL上,因此参数长度受到URL长度的限制。而POST请求的参数放在请求体中,理论上没有长度限制,但实际上服务器和客户端可能会有最大长度限制。

3、4、5意味着根据不同的需求,选择合适的请求方式是很重要的。

其实GET带使用body中传参,从请求方面讲 符合了GET请求的语义(获取和检索数据)有能应用到混存机制。从参数方面讲有能突破长度和数据类型的限制,同时参数传递更安全。

GET使用请求体传参的合理性

对于GET请求通过body传参的情况可以说非常罕见,很多老java都没见过,也算是给我开了眼了。

根据以往的经验,我们约定俗成,GET传参通过URL拼接,POST传参通过body传输。于是有些HTTP库(如OkHttp)是不允许GET请求带有请求体的,默认通过post传请求体。

虽然官方不推荐这样做,但是,http(基于tcp的超文本传输协议)并没有规定Get请求不能加body。

所以这个请求自有其合理性。

三、java实现GET使用请求体传参请求的技术选型

在公司封装好的Http请求方法类中,我们采用的是OkHttp,很不幸的是他不支持GET使用请求体传参。

于是通过网络搜索,我们选定了两个方法。一个是AsyncHttpClient ,另一个是apache.http.client

参考文档

解决HTTP GET方法调用带有body问题 - 简书 (jianshu.com)

httpclient实现HttpGet请求传body的json参数的! - groby - 博客园 (cnblogs.com)

考虑到我们没有异步请求方面的需求,于是最后选定了apache.http.client

四、完整实现

1.pom引入关键依赖

xml 复制代码
<dependency>
  <groupId>org.apache.httpcomponents</groupId>
  <artifactId>httpclient</artifactId>
  <version>4.5.6</version>
</dependency>

<dependency>
  <groupId>org.apache.httpcomponents</groupId>
  <artifactId>httpcore</artifactId>
  <version>4.5.6</version>
</dependency>

2.定义HttpGet实体类

这个实体类主要供后续实现的方法类调用,可以单独作为一个类,也可以将其作为方法类的子类。

java 复制代码
import org.apache.http.client.methods.HttpEntityEnclosingRequestBase;

import java.net.URI;

public class HttpGetWithEntity extends HttpEntityEnclosingRequestBase {
    private final static String METHOD_NAME = "GET";

    public HttpGetWithEntity() {
        super();
    }

    public HttpGetWithEntity(final URI uri) {
        super();
        setURI(uri);
    }

    HttpGetWithEntity(final String uri) {
        super();
        setURI(URI.create(uri));
    }

    @Override
    public String getMethod() {
        return METHOD_NAME;
    }

}

3.实现方法类

这里实现了两个方法,都是通过GET请求传递请求体数据的(请求体数据已转换为为JsonString)。

区别在于一个是正常返回String类型Response。另一个是返回文件流并将文件流写入到文件中。

java 复制代码
import lombok.extern.slf4j.Slf4j;
import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;

@Slf4j
public class ApacheHttpUitls {
    public static String getResponseByJson(String url, String param, String encoding) throws Exception {
        String body = "";
        //创建httpclient对象
        CloseableHttpClient client = HttpClients.createDefault();
        HttpGetWithEntity httpGetWithEntity = new HttpGetWithEntity(url);
        HttpEntity httpEntity = new StringEntity(param, ContentType.APPLICATION_JSON);
        httpGetWithEntity.setEntity(httpEntity);
        //执行请求操作,并拿到结果
        CloseableHttpResponse response = client.execute(httpGetWithEntity);
        //获取结果实体
        HttpEntity entity = response.getEntity();
        if (entity != null) {
            //按指定编码转换结果实体为String类型
            body = EntityUtils.toString(entity, encoding);
        }
        //释放链接
        response.close();
        return body;
    }
    
    public static boolean getStreamGetResponse(String url, String requestBody, String outputFilePath) throws Exception {
        InputStream inputStream = null;
        OutputStream outputStream = null;
        CloseableHttpResponse response = null;
        try{

            //创建httpclient对象
            CloseableHttpClient client = HttpClients.createDefault();
            HttpGetWithEntity httpGetWithEntity = new HttpGetWithEntity(url);
            HttpEntity httpEntity = new StringEntity(requestBody, ContentType.APPLICATION_JSON);
            httpGetWithEntity.setEntity(httpEntity);
            //执行请求操作,并拿到结果
            response = client.execute(httpGetWithEntity);
            //获取结果实体
            HttpEntity entity = response.getEntity();
            log.info("requestUrl: {}, requestBody: {}", url, requestBody);
            if (entity != null) {

                inputStream = entity.getContent();
                Path path = Paths.get(outputFilePath);
                if (!Files.exists(path)) {
                    try {
                        Files.createFile(path);
                    } catch (IOException e) {
                        log.error("File create error ", e);
                        // 处理异常,例如输出错误消息或退出程序
                    }
                }
                outputStream = Files.newOutputStream(path);

                byte[] buffer = new byte[1024]; // 可以根据需要调整缓冲区大小
                int bytesRead;
                while ((bytesRead = inputStream.read(buffer)) != -1) {
                    outputStream.write(buffer, 0, bytesRead);
                }
                log.info("File downloaded and saved at " + outputFilePath);
            }else{
                log.error("Http get Entity is null");
                return false;
            }
            return true;
        } catch (Exception e){
            log.error("File download error ", e);
            return false;
        } finally {
            //释放链接
            if (response != null) response.close();
            if (inputStream != null) inputStream.close();
            if (outputStream != null) outputStream.close();
        }
    }


}

参考文章

解决HTTP GET方法调用带有body问题 - 简书 (jianshu.com)

Http Get支持RequestBody请求(Java)_51CTO博客_java http get

OKHttp和AsyncHttpClient(get请求带body)的使用_okhttpget带消息体请求-CSDN博客

httpclient实现HttpGet请求传body的json参数的! - groby - 博客园 (cnblogs.com)

相关推荐
Dola_Pan29 分钟前
Linux文件IO(二)-文件操作使用详解
java·linux·服务器
wang_book32 分钟前
Gitlab学习(007 gitlab项目操作)
java·运维·git·学习·spring·gitlab
蜗牛^^O^1 小时前
Docker和K8S
java·docker·kubernetes
从心归零2 小时前
sshj使用代理连接服务器
java·服务器·sshj
运维小白。。2 小时前
Nginx 反向代理
运维·服务器·nginx·http
IT毕设梦工厂3 小时前
计算机毕业设计选题推荐-在线拍卖系统-Java/Python项目实战
java·spring boot·python·django·毕业设计·源码·课程设计
城南云小白3 小时前
web基础+http协议+httpd详细配置
前端·网络协议·http
Ylucius4 小时前
动态语言? 静态语言? ------区别何在?java,js,c,c++,python分给是静态or动态语言?
java·c语言·javascript·c++·python·学习
凡人的AI工具箱4 小时前
AI教你学Python 第11天 : 局部变量与全局变量
开发语言·人工智能·后端·python
是店小二呀4 小时前
【C++】C++ STL探索:Priority Queue与仿函数的深入解析
开发语言·c++·后端