Jodd-http是一個微型的、簡約的http client,然而簡單而且方便。使用它可以輕松的實現發送請求和讀取響應。它的目標就是日常應用變的非常簡單,從而簡化開發人員的工作。
了解Jodd-http的最好方法就是示例程序。
簡單的GET方法
HttpRequest httpRequest = HttpRequest.get("http://jodd.org"); HttpResponse response = httpRequest.send(); System.out.println(response);
上述代碼實現了一個get請求,並打印出響應結果。
我們看一下響應結果:
HTTP/1.1 200 OK Date: Fri, 12 Jun 2015 02:43:50 GMT Server: Apache Last-Modified: Mon, 25 May 2015 21:44:44 GMT Accept-Ranges: bytes Content-Length: 14056 Cache-Control: max-age=0, public Expires: Fri, 12 Jun 2015 02:43:50 GMT Vary: Accept-Encoding Connection: close Content-Type: text/html; charset=utf-8 <!DOCTYPE html> <html> <head> .................................. ................................... </body> </html>
請結合下圖理解
你也可以這么寫:
HttpResponse response = HttpRequest.get("http://jodd.org").send();
System.out.println(response);
或者一步步構建:
HttpRequest request = new HttpRequest(); request .method("GET") .protocol("http") .host("srv") .port(8080) .path("/api/jsonws/user/get-user-by-id");
好吧,了解一下請求的樣式吧:
讀取響應報文
發送消息后,響應報文存放在HttpResponse實例中,可以調用HttpResponse的方法獲取statusCode、statusPhrase或者報文頭屬性。
讀取報文本身的方法主要有三種:
body()--通常是ISO-8859-1編碼格式的基本報文內容。
bodyText()--根據報文頭"Content-Type"屬性值的編碼格式編譯的報文。
BodyByte()--以字節數組形式返回的基本報文,例如下載一個要保存的文件。
注意:在使用bodyText()方法前,必須使用charset()方法指定編碼格式。
查詢參數
在get方法中,查詢參數可以在url中指定(需要正確的編碼),如:
HttpResponse response = HttpRequest .get("http://srv:8080/api/jsonws/user/get-user-by-id?userId=10194") .send();
或者通過query()方法設置,如:
HttpResponse response = HttpRequest .get("http://srv:8080/api/jsonws/user/get-user-by-id") .query("userId", "10194") .send();
設置一個參數可以使用query()方法,或者每個參數調用一次query()方法。
當然也可以使用Map<String,String>做為一個參數進行傳參。
注意:查詢參數(報文頭或者表單參數)可以重復的。內部它們存儲在數組中。使用removeQuery方法來刪除參數或者多次調用query方法來替換參數。
query還可以讀取所有內部參數:
Map<String, Object[]> httpParams = request.query(); httpParams.put("userId", new String[] {"10194"});
基本認證
基本認證非常簡單:
request.basicAuthentication("test", "test");
post方法和表單參數
和get方法中的query類似:
HttpResponse response = HttpRequest .post("http://srv:8080/api/jsonws/user/get-user-by-id") .form("userId", "10194") .send();
可以看出,在post方法中使用form來指定表單參數。form使用方法和query的使用方法完全相同。
上傳文件
同樣,也非常容易,只需要將文件增加到form參數中。示例如下:
HttpRequest httpRequest = HttpRequest .post("http://srv:8080/api/jsonws/dlapp/add-file-entry") .form( "repositoryId", "10178", "folderId", "11219", "sourceFileName", "a.zip", "mimeType", "application/zip", "title", "test", "description", "Upload test", "changeLog", "testing...", "file", new File("d:\\a.jpg.zip") ); HttpResponse httpResponse = httpRequest.send();
上面就是全部代碼了。
監控文件上傳進度
上傳大文件時,需要監控上傳進度。HttpProgressListener提供了這種功能:
HttpResponse response = HttpRequest .post("http://localhost:8081/hello") .form("file", file) .monitor(new HttpProgressListener() { @Override public void transferred(long len) { System.out.println(len/size); } }) .send();
在文件上傳前,HttpProgressListener計算出callbackSize---每次要傳輸的文件塊的字節數。默認情況下為整個文件的1%大小。一般情況下,不會少於512字節。
HttpProgressListener包含了一個內部成員size,代表了整個請求的大小。注意:整個請求的size不僅僅是文件,它是整個要發送的請求的真實字節數,通常比大文件的size要大一些(參見請求報文格式)。
報文頭
增加或者獲取報文頭參數,可以使用header()方法。一些通用的報文頭參數已經定義好了,因此你可以通過contentType()等類似方法來查詢。
壓縮、解壓報文
使用unzip()解壓報文示例
HttpResponse response = HttpRequest .get("http://www.liferay.com") .acceptEncoding("gzip") .send(); System.out.println(response.unzip());
使用body方法
可以手動設置請求報文的內容--有時,一些api允許在body內指明類似的命令:
HttpResponse response = HttpRequest .get("http://srv:8080/api/jsonws/invoke") .body("{'$user[userId, screenName] = /user/get-user-by-id' : {'userId':'10194'}}") .basicAuthentication("test", "test") .send();
設置body后可以摒棄上面form()方法來設置參數。
然而,在HttpResponse中使用body()方法去接收報文內容,這樣更有意義。
字符和編碼格式
默認情況下,query和form參數使用utf-8編碼。全局上可以通過JoddHttp來重新設置,或者局部如下所示:
HttpResponse response = HttpRequest .get("http://server/index.html") .queryEncoding("CP1251") .query("param", "value") .send();
當然,可以使用類似的方式設置form的編碼格式。進而,表單提交時檢測報文頭"Content-type"中的值,若存在,將使用該值。接收報文時,body()方法通常返回ISO-8859-1編碼格式的報文,若要返回指定的格式,可以使用bodyText()方法,此時需要預先設置編碼格式"Content-type"的值。
HttpConnection
HttpConnection是封裝了http物理通信的接口,調用send()方法時,http將open()一個連接(不存在該連接時)。內部里,HttpConnectionProvier創建該連接。參見上篇文章(簡約之美Jodd-http--深入源碼理解http協議).默認的連接提供者是基於socket的,它通常返回一個新創建的SocketHttpConnection實例,該實例簡單包裝了一個打開的socket。也可以使用自定義的HttpConnectionProvider,例如你可以擴展SocketHttpConnectionProvider,重寫createSocket()方法,讓其從socket池中返回socket或者返回不同過期時間的socket。
Socket
如上所述,http通信通過普通的socket來完成。因此在發送數據前可以改變socket的行為,下面示例展示了http如何改變socket行為。
SocketHttpConnection
通過httpConnection調用socket:
HttpRequest request = HttpRequest.get()...; request.open(); SocketHttpConnection httpConnection = (SocketHttpConnection) request.httpConnection(); Socket socket = httpConnection.getSocket(); socket.setSoTimeout(1000); ... HttpResponse response = request.send();
SocketHttpConnectionProvider
另外一種方法是SocketHttpConnectionProvider,創建自己的provider:
public class MyConnectionProvider extends SocketHttpConnectionProvider { protected Socket createSocket( SocketFactory socketFactory, String host, int port) throws IOException { Socket socket = super.createSocket(socketFactory, host, port); socket.setSoTimeout(1000); return socket; } }
然后使用open方法獲取socket
HttpConnectionProvider connectionProvider = new MyConnectionProvider(); ... HttpRequest request = HttpRequest.get()...; HttpResponse response = request.open(connectionProvider).send();
若要設置自定義的connectionProvider的默認ConnectionProvider而適用於所有的通信,只需要將它賦給JoddHttp.httpConnectionProvider就不必顯式調用open()方法。
持久連接(keep-alive)
默認情況下,所有的連接時關閉的。http允許通設置連接為keep-alive模式從而使連接一直存在。在keep-alive模式下,在通信會話中,第一次請求時打開一個HttpConnection,以后重用該連接;若非必要,socket不會再次打開,因而可以被多個request重復使用。
有許多方法可以做到,但最簡單的方法如下所示:
HttpRequest request = HttpRequest.get("http://jodd.org"); HttpResponse response = request.connectionKeepAlive(true).send(); // next request request = HttpRequest.get("http://jodd.org/jodd.css"); response = request.keepAlive(response, true).send(); ... // last request request = HttpRequest.get("http://jodd.org/jodd.png"); response = request.keepAlive(response, false).send(); // optionally //response.close();
代理
HttpConnectionProvider支持代理,只需要提供指明代理信息(類型、地址、端口、用戶名、密碼)的ProxyInfo實例即可。支持HTTP/SOCKS4/SOCKET5三種類型,其區別是:
SOCKS:防火牆安全會話轉換協議 (Socks:Protocol for sessions traversal across firewall securely) Socks 協議提供一個框架,在 TCP 和 UDP 域中的客戶機/服務器應用程序能更方便安全地使用網絡防火牆所提供的服務。這個協議從概念上來講是介於應用層和傳輸層之間的 “中介層(shim-layer)”,所以不提供傳遞 ICMP 信息之類的網絡層網關服務。
Socks4和Socks5都屬於Socks協議,只是由於所支持的具體應用不同而存在差異。
Socks4代理只支持TCP應用,而Socks5代理則可以支持TCP和UDP兩種應用。不過由於Socks5代理還支持各種身份驗證機制,服務器端域名解析等;而Socks4代理沒有,所以通常對外開放的 Socks代理都是Socks4代理。因此,UDP應用通常都不能被支持。也就是說,Socks4能做的Socks5都可以做,而socks5能做的,Socks4不一定都可以做。
輸入流解析
HttpRequest和HttpResponse都實現了讀取輸入流的方法readFrom(InputStream)。下面舉例說明服務器如何讀取請求。
HttpBrowser
當需要通過改變目標地址來模擬一下"移動"的場景時,僅僅發送請求、接收響應是不夠。例如登陸需要在當前session內讀取不同目標地址的內容。
HttpBrowser能完成這種功能。它發送請求,自動處理301和302跳轉,從響應報文中讀取cookies存到新的請求報文中等等。簡單示例如下:
HttpBrowser browser = new HttpBrowser(); HttpRequest request = HttpRequest.get("www.facebook.com"); browser.sendRequest(request); // request is sent and response is received // process the page: String page = browser.getPage(); // create new request HttpRequest newRequest = HttpRequest.post(formAction); browser.sendRequest(newRequest);
HttpTunnel
jodd-http提供了一個靈活的構建http隧道的方法(簡單代理和目的地址的隧道),HttpTunnel實現了此功能。
參考文獻:
【1】 http://jodd.org/doc/http.html
【2】http://www.it165.net/admin/html/201403/2541.html
【3】http://www.cnblogs.com/skynet/archive/2010/12/11/1903347.html
【4】http://www.ccproxy.com/socks4-yu-socks5-de-qu-bie-jie-shao.htm