簡單封裝 HTTP 請求
2017-2-19 更新到第二版:
原始碼地址:http://git.oschina.net/sp42/ajaxjs/tree/master/ajaxjs-base/src/com/ajaxjs/net?dir=1&filepath=ajaxjs-base%2Fsrc%2Fcom%2Fajaxjs%2Fnet
和檔案操作一樣,其內部使用了鏈式風格的呼叫方式。
GET/HEAD 請求
GET 請求用法參見下面的測試用例,包括普通 GET 請求、獲取 302 重定向調轉地址、獲取資原始檔體積大小、是否 404以及下載二進位制檔案等功能。
POST 請求System.out.println(Client.GET("https://www.baidu.com/")); // 獲取資原始檔體積大小 long size = Client.getFileSize("http://c.csdnimg.cn/jifen/images/xunzhang/xunzhang/bokezhuanjiamiddle.png"); assertEquals(size, 4102L); // 獲取 302 重定向跳轉地址 System.out.println(Client.get302redirect("https://baidu.com")); // 封裝 head 請求檢測是否 404 assertTrue(!Client.is404("http://c.csdnimg.cn/jifen/images/xunzhang/xunzhang/bokezhuanjiamiddle.png")); // B 站強制 Gzip 返回,無論請求是否帶有 GZIP 欄位 System.out.println(Client.GET_Gzip("http://www.bilibili.com/video/av5178498/"));
String url = "http://localhost:8080/pachong/post.jsp";
String result = Client.POST(url, new HashMap<String, Object>() {
private static final long serialVersionUID = 1L;
{
put("foo", "bar");
}
});
System.out.println("Feedback:" + result);
2016-7-12 : 新增 GZip 請求和 Gzip 響應判斷。
請求判斷
if(request.isEnableGzip()) // 是否啟動 GZip 請求
connection.addRequestProperty("Accept-Encoding", "gzip, deflate");
一般情況下,請求頭加入了上面的 Accept-Encoding 欄位,伺服器才會對內容進行 GZip 壓縮,否則就不壓縮,原文輸出。但有些網站是不管有沒有這種請求都一概返回 GZIP 的。如果有 GZIP,伺服器會告訴我們的,在響應頭中加入 Content-Encoding 的欄位。響應判斷:
// 是否啟動 GZip 請求 // 有些網站強制加入 Content-Encoding:gzip,而不管之前的是否有 GZip 的請求 boolean isGzip = request.isEnableGzip() || "gzip".equals(connection.getHeaderField("Content-Encoding"));
如果不對 Gzip 的內容進行 GZipInputStream 處理會一段亂碼。
測試用例:
@Test
public void testGZipGet() {
Request request = new Request();
request.setUrl("http://u.3gtv.net");
request.setEnableGzip(true);
RequestClient rc = new RequestClient(request);
try {
rc.connect();
} catch (ConnectException e) {
System.out.println("請求出錯" + request.getUrl());
}
String html = request.getFeedback();
System.out.println(html);
assertNotNull(html);
}
// B 站強制 Gzip 返回,無論請求是否帶有 GZIP
@Test
public void testForce_GZipGet() {
String url = "http://www.bilibili.com/video/av5178498/";
String html = Get.GET(url);
System.out.println(html);
assertNotNull(html);
}
-------------------------------------------------------------------------------------------------------------
簡單封裝 Java 類庫裡面的 HttpURLConnection 來完成日常的 HTTP 請求,如 GET、HEAD、POST 等的請求。
GET 請求用法參見下面的測試用例,包括普通 GET 請求、獲取 302 重定向調轉地址、獲取資原始檔體積大小、是否 404以及下載二進位制檔案等功能。
import static org.junit.Assert.*;
import org.junit.Test;
import java.io.IOException;
import com.ajaxjs.net.http.Get;
import com.ajaxjs.util.FileUtil;
public class TestGet {
@Test
public void testSimpleGET() {
String url = "https://baidu.com";
String html = Get.simpleGET(url);
System.out.println(html);
assertNotNull(html);
}
@Test
public void testGet() {
String url = "https://baidu.com";
String html = Get.GET(url);
System.out.println(html);
assertNotNull(html);
}
@Test
public void testGet302redirect() {
String url = "http://baidu.com";
String location = Get.get302redirect(url);
System.out.println(location);
assertNotNull(location);
}
@Test
public void testIs404() {
String url = "http://c.csdnimg.cn/jifen/images/xunzhang/xunzhang/bokezhuanjiamiddle.png";
assertTrue(!Get.is404(url));
assertTrue(Get.is404("http://www.qq.com/54543"));
}
@Test
public void testGetFileSize() {
String url = "http://c.csdnimg.cn/jifen/images/xunzhang/xunzhang/bokezhuanjiamiddle.png";
long size = Get.getFileSize(url);
assertEquals(size, 4102L);
}
@Test
public void testDownload2disk() throws IOException {
String url = "http://c.csdnimg.cn/jifen/images/xunzhang/xunzhang/bokezhuanjiamiddle.png";
Get.download2disk(url, "c:/temp/dd.png");
String saveTo = "c:/temp/js.js";
Get.download2disk("http://bdimg.share.baidu.com/static/api/js/base/tangram.js?v=37768233.js", saveTo);
assertNotNull(FileUtil.readFileAsText(saveTo));
}
}
Get.simpleGET 和 Get.GET 用法一致,傳入 url 引數,返回該網址的文字內容。具體不同可能要看看原始碼。simpleGET 是原始 API 版, 一句話完事的:new URL(url).openStream(),然後把字元流轉為 text 即可。Get.GET 也是字元流轉為 text,只是前期處理上不是調 API,為自己實現邏輯,遵循了 bean 方式的呼叫。這種方式在咱們這個 HTTP 庫裡面的是通用方式。首先 Request 類是一個 Java bean,定義了請求地址 HTTP Url、請求方法 HTTP Mehtod,還有若干常見的 HTTP 頭,都做成了 getter/setter,使用者按照 Request 類的方法呼叫即可。其次 GET 請求和 POST 請求本身就差別不少,因此劃分為 Get/Post 兩個類。但實際發出請求的是 RequestClient 類。這個類是不管哪種請求的,都是圍繞後期 HTTP 響應作處理,主要是流的處理,以及一些諸如 404、超時異常的處理。下面是 RequestClient 原始碼:
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.SocketTimeoutException;
import java.net.URL;
import java.net.UnknownHostException;
import com.ajaxjs.util.FileUtil;
import com.ajaxjs.util.StringUtil;
import sun.misc.BASE64Encoder;
/**
* 發起 HTTP 請求
* @author frank
*
*/
public class RequestClient {
/**
* 請求目標地址
*/
private URL url;
/**
* API 連結物件
*/
private HttpURLConnection connection;
/**
* 攜帶請求資訊的 Bean
*/
private Request request;
/**
* 建立 HTTP 請求
*
* @param request
* 請求資訊物件
*/
public RequestClient(Request request) {
this.request = request;
try {
url = new URL(request.getUrl());
connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod(request.getMethod());
} catch (IOException e) {
System.err.println("初始化連接出錯!" + request.getUrl());
e.printStackTrace();
}
}
/**
* 內部初始化
*/
private void init() {
connection.addRequestProperty("User-Agent", request.getUserAgent());
connection.addRequestProperty("Referer", request.getUrl() == null ? url.getHost() : request.getUrl());
connection.setConnectTimeout(request.getTimeout() * 1000);// 設定超時
// connection.setReadTimeout(30000);
if (request.getCookies() != null) {
String cookieStr = StringUtil.HashJoin(request.getCookies(), ";");
connection.setRequestProperty("Cookie", cookieStr);
}
if (request.getBasicAuthorization() != null) { // HTTP 使用者認證
String username = request.getBasicAuthorization()[0], password = request.getBasicAuthorization()[1];
String encoding = new BASE64Encoder().encode((username + ":" + password).getBytes());
connection.setRequestProperty("Authorization", "Basic " + encoding);
}
}
/**
* 發起請求
*
* @return true 表示為發起請求成功
* @throws ConnectException
*/
public boolean connect() throws ConnectException {
init();
// 寫入資料(POST ONLY, GET 不需要)
if (request.getWriteData() != null && !request.getMethod().equalsIgnoreCase("GET")) {
// 寫入 POST 資料
try (OutputStream os = connection.getOutputStream()) {
os.write(request.getWriteData());
os.flush();
} catch (IOException e) {
e.printStackTrace();
return false;
}
}
// 接受響應
try (InputStream is = connection.getInputStream();) {
if (connection.getResponseCode() >= 400) {// 如果返回的結果是400以上,那麼就說明出問題了
ConnectException e = null;
if (connection.getResponseCode() < 500) {
e = new ConnectException(connection.getResponseCode() + ":客戶端請求引數錯誤!");
} else {
e = new ConnectException(connection.getResponseCode() + ":抱歉!我們服務端出錯了!");
}
String msg = FileUtil.readText(is);
e.setFeedback(msg);
if (request.isTextResponse())
request.setFeedback(msg);
throw e;
}
if (request.getCallback() != null) {
request.getCallback().onDataLoad(is);
}
if (request.isTextResponse())
request.setFeedback(FileUtil.readText(is));
} catch (UnknownHostException e) {
throw new ConnectException("未知地址!" + request.getUrl());
} catch (FileNotFoundException e) {
throw new ConnectException("404 地址!" + request.getUrl());
} catch (SocketTimeoutException e) {
throw new ConnectException("請求地址超時!" + request.getUrl());
} catch (IOException e) {
try {
System.out.println(connection.getResponseCode());
} catch (IOException e1) {
e1.printStackTrace();
}
throw new ConnectException("請求地址 IO 異常!" + request.getUrl());
}
return true;
}
public URL getUrl() {
return url;
}
public void setUrl(URL url) {
this.url = url;
}
public Request getRequest() {
return request;
}
public void setRequest(Request request) {
this.request = request;
}
public HttpURLConnection getConnection() {
return connection;
}
public void setConnection(HttpURLConnection connection) {
this.connection = connection;
}
}
其他原始碼就不張貼了,有興趣的可以到這裡看全部原始碼。
POST 例子不多,先放一個:
String url = "http://localhost:8080/pachong/post.jsp";
Request request = Post.POST(url, new HashMap<String, Object>() {
private static final long serialVersionUID = 1L;
{
put("foo", "bar");
}
});
System.out.println("Feedback:" + request.getFeedback());
assertNotNull(request);
assertTrue(request.isDone());
程式碼比較簡單,應該沒有什麼晦澀的地方。請大家多給給意見。