如何使用java.net.URLConnection接收及发送HTTP请求
本文于1931天之前发表,文中内容可能已经过时。
首先声明,下面的代码,都是基本的例子。更严谨的话,还应加入处理各种异常的代码(如IOExceptions、NullPointerException、ArrayIndexOutOfBoundsException)
准备
首先,需要设置请求的URL以及charset(编码);额外的参数,则取决于各自url的要求。
1 | S |
url中附带的请求参数,必须是name=value这样的格式,每个参数间用&连接。一般来说,你还得用 URLEncoder#encode()对参数做编码
上面例子还用到了String#format()。字符拼接方式,看个人喜好,我更喜欢用这个方式。
发送一个HTTP GET请求(可选:带上参数)
这依然是个繁琐的事情。默认的方式如下:
1 | URLConnection connection = new URL(url + "?" + query).openConnection(); |
url和参数之间,要用?号连接。请求头(header)中的Accept-Charset,用于告诉服务器,你所发送参数的编码。如果你不带送任何参数,也可以不管Accept-Charset。另外如果你无需设置header,也可以用URL#openStream() 而非openConnection。
不管那种方式,假设服务器端是 HttpServlet,那么你的get请求将会触发它的doGet()方法,它能通过HttpServletRequest#getParameter()获取你传递的参数。
发送一个HTTP POST请求,并带上参数
设置URLConnection#setDoOutput(),等于隐式地将请求方法设为POST。标准的HTTP POST 表单,其Content-Tyep为application/x-www-form-urlencoded,请求的内容放到到body中。也就是如下代码:
1 | URLConnection connection = new URL(url).openConnection(); |
提醒:
当你要提交一个HTML表单时,务必要把<input type="hidden"
,<input type="submit">
这类元素的值,也以name=value的形式提交,因为,服务端通常也需要这个信息,已确认哪一个按钮触发了这个提交动作。
也可以使用HttpURLConnection 来代替URLConnection ,然后调用HttpURLConnection#setRequestMethod()来将请求设为POST类型。
1 | HttpURLConnection httpConnection = (HttpURLConnection) new URL(url).openConnection(); |
同样的,如果服务端是HttpServlet,将会触发它的doPost()方法,可以通过HttpServletRequest#getParameter()获取post参数
触发HTTP请求的发送
你可以显式地通过URLConnection#connect()来发送请求,但是,当你调用获取响应信息的方法时,一样将自动发送请求。例如当你使用URLConnection#getInputStream()时,就会自动触发请求,因此,connect()方法往往都是多余的。上面我的例子,也都是直接调用getInputStream()方法。
获取HTTP响应信息
HTTP响应码:
首先默认你使用了 HttpURLConnection1
int status = httpConnection.getResponseCode();
HTTP 响应头(headers)
1
2
3for (Entry<String, List<String>> header : connection.getHeaderFields().entrySet()) {
System.out.println(header.getKey() + "=" + header.getValue());
}HTTP响应编码:
当Content-Type中包含charset参数时,说明响应内容是基于charset参数指定的编码。因此,解码响应信息时,也要按照这个编码格式来。
1 | String contentType = connection.getHeaderField("Content-Type"); |
session的维护
服务端session,通常是基于cookie实现的。你可以通过CookieHandlerAPI来管理cookie。在发送HTTP请求前,初始化一个CookieManager, 然后设置参数为CookiePolicy.CCEPT_ALL。
1 | // First set the default cookie manager. |
请注意,这个方式并非适用于所有场景。如果使用这个方式失败了,你可以尝试自己设置cookie:你需要从响应头中拿到Set-Cookie参数,然后再把cookie设置到接下来的其他请求中。
1 | // Gather all cookies on the first request. |
上面的split(“;”, 2)[0],作用是去掉一些跟服务端无关的cookie信息(例如expores,path等)。也可用cookie.substring(0, cookie.indexOf(‘;’))达到同样的目的
流的处理
不管你是否通过connection.setRequestProperty(“Content-Length”, contentLength)方法,为content设置了定长, HttpURLConnection在发送请求前,默认都会缓存整个请求的body。如果发送一个比较大的post请求(例如上传文件),有可能会导致OutOfMemoryException。为了避免这个问题,可以设置HttpURLConnection#setFixedLengthStreamingMode()
1 | httpConnection.setFixedLengthStreamingMode(contentLength); |
但如果content长度是未知的,则可以用HttpURLConnection#setChunkedStreamingMode()。这样,header中Transfer-Encoding会变成chunked,你的请求将会分块发送,例如下面的例子,请求的body,将会按1KB一块,分块发送
1 | httpConnection.setChunkedStreamingMode(1024); |
User-Agent
有时候,你发送的请求,可能只有在浏览器下才能正常返回,而其他方式却不行。这可能跟请求头中的User-Agent有关。通过URLConnection发送的请求,默认会带上的User-Agent信息是Java/1.6.0_19,也就是java+jre的版本。你可以重写这个信息:
1 | connection.setRequestProperty("User-Agent", "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.2.3) Gecko/20100401"); // Do as if you're using Firefox 3.6.3. |
这里有一份更全的浏览器User-Agent清单
错误处理
如果HTTP的响应码是4xx(客户端异常)或者5xx(服务端异常),你可以通过HttpURLConnection#getErrorStream()获取信息,服务端可能会将一些有用的错误信息放到这里面。
1 | InputStream error = ((HttpURLConnection) connection).getErrorStream(); |
上传文件
一般来说,你需要将post的内容设为multipart/form-data(相关的RFC文档:RFC2388)
1 | String param = "value"; |
假设服务端还是一个HttpServlet,它的doPost()方法将会处理这个请求,服务端通过HttpServletRequest#getPart()获取你发送的内容(注意了,不是getParameter())。getPart()是个比较新的方法,是在Servlet 3.0后才引入的。如果你是Servlet 3.0之前的版本,则可以选用Apache Commons FileUpload
最后的话
上面啰嗦了很多,Apache提供了工具包,帮助我们更方便地完成这些事情
Apache HttpComponents HttpClient:
google也有类似的工具包
解析、提取HTML内容
如果你是想解析提取html的内容,你可以用Jsoup等解析器
stackoverflow原址:
http://stackoverflow.com/questions/2793150/using-java-net-urlconnection-to-fire-and-handle-http-requests