顯示具有 Apache 標籤的文章。 顯示所有文章
顯示具有 Apache 標籤的文章。 顯示所有文章

[Java]Http Get與Post函式(Apache HttpClient)

序言

要達到基本的Http get與post方法取得網站內容,Java內的URLConnection就可以達成。
但我實作時發現它太過於底層,以致於我不知該如何做到關於Session狀態的延續,也就是不能夠在完成登入後,保留住登入狀態然後進入下個動作。
於是就尋求別的作法,最後發現了Apache有提供了HttpClient套件的JAR檔,能讓我們用更簡單的方式達成工作。
在此參考網路上的文章:用HttpClient來模擬瀏覽器GET,POST,加上可自動轉導的功能,包成函式。
另外再加上可用三種類型來設定傳遞的參數,並控制其連線與中斷狀態。

下載套件

到HttpClient Download下載套件,選擇Binary with dependencies的版本(如4.0.1.zip),
我們需要的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("&amp;")) {
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) + "&amp;" + 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連線數可以解決)。

[初學]PHP入門心得

序言

雖然寫了許久的程式,但一直沒花時間去練習PHP。
最近花了點時間試試PHP,增長了些見聞,後續幾篇會一一記錄下我開發時注意到的事。

開發環境

  • Windows 2003
  • TWAMPs(Tiny Windows Apache MySQL PHP Portable Web Server)V1.3.3中文版
    我開發的時候雖然已經有V1.3.5版,但執行的時候不知道為什麼我的整個作業系統會出現很多衝突的現象,很多程式都受影響,所以就往回推用舊的版本。V1.3.4版無法下載,就改用V1.3.3。以下是這個免安裝的環境提供的版本:
    • Apache:2.2.11
    • PHP:5.2.10
    • MySQL:5.1.36-community
    • phpMyAdmin:3.2.0.1
    • Drupal:6.13
  • Notepad++ 5.4.4
    我沒有特地找PHP的編輯器,所以編輯起來比較費工,不過Notepad還是有用顏色標亮特定的語法,所以還是有點用的。

環境設定

  • 啟動前可以看一下根目錄的【README.txt】來找到對應的Port或其他值的更改地點,但有些部分【README.txt】沒有寫的很正確,所以可能還是要靠經驗與人工找找實際的目錄,如:
    •  Apache PORT更改,README.txt說【edit httpd.conf in TWAMP\Apache\conf】,但實際路徑會在【 TWAMPd\app\conf】下的httpd.conf,找到並更改【Listen 80】
    • php.ini的位置,README.txt說【Edit php.ini in TWAMPd\PHP】,但實際路徑會在【TWAMPd\app\bin】
    • 【TWAMPd\htdocs】目錄下會是所有網頁文件所在的地方。

Apache 目錄存取限制設定

【.htaccess】 是一個文字檔,可以自己建立後,放在要限制/保護的目錄下,Apache就會依裡面的設定內容對使用者的存取進行限制或保護。
例如我對目錄要完全不給人進來存取檔案,就在文字檔內寫入以下內容

order allow,deny
deny from all

基本上這只是最簡單的功能,但很常用。如果需要更多設定,其實這個檔也可以設定的很複雜,網路上還有人專門做出設定工具:htaccessEditor,在此就不詳述了。

