Java開發筆記(一百一十一)POST方式的HTTP調用

前面介紹了GET方式的HTTP調用,該方式主要用於向服務器索取數據,不論是字符串形式的應答報文,仍是二進制形式的網絡文件,都屬於服務器提供的信息。固然調用方也能夠向服務地址傳送請求參數,除了經過鏈接對象設置的HTTP參數,還能給url地址添加形如「?參數A名稱=A參數值&參數B名稱=B參數值」這樣的業務參數,服務地址根據url後面的業務參數,再返回符合條件的應答數據。假若服務器不只僅做爲信息提供方,還想成爲信息接收方,例如保存調用方提交的表單數據,或者保存調用方待上傳的文件,那便要求調用方的程序可以傳送複雜的數據信息。經過GET方式當然也能在url後方填寫簡單的請求參數,可是這並不是信息傳送的可靠手段,緣由有三:
一、往URL末尾添加的請求參數,全爲明文傳輸,不利於數據的保密措施;
二、URL格式的請求串只支持鍵值對形式的參數,難以表達複雜的結構化數據,譬如數組形式的參數;
三、URL自己是個字符串,Query部分的請求參數也只能是字符串,這叫二進制形式的文件上傳如何是好?
鑑於種種不可避免的困難,GET方式實在不適合向服務器提交數據,必須採用POST方式提交數據才行。POST方式一樣須要服務器給個調用地址,但該方式的業務參數沒放到URL末尾,而是放在了請求報文當中。所謂的請求報文與應答報文相對應,應答報文要從鏈接對象的輸入流中獲取,而請求報文要寫入鏈接對象的輸出流。編碼實現POST請求的時候,除了調用setRequestMethod要將請求方式設置爲POST,還需留意鏈接對象的下列幾種方法:
setRequestProperty:設置請求屬性。該方法可設置特定名稱的屬性值。
setDoOutput:準備讓鏈接執行輸出操做。默認爲false(GET方式),POST方式須要設置爲true。
setDoInput:準備讓鏈接執行輸入操做。默認爲true,一般無需特地調用該方法。
getOutputStream:從鏈接對象中獲取輸出流,後續會把請求報文寫入輸出流。
getHeaderField:獲取應答報文頭部指定名稱的字段值。該方法可獲得特定名稱的參數值,例如getHeaderField("Content-Length")返回的是應答報文的長度,getHeaderField("Content-Type")返回的是應答報文的內容類型,conn.getHeaderField("Content-Encoding")返回的是應答報文的壓縮方式。
上述幾種方法中尤其值得注意的是setRequestProperty,依據不一樣的請求屬性名稱,該方法將會設置各式各樣的屬性值,以此提醒服務器作好相應的準備工做。其中常見的屬性名稱及其屬性值羅列以下:
Content-Type:請求報文的內容類型。若是請求報文采起形如「參數A名稱=A參數值&參數B名稱=B參數值」的url參數格式,則內容類型應設置爲「application/x-www-form-urlencoded」;若是請求報文是json格式,則內容類型應設置爲「application/json」;若是請求報文是xml格式,則內容類型應設置爲「application/xml」;若是請求報文是分段傳輸的文件數據,則內容類型應設置爲「multipart/form-data;boundary=***」。
Connection:指定鏈接的保持方式。若是是文件上傳,則必須設置爲「Keep-Alive」,表示建議服務器保留鏈接,以便可以持續發送文件的分段數據。
User-Agent:指定調用方的瀏覽器類型。
Accept:指定可接受的應答報文類型。若是不設置則默認「*/*」,表示容許返回任何類型的應答報文;若是設置爲「image/png」,則表示只接受返回png圖片。
Accept-Language:指定可接受的應答報文語言。一般無需設置,若是隻接受中文則可設置爲「zh-cn」。
Accept-Encoding:指定可接受的應答報文壓縮方式。若是不設置則默認identity,表示不容許應答報文使用壓縮;若是設置爲gzip,則表示容許應答報文采用GZIP壓縮,此時服務器可能返回gzip壓縮的應答數據,也可能返回未壓縮的應答數據。
接下來舉個請求報文是json串的HTTP接口例子,採用POST方式的調用方法代碼以下所示:html

	// 對指定url發起POST調用
	private static void testCallPost(String callUrl, String body) {
		try {
			URL url = new URL(callUrl); // 根據網址字符串構建URL對象
			// 打開URL對象的網絡鏈接,並返回HttpURLConnection鏈接對象
			HttpURLConnection conn = (HttpURLConnection) url.openConnection();
			conn.setRequestMethod("POST"); // 設置請求方式爲POST調用
			conn.setRequestProperty("Content-Type", "application/json"); // 請求報文爲json格式
			conn.setDoOutput(true); // 準備讓鏈接執行輸出操做。默認爲false,POST方式須要設置爲true
			conn.connect(); // 開始鏈接
			OutputStream os = conn.getOutputStream(); // 從鏈接對象中獲取輸出流
			os.write(body.getBytes()); // 往輸出流寫入請求報文
			// 打印HTTP調用的應答內容長度、內容類型、壓縮方式
			System.out.println( String.format("應答內容長度=%s, 內容類型=%s, 壓縮方式=%s", 
					conn.getHeaderField("Content-Length"), conn.getHeaderField("Content-Type"),
					conn.getHeaderField("Content-Encoding")) );
			// 對輸入流中的數據進行解壓和字符編碼,獲得原始的應答字符串
			String content = StreamUtil.getUnzipString(conn);
			// 打印HTTP調用的應答狀態碼和應答報文
			System.out.println( String.format("應答狀態碼=%d, 應答報文=%s", 
					conn.getResponseCode(), content) );
			conn.disconnect(); // 斷開鏈接
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

 

而後由外部在調用testCallPost時輸入服務地址和請求報文,具體代碼示例以下:json

		testCallPost("http://localhost:8080/NetServer/checkUpdate", "{\"package_list\":[{\"package_name\":\"com.qiyi.video\"}]}");

 

運行上述的POST代碼,從如下的接口日誌可知POST方式正確發送了請求報文,且正常收到了應答報文。數組

請求報文={"package_list":[{"package_name":"com.qiyi.video"}]}
應答內容長度=152, 內容類型=text/plain;charset=utf-8, 壓縮方式=null
應答狀態碼=200, 應答報文={"package_list":[{"package_name":"com.qiyi.video","download_url":"https://3g.lenovomm.com/w3g/yydownload/com.qiyi.video/60020","new_version":"10.2.0"}]}

經過HTTP接口上傳文件也要採用POST方式,只是文件上傳還需遵照必定的數據規則,除了內容類型設置爲「multipart/form-data;boundary=***」(***處要替換成邊界字符串),請求報文也得依順序填入報文頭、報文體和報文尾,詳細的上傳過程代碼以下所示:瀏覽器

	// 把本地文件上傳給指定url
	private static void testUpload(String filePath, String uploadUrl) {
		// 從本地文件路徑獲取文件名
		String fileName = filePath.substring(filePath.lastIndexOf("/"));
		String end = "\r\n"; // 結束字符串
		String hyphens = "--"; // 鏈接字符串
		String boundary = "WUm4580jbtwfJhNp7zi1djFEO3wNNm"; // 邊界字符串
		try (FileInputStream fis = new FileInputStream(filePath)) {
			URL url = new URL(uploadUrl); // 根據網址字符串構建URL對象
			// 打開URL對象的網絡鏈接,並返回HttpURLConnection鏈接對象
			HttpURLConnection conn = (HttpURLConnection) url.openConnection();
			conn.setDoOutput(true); // 準備讓鏈接執行輸出操做。默認爲false,POST方式都要設置爲true
			conn.setRequestMethod("POST"); // 設置請求方式爲POST調用
			// 鏈接過程要保持活躍
			conn.setRequestProperty("Connection", "Keep-Alive");
			// 請求報文要求分段傳輸,而且各段之間以邊界字符串隔開
			conn.setRequestProperty("Content-Type", "multipart/form-data;boundary=" + boundary);
			// 根據鏈接對象的輸出流構建數據輸出流
			DataOutputStream ds = new DataOutputStream(conn.getOutputStream());
			// 如下寫入請求報文的頭部
			ds.writeBytes(hyphens + boundary + end);
			ds.writeBytes("Content-Disposition: form-data; "
					+ "name=\"file\";filename=\"" + fileName + "\"" + end);
			ds.writeBytes(end);
			// 如下寫入請求報文的主體
			byte[] buffer = new byte[1024];
			int length;
			// 先將文件數據寫入到緩衝區,再將緩衝數據寫入輸出流
			while ((length = fis.read(buffer)) != -1) {
				ds.write(buffer, 0, length);
			}
			ds.writeBytes(end);
			// 如下寫入請求報文的尾部
			ds.writeBytes(hyphens + boundary + hyphens + end);
			ds.close(); // 關閉數據輸出流
			// 對輸入流中的數據進行解壓和字符編碼,獲得原始的應答字符串
			String content = StreamUtil.getUnzipString(conn);
			// 打印HTTP上傳的應答狀態碼和應答報文
			System.out.println( String.format("應答狀態碼=%d, 應答報文=%s", 
					conn.getResponseCode(), content) );
			conn.disconnect(); // 斷開鏈接
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

 

而後由外部在調用testUpload方法時輸入上傳地址和待上傳的文件路徑,具體代碼示例以下:服務器

		testUpload("E:/bliss.jpg", "http://localhost/NetServer/uploadServlet");

 

運行上述的上傳代碼,從如下的上傳日誌可知文件已經成功上傳至服務器。網絡

應答狀態碼=200, 應答報文=文件上傳成功,文件大小爲1912K

  

更多Java技術文章參見《Java開發筆記(序)章節目錄app

相關文章
相關標籤/搜索