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)

相关推荐
IU宝几秒前
C/C++内存管理
java·c语言·c++
瓜牛_gn1 分钟前
依赖注入注解
java·后端·spring
hakesashou2 分钟前
Python中常用的函数介绍
java·网络·python
佚先森11 分钟前
2024ARM网络验证 支持一键云注入引流弹窗注册机 一键脱壳APP加固搭建程序源码及教程
java·html
Estar.Lee18 分钟前
时间操作[取当前北京时间]免费API接口教程
android·网络·后端·网络协议·tcp/ip
喜欢猪猪20 分钟前
Django:从入门到精通
后端·python·django
一个小坑货20 分钟前
Cargo Rust 的包管理器
开发语言·后端·rust
bluebonnet2724 分钟前
【Rust练习】22.HashMap
开发语言·后端·rust
古月居GYH25 分钟前
在C++上实现反射用法
java·开发语言·c++
uhakadotcom1 小时前
如何实现一个基于CLI终端的AI 聊天机器人?
后端