PHP開發兩三事

  • 基本語法

    <?php echo "123"; ?>

    【<?php ... ?>】指的是裡面的code【...】是php,以副檔名為*.php的檔案放在【TWAMPd\htdocs】目錄下,就可以被編譯執行。【echo】會將後面帶的文字顯示在頁面這段程式所在的位置上。→官方文件-基本語法、官方文件-echo
  • include

    <?php
    include 'myClass/HTMLParser.php';
    ?>

    include 的寫法,會將include的PHP程式如同嵌入在宣告的位置一樣,我想使用方法應該就如同C一樣,欲使用前必先宣告。→官方文件
  • 變數、單引號與雙引號

    <?php
        $value="123";
        echo "1: " . $value . "\r\n<br>";
        echo "2: " . "abc\t$valuedef\r\n<br>";
        echo "3: " . 'abc$valuedef\r\n<br>';
        echo "4: " . "abc\t$value def\r\n<br>";
        echo "5: " . 'abc$value def \r\n<br>';
        echo "6: " . 'abc' . $value . 'def\r\n<br>';
        echo "7: " . '"' . $value . "'\r\n<br>";
    ?>

    結果:

    1: 123
    <br>2: abc    
    <br>3: abc$valuedef\r\n<br>4: abc    123 def
    <br>5: abc$value def \r\n<br>6: abc123def\r\n<br>7: "123'
    <br>

    上面的程式中可以看到幾個現象:變數的前面用【$】來標示它是個變數,而且不用先行宣告,隨使用就會產生。以下逐行來看
    1. 字串的組合是用點【.】來標示兩字串要串聯起來。【雙引號】中的特殊文元【\】標示了後面帶了特殊字元【\r\n】,這都是換行符號,如果用VB來形容就是【vbCrLf】,這個特殊字元可以單獨使用,如【\n】。(PS:按「Enter」鍵實際產生兩個字元換行和回到開頭(ASCII編碼10:\n和13:\r))
    2. 【雙引號】中的變數被編譯成顯示,但因為英文字連再一起,所以編譯後是顯示【valuedef】變數的值。因為根本沒有宣告【valuedef】變數的值,所以最後產生的時候是空值,就什麼都沒有。
    3. 【單引號】直接輸出文字內容,所以特殊字元或變數都無效。
    4. 【雙引號】中的變數被編譯成顯示,此字串內容中,還有用到特殊字元【\t】來出現tab的字元。
    5. 【單引號】直接輸出文字內容,所以特殊字元或變數都無效。
    6. 【單引號】直接輸出文字內容,所以特殊字元或變數都無效。
    7. 雙引號內容中可以出現單引號,反過來說也行,如果雙引號內容中要出現雙引號,需要加上特殊字元\。
    PHP 會解譯【雙引號】內容中的變數,而【單引號】則視為純字串出,PHP 不會再處理單引號內的內容。所以單引號與雙引號在使用時會有時機上的差異。
  • 常數

    <?php
        define('myValue','1234');
        echo myValue;
    ?>

    常數的寫法與變數不同,定義後名稱前面不需要再加上【$】號。→官方文件
  • 脫逸 (Escape)字

    <?php
        //去除get_magic_quotes_gpc on 時 針對特殊字做脫逸 (Escape)的影響
        if (get_magic_quotes_gpc())
        {   
            $HTTP_GET_VARS = array_map('stripslashes', $HTTP_GET_VARS);
            $HTTP_POST_VARS = array_map('stripslashes', $HTTP_POST_VARS);
            $HTTP_COOKIE_VARS = array_map('stripslashes', $HTTP_COOKIE_VARS);
            $_GET = array_map('stripslashes', $_GET);
            $_POST = array_map('stripslashes', $_POST);
            $_COOKIE = array_map('stripslashes', $_COOKIE);
            $_REQUEST = array_map('stripslashes', $_REQUEST);
        }
    ?>

    這個部份是比較與執行環境設定有關的。在PHP.ini中有個設定【get_magic_quotes_gpc on】(官方文件),可以讓使用者輸入的字元中一些特殊的字元加上【\】,如單引號【'】、雙引號【"】、斜線【\】,以防程式處理時會造成意想不到的錯誤。但這樣的設定可能反而會讓我們在處理資料時更加棘手,所以這串程式能判斷當這個設定開啟時,讓這些特殊的字元不經過加上【\】的畜哩,直接取得使用者輸入的內容。
相關連結
  • TWAMP:免安裝的中英文伺服器執行環境。
  • PHP官方開發手冊:英文開發手冊。
  • W3School:簡體中文的網站開發程式語言文件,雖然有些不夠完整,不過很適合當工具書或初學參考(除了PHP,其他還有許多網頁語言)。
這裡是關於技術的手札~

也歡迎大家到

倫與貓的足跡

或

到噗浪來

關心一下我唷!
by 倫
 
Copyright 2009 倫倫3號Beta-Log All rights reserved.
Blogger Templates created by Deluxe Templates
Wordpress Theme by EZwpthemes