序言
要達到基本的Http get與post方法取得網站內容,Java內的URLConnection就可以達成。但我實作時發現它太過於底層,以致於我不知該如何做到關於Session狀態的延續,也就是不能夠在完成登入後,保留住登入狀態然後進入下個動作。
於是就尋求別的作法,最後發現了Apache有提供了HttpClient套件的JAR檔,能讓我們用更簡單的方式達成工作。
在此參考網路上的文章:用HttpClient來模擬瀏覽器GET,POST,加上可自動轉導的功能,包成函式。
另外再加上可用三種類型來設定傳遞的參數,並控制其連線與中斷狀態。
下載套件
到HttpClient Download下載套件,選擇Binary with dependencies的版本(如4.0.1.zip),
我們需要的JAR在該壓縮檔中的lib目錄下。會用到的JAR檔有三個:
我們需要的JAR在該壓縮檔中的lib目錄下。會用到的JAR檔有三個:
- commons-logging-1.1.1.jar
- httpclient-4.0.1.jar
- httpcore-4.0.1.jar
函式原始碼
參數轉換函式package common.control;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.HashMap;
public class HTTPParseFunc {
/**
* hashMapToString
*
* @param map
* @param charset編碼
* ,如HTTP.UTF_8
* @return
* @throws UnsupportedEncodingException
*/
@SuppressWarnings("unchecked")
public static String hashMapToString(HashMap<String, String> map,
String charset) throws UnsupportedEncodingException {
StringBuffer result = new StringBuffer();
java.util.Iterator it = map.entrySet().iterator();
boolean isfirst = true;
while (it.hasNext()) {
java.util.Map.Entry entry = (java.util.Map.Entry) it.next();
if (isfirst) {
isfirst = false;
} else {
result.append("&");
}
result
.append(URLEncoder.encode(entry.getKey().toString(),
charset));
result.append("=");
result.append(URLEncoder.encode(entry.getValue().toString(),
charset));
}
return result.toString();
}
/**
* 將inputStream轉為String
*
* @param is
* inputStream
* @param charset
* 編碼,如HTTP.UTF_8
* @return inputStream的內容
* @throws UnsupportedEncodingException
*/
public static String inputStream2String(InputStream is, String charset)
throws UnsupportedEncodingException {
BufferedReader in = new BufferedReader(new InputStreamReader(is,
charset));
StringBuffer buffer = new StringBuffer();
String line = "";
try {
boolean isfirst = true;
while ((line = in.readLine()) != null) {
if (!isfirst) {
buffer.append("\n");
} else {
isfirst = false;
}
buffer.append(line);
}
} catch (IOException e) {
e.printStackTrace();
}
return buffer.toString();
}
/**
* HTTP 傳輸參數分割
* @param param 如name1=value1&name2=value2
* @return
* @throws UnsupportedEncodingException
*/
public static ArrayList<String[]> paramToArray(String param)
throws UnsupportedEncodingException {
ArrayList<String[]> arr = null;
String[] p = param.split("&");
if (param.toLowerCase().contains("&")) {
ArrayList<String> p2 = new ArrayList<String>();
int j = 0;
for (int i = 0; i < p.length; i++) {
if (p[i].toLowerCase().startsWith("amp;")) {
p2.set(j - 1, p2.get(j - 1) + "&" + p[i].substring(4));
j--;
}
p2.add(p[i]);
j++;
}
p2.toArray(p);
}
for (int i = 0; i < p.length; i++) {
String[] item = p[i].split("=");
if (item.length == 2) {
if (arr == null)
arr = new ArrayList<String[]>();
// item[0]=URLDecoder.decode(item[0],charset);
// item[1]=URLDecoder.decode(item[1],charset);
arr.add(item);
}
}
return arr;
}
}
HTTP連線函式
package common;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.HashMap;
import java.util.List;
import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.HttpHost;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.NameValuePair;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.conn.params.ConnRoutePNames;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.params.HttpConnectionParams;
import org.apache.http.params.HttpParams;
import common.control.HTTPParseFunc;
public class HTTPBaseIO {
public enum Method {
get, post
}
private DefaultHttpClient httpclient = null;
private boolean isClosedConn = false;
private String newuri = null;
private int statuscode = HttpStatus.SC_NO_CONTENT;
private HttpHost proxy = null;
private Integer timeout=null;
public HTTPBaseIO(){
}
public HTTPBaseIO(String proxyIP,int proxyPort){
setProxy(proxyIP,proxyPort);
}
public HTTPBaseIO(HttpHost proxy){
setProxy(proxy);
}
/**
* 取得使用的proxy
*/
public HttpHost getProxy() {
return proxy;
}
/**
* 設定proxy
*/
public void setProxy(HttpHost proxy) {
this.proxy = proxy;
}
/**
* 設定proxy
*
* @param ip
* proxy的IP(hostname)
* @param port
* proxy的Port
*/
public void setProxy(String ip, int port) {
if(ip!=null)
proxy = new HttpHost(ip, port);
}
/**
* 取得回應後所得到的代碼,可參考org.apache.http.HttpStatus類別
*
* @return org.apache.http.HttpStatus
*/
public int getStatuscode() {
return statuscode;
}
/**
* 如果是轉導的狀態所得到的URI
*
* @return
*/
public String getNewuri() {
return newuri;
}
public void resetNewuri() {
newuri = null;
}
/**
* 取得連線物件
*
* @return
*/
public DefaultHttpClient getHttpclient() {
return httpclient;
}
/**
* 設定連線物件
*
* @param httpclient
*/
public void setHttpclient(DefaultHttpClient httpclient) {
this.httpclient = httpclient;
}
/**
* 是否已關閉連線
*
* @return
*/
public boolean isClosedConn() {
return isClosedConn;
}
/**
* 關閉連線
*/
public void closeConn() {
closeConn(true);
}
/**
* 關閉連線
*
* @param isCloseConn
* 是否關閉
*/
public void closeConn(boolean isCloseConn) {
if (isCloseConn && httpclient != null && !isClosedConn) {
httpclient.getConnectionManager().shutdown();
httpclient = null;
isClosedConn = true;
}
}
public void setHttpConnectionFactoryTimeout(Integer milliseconds){
timeout=milliseconds;
}
/**
* 取得網頁內容
*
* @param urlpath
* 網址
* @param method
* Method.get or Method.post
* @param params
* 參數
* @param charset
* 編碼,如HTTP.UTF_8
* @param isAutoRedirect
* 如果網頁回應狀態為轉導到新網頁,且Header的location有值,則自己以location所指網址取得內容
* @param isCloseConn
* 是否關閉連線
* @return 失敗回傳null,成功回傳網頁HTML
* @throws ClientProtocolException
* @throws IOException
*/
public String doSend(String urlpath, Method method, String params,
String charset, boolean isAutoRedirect, boolean isCloseConn)
throws ClientProtocolException, IOException {
return doSendBase(urlpath, method, StringToHttpEntity(params, charset),
charset, isAutoRedirect, isCloseConn);
}
/**
* 取得網頁內容
*
* @param urlpath
* 網址
* @param method
* Method.get or Method.post
* @param params
* 參數
* @param charset
* 編碼,如HTTP.UTF_8
* @param isAutoRedirect
* 如果網頁回應狀態為轉導到新網頁,且Header的location有值,則自己以location所指網址取得內容
* @param isCloseConn
* 是否關閉連線
* @return 失敗回傳null,成功回傳網頁HTML
* @throws ClientProtocolException
* @throws IOException
*/
public String doSend(String urlpath, Method method,
List<NameValuePair> params, String charset, boolean isAutoRedirect,
boolean isCloseConn) throws ClientProtocolException, IOException {
return doSendBase(urlpath, method, ListToHttpEntity(params, charset),
charset, isAutoRedirect, isCloseConn);
}
/**
* 取得網頁內容
*
* @param urlpath
* 網址
* @param method
* Method.get or Method.post
* @param params
* 參數
* @param charset
* 編碼,如HTTP.UTF_8
* @param isAutoRedirect
* 如果網頁回應狀態為轉導到新網頁,且Header的location有值,則自己以location所指網址取得內容
* @param isCloseConn
* 是否關閉連線
* @return 失敗回傳null,成功回傳網頁HTML
* @throws ClientProtocolException
* @throws IOException
*/
public String doSend(String urlpath, Method method,
HashMap<String, String> params, String charset,
boolean isAutoRedirect, boolean isCloseConn)
throws ClientProtocolException, IOException {
return doSendBase(urlpath, method,
HashMapToHttpEntity(params, charset), charset, isAutoRedirect,
isCloseConn);
}
/**
* 取得網頁內容
*
* @param urlpath
* 網址
* @param method
* Method.get or Method.post
* @param params
* 參數
* @param charset
* 編碼,如HTTP.UTF_8
* @param isAutoRedirect
* 如果網頁回應狀態為轉導到新網頁,且Header的location有值,則自己以location所指網址取得內容
* @param isCloseConn
* 是否關閉連線
* @return 失敗回傳null,成功回傳網頁HTML
* @throws ClientProtocolException
* @throws IOException
*/
public String doSendBase(String urlpath, Method method, HttpEntity params,
String charset, boolean isAutoRedirect, boolean isCloseConn)
throws ClientProtocolException, IOException {
String responseBody = null;
HttpUriRequest httpgetpost = null;
statuscode = HttpStatus.SC_NO_CONTENT;
try {
if (httpclient == null || isClosedConn())
httpclient = new DefaultHttpClient();
if (proxy != null)
httpclient.getParams().setParameter(
ConnRoutePNames.DEFAULT_PROXY, proxy);
if(timeout!=null){
HttpParams param = httpclient.getParams();
HttpConnectionParams.setConnectionTimeout(param, timeout);
HttpConnectionParams.setSoTimeout(param, timeout);
}
if (method == Method.post) {
httpgetpost = new HttpPost(urlpath);
if (params != null) {
((HttpPost) httpgetpost).setEntity(params);
}
} else {
if (params != null) {
urlpath += "?"
+ HTTPParseFunc.inputStream2String(params.getContent(), charset);
}
httpgetpost = new HttpGet(urlpath);
}
HttpResponse response = httpclient.execute(httpgetpost);
statuscode = response.getStatusLine().getStatusCode();
if ((statuscode == HttpStatus.SC_MOVED_TEMPORARILY)
|| (statuscode == HttpStatus.SC_MOVED_PERMANENTLY)
|| (statuscode == HttpStatus.SC_SEE_OTHER)
|| (statuscode == HttpStatus.SC_TEMPORARY_REDIRECT)) {
Header header = response.getFirstHeader("location");
if (header != null) {
newuri = header.getValue();
if ((newuri == null) || (newuri.equals("")))
newuri = "/";
if (isAutoRedirect) {
httpgetpost.abort();
httpgetpost = null;
responseBody = doSendBase(newuri, Method.get, null,
charset, true, false);
}
}
} else if (statuscode == HttpStatus.SC_OK) {
responseBody = HTTPParseFunc.inputStream2String(response.getEntity()
.getContent(), charset);
}
} catch (ClientProtocolException e) {
throw e;
} catch (IOException e) {
throw e;
} catch (Exception e) {
e.printStackTrace();
} finally {
if (httpgetpost != null) {
httpgetpost.abort();
httpgetpost = null;
}
closeConn(isCloseConn);
}
return responseBody;
}
/**
* List<NameValuePair>轉為HttpEntity
*
* @param nvps
* @param charset
* 編碼,如HTTP.UTF_8
* @return
* @throws UnsupportedEncodingException
*/
public static HttpEntity ListToHttpEntity(List<NameValuePair> nvps,
String charset) throws UnsupportedEncodingException {
HttpEntity result = null;
if (nvps != null && nvps.size() > 0) {
result = new UrlEncodedFormEntity(nvps, charset);
}
return result;
}
/**
* String to HttpEntity(
*
* @param nvps
* @param charset
* 編碼,如HTTP.UTF_8
* @return
* @throws UnsupportedEncodingException
*/
public static HttpEntity StringToHttpEntity(String nvps, String charset)
throws UnsupportedEncodingException {
HttpEntity result = null;
if (nvps != null) {
StringEntity reqEntity = new StringEntity(nvps, charset);
reqEntity.setContentType("application/x-www-form-urlencoded");
result = reqEntity;
}
return result;
}
/**
* HashMap To HttpEntity
*
* @param nvps
* @param charset
* 編碼,如HTTP.UTF_8
* @return
* @throws UnsupportedEncodingException
*/
public static HttpEntity HashMapToHttpEntity(HashMap<String, String> nvps,
String charset) throws UnsupportedEncodingException {
HttpEntity result = null;
if (nvps != null) {
result = new StringEntity(HTTPParseFunc.hashMapToString(nvps, charset), charset);
try {
result = StringToHttpEntity(HTTPParseFunc.inputStream2String(result
.getContent(), charset), charset);
} catch (IllegalStateException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
return result;
}
}
使用範例程式
package common.test;
import static org.junit.Assert.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import org.apache.http.NameValuePair;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.protocol.HTTP;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import common.HTTPBaseIO;
import common.control.HTTPParseFunc;
public class HTTPBaseIOTest {
@Before
public void setUp() throws Exception {
}
@After
public void tearDown() throws Exception {
}
@Test
public void testDoGet() {
String urltest = "http://www.google.com.tw";
String charset = "UTF-8";
HTTPBaseIO.Method method = HTTPBaseIO.Method.get;
HTTPBaseIO reqClient = new HTTPBaseIO();
try {
String html = reqClient.doSendBase(urltest, method, null, charset,
false, false);
System.out.println(html);
if (html == null)
fail("Client get nothing");
} catch (Exception e) {
e.printStackTrace();
} finally {
reqClient.closeConn();
}
}
@Test
public void testDoPost() {
String urlserv1 = "http://localhost/Test1/Serv1";
String urlserv2 = "http://localhost/Test1/Serv2";
String charset = HTTP.UTF_8;
HTTPBaseIO.Method method = HTTPBaseIO.Method.post;
HTTPBaseIO reqClient = new HTTPBaseIO();
try {
HashMap<String, String> map = new HashMap<String, String>();
map.put("jobname", "login");
map.put("id", "aaa");
map.put("pswd", "1234");
String html = reqClient.doSend(urlserv1, method, map, charset,
false, false);
System.out.println(html);
ArrayList<String[]> arr = HTTPParseFunc.paramToArray(html);
for (int i = 0; i < arr.size(); i++)
System.out.println(arr.get(i)[0] + "=" + arr.get(i)[1]);
if (html == null)
fail("Login get nothing");
String param = "jobname=getname";
html = reqClient.doSend(urlserv1, method, param, charset, false,
false);
System.out.println(html);
if (html == null)
fail("getname get nothing");
List<NameValuePair> nvps = new ArrayList<NameValuePair>();
nvps.add(new BasicNameValuePair("jobname", "loadserv2"));
nvps.add(new BasicNameValuePair("name", "aaa"));
reqClient.resetNewuri();
html = reqClient.doSend(urlserv1, method, nvps, charset, true,
false);
if (html == null)
fail("loadserv2 get nothing");
nvps.clear();
if (reqClient.getNewuri() != null && html != null) {
nvps.add(new BasicNameValuePair("jobname", "getname2"));
html = reqClient.doSend(urlserv2, method, nvps, charset, false,
false);
System.out.println(html);
if (html == null)
fail("Name get nothing");
nvps.clear();
}
nvps.clear();
nvps.add(new BasicNameValuePair("jobname", "Logout"));
html = reqClient.doSend(urlserv1, method, nvps, charset, false,
false);
System.out.println(html);
if (html == null)
fail("Logout get nothing");
nvps.clear();
} catch (Exception e) {
e.printStackTrace();
} finally {
reqClient.closeConn();
}
}
}
總結
在範例的地方我假設了兩個servlet用來接收傳遞的參數,這部份可能不能直接執行,不過函式我測試過是沒問題的。
使用時可選擇用Hashmap、String、或是List作為參數設定,我分別在範例中都使用過了。
轉跳時可用getNewuri函式來確認是否有取得轉跳的網址,用回傳的html來確認是否有抓到回應的內容。
PS.我發現了Apache提供的這個Client在Windows下會有連線數上限的問題(聽說好像調整XP連線數可以解決)。
使用時可選擇用Hashmap、String、或是List作為參數設定,我分別在範例中都使用過了。
轉跳時可用getNewuri函式來確認是否有取得轉跳的網址,用回傳的html來確認是否有抓到回應的內容。
PS.我發現了Apache提供的這個Client在Windows下會有連線數上限的問題(聽說好像調整XP連線數可以解決)。