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