感謝大佬:https://blog.csdn.net/qq_40036754/article/details/102554755
一、前言
-
實習第二個月了,遇到了一個問題,需求使用Java 的 HttpURLConnection 來轉發請求。需求也給了示例,但是流程不明白,所以再網上查找了很長時間之后,特來總結一番。
-
3.3中,是關於這次的問題總結,建議大家看一下。
二、HttpURLConnection 介紹
JDK的java.net包中提供了訪問HTTP協議的基本功能的類:HttpURLConnection。
URLConnection是個抽象類,它有兩個直接子類分別是HttpURLConnection和JarURLConnection。另外一個重要的類是URL,通常URL可以通過傳給構造器一個String類型的參數來生成一個指向特定地址的URL實例。
HttpURLConnection是Java的標准類,它繼承自URLConnection,可用於向指定網站發送GET請求、POST請求。它在URLConnection的基礎上提供了如下便捷的方法:
int getResponseCode(); // 獲取服務器的響應代碼。
String getResponseMessage(); // 獲取服務器的響應消息。
String getResponseMethod(); // 獲取發送請求的方法。
void setRequestMethod(String method); // 設置發送請求的方法。
每個 HttpURLConnection 實例都可用於生成單個請求,但是其他實例可以透明地共享連接到 HTTP 服務器的基礎網絡。請求后在 HttpURLConnection 的 InputStream 或 OutputStream 上調用 close() 方法可以釋放與此實例關聯的網絡資源,但對共享的持久連接沒有任何影響。如果在調用 disconnect() 時持久連接空閑,則可能關閉基礎套接字。
三、GET、POST請求
1、GET請求
package com.feng.demo;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
/** * GET請求示例 * * @author 安辰 * */
public class GetDemo {
public void doGet(String[] args) {
HttpURLConnection httpURLConnection = null;
try {
// 1. 得到訪問地址的URL
URL url = new URL(
"http://localhost:8080/Servlet/do_login.do?username=test&password=123456");
// 2. 得到網絡訪問對象java.net.HttpURLConnection
httpURLConnection = (HttpURLConnection) url.openConnection();
/* 3. 設置請求參數(過期時間,輸入、輸出流、訪問方式),以流的形式進行連接 */
// 設置是否向HttpURLConnection輸出
httpURLConnection.setDoOutput(false);
// 設置是否從httpUrlConnection讀入
httpURLConnection.setDoInput(true);
// 設置請求方式 默認為GET
httpURLConnection.setRequestMethod("GET");
// 設置是否使用緩存
httpURLConnection.setUseCaches(true);
// 設置此 HttpURLConnection 實例是否應該自動執行 HTTP 重定向
httpURLConnection.setInstanceFollowRedirects(true);
// 設置超時時間
httpURLConnection.setConnectTimeout(3000);
// 連接
httpURLConnection.connect();
// 4. 得到響應狀態碼的返回值 responseCode
int code = httpURLConnection.getResponseCode();
// 5. 如果返回值正常,數據在網絡中是以流的形式得到服務端返回的數據
String msg = "";
if (code == 200) { // 正常響應
// 從流中讀取響應信息
BufferedReader reader = new BufferedReader(new InputStreamReader(httpURLConnection.getInputStream()));
String line = null;
while ((line = reader.readLine()) != null) { // 循環從流中讀取
msg += line + "\n";
}
reader.close(); // 關閉流
}
// 顯示響應結果
log.info(msg);
} catch (IOException e) {
log.error("轉發出錯,錯誤信息:"+e.getLocalizedMessage()+";"+e.getClass());
}finally {
// 6. 斷開連接,釋放資源
if (null != httpURLConnection){
try {
httpURLConnection.disconnect();
}catch (Exception e){
log.info("httpURLConnection 流關閉異常:"+ e.getLocalizedMessage());
}
}
}
}
}
2、POST請求
package com.feng.demo;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
/** * POST請求示例 * * @author 安辰 * */
public class PostDemo {
public void doPost(String[] args) {
HttpURLConnection httpURLConnection = null;
try {
// 1. 獲取訪問地址URL
URL url = new URL("http://localhost:8080/Servlet/do_login.do");
// 2. 創建HttpURLConnection對象
httpURLConnection = (HttpURLConnection) url.openConnection();
/* 3. 設置請求參數等 */
// 請求方式 默認 GET
httpURLConnection.setRequestMethod("POST");
// 超時時間
httpURLConnection.setConnectTimeout(3000);
// 設置是否輸出
httpURLConnection.setDoOutput(true);
// 設置是否讀入
httpURLConnection.setDoInput(true);
// 設置是否使用緩存
httpURLConnection.setUseCaches(false);
// 設置此 HttpURLConnection 實例是否應該自動執行 HTTP 重定向
httpURLConnection.setInstanceFollowRedirects(true);
// 設置請求頭
httpURLConnection.addRequestProperty("sysId","sysId");
// 設置使用標准編碼格式編碼參數的名-值對
httpURLConnection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
// 連接
httpURLConnection.connect();
/* 4. 處理輸入輸出 */
// 寫入參數到請求中
String params = "username=test&password=123456";
OutputStream out = httpURLConnection.getOutputStream();
out.write(params.getBytes());
// 簡化
//httpURLConnection.getOutputStream().write(params.getBytes());
out.flush();
out.close();
// 從連接中讀取響應信息
String msg = "";
int code = httpURLConnection.getResponseCode();
if (code == 200) {
BufferedReader reader = new BufferedReader(
new InputStreamReader(httpURLConnection.getInputStream()));
String line;
while ((line = reader.readLine()) != null) {
msg += line + "\n";
}
reader.close();
}
// 處理結果
log.info(msg);
} catch (IOException e) {
log.error("轉發出錯,錯誤信息:"+e.getLocalizedMessage()+";"+e.getClass());
}finally {
// 5. 斷開連接
if (null != httpURLConnection){
try {
httpURLConnection.disconnect();
}catch (Exception e){
log.info("httpURLConnection 流關閉異常:"+ e.getLocalizedMessage());
}
}
}
}
}
3、總結
從網上找資料的時候,發現 connect() 和 flush() 方法有時寫,有時不寫,還沒有理由,特來總結一番。
a、connect()方法調用不調用:
- 調用connect()只是建立連接,並不會向服務器傳送數據,只要調用getResponseCode(),就不必要調用connect方法(調用也無妨)。
b、是否調用connect方法?
不需要顯示調用connect方法
c、openConnection()方法到底是如何返回URLConnection對象的:
- openConnection()在你不自己實現網絡協議等網絡相關抽象類和抽象接口的情況下,此方法最終調用的是sun.net.www.protocol.http.Handler類中的方法
d、必須調用getResponseCode()方法 。
- 安辰試了一下在不調用getResponseCode()方法的時候,無論是否調用connect()方法,請求都是不能成功的,調用connect()方法只是建立連接,並不會向服務器傳遞數據。
- 只用調用getRespconseCode()方法時,才會向服務器傳遞數據(有博文說是getInputStream()才會向服務器傳遞數據,getResponseCode中會調用getInputStream方法)。
- 跟着getResponseCode()源碼發現里面調用了getInputStream()方法,在getInputStream()方法中會判斷當前是否連接,如果沒有連接,則調用connect()方法建立連接。
四、小方法解釋
1、flush()
-
flush()意思是把**緩沖區的內容強制的寫出**。 因為操作系統的某些機制,為了防止一直不停地磁盤讀寫,所以有了延遲寫入的概念,(注意不要和frush()刷新混淆了)
-
主要用在IO中,即清空緩沖區數據,一般在讀寫流(stream)的時候,數據是先被讀到了內存中,再把數據寫到文件中,當你數據讀完的時候不代表你的數據已經寫完了,因為還有一部分有可能會留在內存這個緩沖區中。這時候如果你調用了close()方法關閉了讀寫流,那么這部分數據就會丟失,所以應該在關閉讀寫流之前先flush()。。
-
為了防止過於頻繁的寫操作 所以Java提供了一個java.io.BufferedOutputStream類 內部持有一個緩沖區 默認不直接將數據寫到硬盤上 而是存到緩沖區中 直到一定條件后觸發(就是調用上面的flushBuffer()了) 也可以強制通過flush()方法提前觸發。
-
所以當你認為你完成了某一個比較重要的操作的時候 最好進行一次flush 防止數據在內存中丟失。
多數時候,如果你最后會調用一次close方法,flush方法是可以不使用的,除非你明確的想使數據盡早寫到磁盤或者網絡上。
補充:注意setRequestMethod()中的字符串參數為大寫!