java中有很多可以用户发送http请求的工具,而我个人喜欢用更底层一点的,所以就选择了这个java自带的http请求工具。
底层的优点就是可以自定义发送的编码,头部等,很随意。
HttpURLConnection 请求代码
下面是我自己写的发送请求的代码,专门用于发送json格式的请求:
private void httpPost(String body, String httpUrl) {
try {
URL url = new URL(httpUrl); // url中用的是http:// 那么返回的就是HttpURLConnection
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
// 配置
connection.setRequestMethod("POST");
connection.setDoInput(true);
connection.setDoOutput(true);
connection.setUseCaches(false);
connection.setRequestProperty("Content-Type", "application/json");
// 连接
connection.connect();
// 发送请求
connection.getOutputStream().write(body.getBytes(StandardCharsets.UTF_8));
connection.getOutputStream().close();
// 获取响应
int code = connection.getResponseCode();
int size = connection.getHeaderFieldInt("Content-Length", 1024);
byte[] bytes = new byte[size];
InputStream inputStream;
if (code == 200)
inputStream = connection.getInputStream();
else
inputStream = connection.getErrorStream();
int o = inputStream.read(bytes);
inputStream.close(); // 流关闭后连接自动断开
System.out.println("code = " + code);
System.out.println("size = " + o);
// 输出结果
System.out.println(new String(bytes, StandardCharsets.UTF_8));
} catch (IOException e) {
e.printStackTrace();
}
}
但是,上面的方法在某次请求中发生了一些问题:
- 没有获取到响应的 Content-Length。
- 获取到的数据被截断。
在没有获取到Content-Length
时,默认会使用1024
大小的bytes
,但是在read
的时候,只读取到了前十几个字符。
完善后的 HttpURLConnection 请求代码
经查阅,发现http响应并不是一定会有Content-Length
。当响应数据过大或是客户端选择保持链接(请求头部Connection: keep-alive
)的时候,服务端的响应就不会带有Content-Length
,而会带有响应头部:Transfer-Encoding: chunked
,表示body
将会分块传输,而read
一次只能读取一块数据,所以就会导致接收数据不全的情况。
在分块传输中,最后一块没有数据但有块的结构,表示数据传输完毕。
所以经过修改,就有了如下的请求代码:
这样数据丢失的情况就没有了。
private void httpPost(String body, String httpUrl) {
try {
URL url = new URL(httpUrl);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("POST");
connection.setDoInput(true);
connection.setDoOutput(true);
connection.setUseCaches(false);
connection.setRequestProperty("Content-Type", "application/json");
// 请求
connection.connect();
connection.getOutputStream().write(body.getBytes(StandardCharsets.UTF_8));
connection.getOutputStream().close();
// 响应
int code = connection.getResponseCode();
// + 1 是防止因为缓存被用完而进行无用的扩容。
int ct = connection.getHeaderFieldInt("Content-Length", 1023) + 1;
bytes = new byte[ct];
InputStream inputStream;
if (code == 200)
inputStream = connection.getInputStream();
else
inputStream = connection.getErrorStream();
int r, s = 0;
while (true)
{
r = inputStream.read(bytes, s, ct - s);
if (r == -1)
break;
else
s += r;
if (ct == s)
{
ct *= 2; // 缓存扩容
bytes = Arrays.copyOf(bytes, ct);
}
}
inputStream.close();
System.out.println("code = " + code);
System.out.println("size = " + s);
System.out.println(new String(bytes, 0, s, StandardCharsets.UTF_8));
} catch (IOException e) {
e.printStackTrace();
}
}