解决HttpURLConnection GET封装请求体
广义上get没有请求体,但在实际开发中可能会遇到get请求封装请求体的需求。这里我们要解决的是使用 HttpURLConnection 封装get请求body时,源码中get请求封装请求体会转为post请求。
源码如下:
//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"; //注意这里
}
if ("TRACE".equals(this.method) && "http".equals(this.url.getProtocol())) {
throw new ProtocolException("HTTP method TRACE doesn't support output");
} else if (this.inputStream != null) {
throw new ProtocolException("Cannot write output after reading input.");
} else {
if (!this.checkReuseConnection()) {
this.connect();
}
boolean var1 = false;
String var8 = this.requests.findValue("Expect");
if ("100-Continue".equalsIgnoreCase(var8) && this.streaming()) {
this.http.setIgnoreContinue(false);
var1 = true;
}
if (this.streaming() && this.strOutputStream == null) {
this.writeRequests();
}
if (var1) {
this.expect100Continue();
}
this.ps = (PrintStream)this.http.getOutputStream();
if (this.streaming()) {
if (this.strOutputStream == null) {
if (this.chunkLength != -1) {
this.strOutputStream = new HttpURLConnection.StreamingOutputStream(new ChunkedOutputStream(this.ps, this.chunkLength), -1L);
} else {
long var3 = 0L;
if (this.fixedContentLengthLong != -1L) {
var3 = this.fixedContentLengthLong;
} else if (this.fixedContentLength != -1) {
var3 = (long)this.fixedContentLength;
}
this.strOutputStream = new HttpURLConnection.StreamingOutputStream(this.ps, var3);
}
}
return this.strOutputStream;
} else {
if (this.poster == null) {
this.poster = new PosterOutputStream();
}
return this.poster;
}
}
}
} catch (RuntimeException var5) {
this.disconnectInternal();
throw var5;
} catch (ProtocolException var6) {
int var2 = this.responseCode;
this.disconnectInternal();
this.responseCode = var2;
throw var6;
} catch (IOException var7) {
this.disconnectInternal();
throw var7;
}
}
解决方法:通过反射将对象的参数 requestMethod 重置为get。
//将post请求转为get请求(通过反射)
// 解决在Rest请求中,GET请求附带body导致GET请求被强制转换为POST
// 在sun.net.www.protocol.http.HttpURLConnection.getOutputStream0方法中,会把GET方法
// 修改为POST,而且无法调用setRequestMethod方法修改,因此此处使用反射强制修改字段属性值
// https://stackoverflow.com/questions/978061/http-get-with-request-body/983458
List<Field> fields = new ArrayList<>();
Class tempClass = con.getClass();
while (tempClass != null) {//当父类为null的时候说明到达了最上层的父类(Object类).
fields.addAll(Arrays.asList(tempClass.getDeclaredFields()));
tempClass = tempClass.getSuperclass(); //得到父类,然后赋给自己
}
for (Field field : fields) {
if ("method".equals(field.getName())) {
field.setAccessible(true);
field.set(con, "GET");
}
}
//写入请求参数
writer.write(params.toString());
writer.flush();
writer.close();
完整请求方法:
/**
* 发送请求
*/
public JSONObject sendRequest(String urlParam, String requestType, String username, String password, JSONObject params) throws IOException {
BufferedReader buffer = null;
InputStream inputStream = null;
try {
URL url = new URL(urlParam);
//得到连接对象
HttpURLConnection con = (HttpURLConnection) url.openConnection();
//设置请求类型
con.setRequestMethod(requestType);
//header内的的参数在这里set
//设置请求需要返回的数据类型和字符集类型
con.setRequestProperty("Content-Type", "application/json");
//设置Basic认证
// String auth = "pi_csbuyt:*****";
// 后期使用
String auth = username + ":" + password;
byte[] rel = Base64.encodeBase64(auth.getBytes());
String res = new String(rel);
con.setRequestProperty("Authorization", "Basic " + res);
baseBean.writeLog("auth == " + auth);
//设置客户端与服务连接类型
// con.addRequestProperty("Connection", "Keep-Alive");
//允许写出、读入
con.setDoOutput(true);
con.setDoInput(true);
//post请求不使用缓存
// con.setUseCaches(false);
//设置body内的参数,put到JSONObject中
// JSONObject param = new JSONObject();
// param.put("REQ_DATA", "");
// baseBean.writeLog(param);
// 建立实际的连接
con.connect();
OutputStreamWriter writer = new OutputStreamWriter(con.getOutputStream(), "UTF-8");
//将post请求转为get请求(通过反射)
// 解决在Rest请求中,GET请求附带body导致GET请求被强制转换为POST
// 在sun.net.www.protocol.http.HttpURLConnection.getOutputStream0方法中,会把GET方法
// 修改为POST,而且无法调用setRequestMethod方法修改,因此此处使用反射强制修改字段属性值
// https://stackoverflow.com/questions/978061/http-get-with-request-body/983458
List<Field> fields = new ArrayList<>();
Class tempClass = con.getClass();
while (tempClass != null) {//当父类为null的时候说明到达了最上层的父类(Object类).
fields.addAll(Arrays.asList(tempClass.getDeclaredFields()));
tempClass = tempClass.getSuperclass(); //得到父类,然后赋给自己
}
for (Field field : fields) {
if ("method".equals(field.getName())) {
field.setAccessible(true);
field.set(con, "GET");
}
}
//写入请求参数
writer.write(params.toString());
writer.flush();
writer.close();
if (con.getResponseCode() == HttpURLConnection.HTTP_OK) {
//得到响应流
inputStream = con.getInputStream();
//将响应流转换成字符串
StringBuffer resultBuffer = new StringBuffer();
String line;
buffer = new BufferedReader(new InputStreamReader(inputStream, "UTF-8"));
while ((line = buffer.readLine()) != null) {
resultBuffer.append(line);
}
//判断响应参数是否为空
if (!(resultBuffer.length() > 0)) {
return null;
}
return JSONObject.parseObject(resultBuffer.toString());
} else {
return null;
}
} catch (Exception e) {
e.printStackTrace();
return null;
} finally {
if (buffer != null && inputStream != null) {
inputStream.close();
buffer.close();
}
}
}