在调试Fate平台时,我遇到了一个奇葩的接口类型。该接口为Get方式,入参是一个json类型在body中传递。我非常费解使用body中传参的话为什么不用POST请求而使用了GET请求?
在本篇文章中加入了自己对这个问题的一点理解,以及通过java请求这个接口的完整可使用案例。(包括返回string类型和文件流类型)
一、接口调用方法
当我看到这个接口的时候,我是有点懵的。Get请求为什么要采用这样的参数传递方式,一般来说GET请求就直接将参数拼接在URL中了。
二、传参方式的讨论
GET和POST请求和其常见的传参方式
我们知道GET和POST请求是HTTP协议中的两种最常用的请求方式,它们在处理参数和数据传输方面差距还是比较大的。
- 参数传递方式:GET请求的参数直接附加在URL的末尾,而POST请求的参数则包含在请求体中。
这意味着GET请求的参数是明文传输的,因此在请求过程中可能会被记录或泄露。而POST请求的参数则是加密的,相对更安全。
- 参数的数据类型:GET请求的参数只能发送简单的字符串,而POST请求可以发送复杂的数据类型,如表单数据、JSON数据等。
这使得POST请求相比GET请求在传递复杂数据时更为方便和灵活。
-
请求的语义:GET请求通常用于获取或检索数据,而POST请求则用于提交数据或执行某些操作。
-
缓存机制:GET请求默认开启浏览器缓存机制,因为它的主要目的是获取数据,而POST请求则默认禁用缓存机制,因为它可能对服务器上的资源进行修改。
-
参数长度限制:由于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)