2010年11月27日 星期六

[轉載] Apache HttpClient 4.x 使用 GET, POST 範例

先前我曾經發了一篇介紹如何抓取網頁的教學:
利用Jakarta.Commons.HttpClient抓取網頁、網站(Parser),不過當時用的是 HttpClient 3.1。
經過時間的演進 Apache 已經在 14 August 2009 發佈HttpComponents HttpClient 4.0 (GA)
由 3.1 到 4.0 因為底層幾乎全部重新改寫,所以也使有些舊的程式無法使用。
這篇就是我自己寫的一個簡單範例。

在看範例之前先把一些重要連結整理給大家:
想知道這次到底更動了哪些東西可以看:Apache HttpClient 首頁
官方的 Tutorial 在:Apache HttpClient Tutorial
而 API DOC、說明文件則在:Apache HttpClient apidocs

相關的程式碼、jar 檔在:HttpComponents HttpClient 4.0 (GA)
注意,在寫程式前必需先將四個 jar 檔正確匯入,最後兩個(*)是選用,
請參考:http://hc.apache.org/httpcomponents-client-ga/quickstart.html

  • commons-logging-x.x.x.jar
  • commons-codec-x.x.x.jar
  • httpcore-x.x.x.jar
  • httpclient-x.x.x.jar
  • apache-mime4j-x.x.x.jar (*)
  • httpmime-x.x.x.jar (*)
說了這麼多,以下是程式的範例,
第一個是傳回在 google 查詢 httpclient 的結果。
第二則是傳回台大圖書館查詢 Head First Java 的結果。

 package demo.httpclient;

import java.io.IOException;
import java.util.ArrayList;

import org.apache.commons.httpclient.HttpStatus;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.utils.URLEncodedUtils;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;

/**
* Apache HttpClient 4.x 使用 GET, POST 查詢網頁的範例
*
* @author werdna at http://werdna1222coldcodes.blogspot.com/
*/

public class HttpClientDemo extends DefaultHttpClient {

public static void main(String[] args) throws IOException {

DefaultHttpClient demo = new DefaultHttpClient();
demo.getParams().setParameter("http.protocol.content-charset", "UTF-8");

// Get Request Example,取得 google 查詢 httpclient 的結果
HttpGet httpGet = new HttpGet("http://www.google.com.tw/search?q=httpclinet");
HttpResponse response = demo.execute(httpGet);
String responseString = EntityUtils.toString(response.getEntity());
if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
// 如果回傳是 200 OK 的話才輸出
System.out.println(responseString);
} else {
System.out.println(response.getStatusLine());
}

// Post Request Example,查詢台大圖書館書籍
ArrayList<NameValuePair> pairList = new ArrayList<NameValuePair>();
pairList.add(new BasicNameValuePair("searchtype", "t"));
pairList.add(new BasicNameValuePair("searchscope", "keyword"));
pairList.add(new BasicNameValuePair("searcharg", "Head First Java"));
pairList.add(new BasicNameValuePair("SORT", "D"));

HttpPost httpPost = new HttpPost("http://tulips.ntu.edu.tw:1081/search*cht/a?");
StringEntity entity = new StringEntity(URLEncodedUtils.format(pairList, "UTF-8"));
httpPost.setEntity(entity);
response = demo.execute(httpPost);
responseString = EntityUtils.toString(response.getEntity());
if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
// 如果回傳是 200 OK 的話才輸出
System.out.println(responseString);
} else {
System.out.println(response.getStatusLine());
}
}
}
更多的程式範例可以參考其教學: 
HttpClient 4 HTTP request 
HttpClient 4 使用POST方式提交普通表單數據的例子

關鍵字:Apache、HttpClient、4、教學、示範、範例、設定
參考資料:
  1. Apache HttpClient 首頁
  2. HttpComponents HttpClient 4.0 (GA)
  3. Apache HttpClient Tutorial
  4. Apache HttpClient apidocs
  5. HttpClient 4 HTTP request
  6. HttpClient 4 使用POST方式提交普通表單數據的例子 

2010年11月16日 星期二

[轉載] JSON傳送與接收


原文

XML可用來表現階層性資料,然而建立與處理XML DOM較為複雜
在Web應用程式中,許多時候並不需要用到XML的複雜階層性
此時通常會採用JSON作為資料交換格式
而JSON也正成為Web應用程式中交換資料的主要選擇。

JSON全名JavaScript Object Notation,為彷造JavaScript的物件實字(Object literal)格式而來
你可以在 http://www.json.org/ 找到詳細的JSON格式說明
大致而言,與物件實字格式類似,主要注意的是JSON:
作為名稱的部份,必須用"雙引號包括
作為值的部份,若為字串,必須用"雙引號包括
不支援函式表示

舉個例子來說,下面是個物件實字:
var obj = {
    name : 'Justin',
    age : 35,
    childs : [ { name : 'hamimi', age : 3} , { name : 'another', age : 5}]
};

若使用JSON表示,則是如下:
var json = '{"name":"Justin","age":35,"childs":[{"name":"hamimi","age":3}, { "name" : "another", "age" : 5}]}';

若為排版會比較容易觀察:
{
    "name":"Justin",
    "age":35,
    "childs":[
        {
            "name" : "hamimi",
            "age" : 3
        },

       {
            "name" : "another",
            "age" : 5
        }

    ]
}

你可以傳送JSON字串給伺服端,要建立JSON字串很簡單
在Firefox 3.1、Internet Explorer 8以上,支援簡單的JSON處理程式庫
如果要從JavaScript建立JSON字串,只要使用JSON.stringify(),例如:
var obj = {
    name : 'Justin',
    age : 35,
    childs : [ { name : 'hamimi', age : 3}, { "name" : "another", "age" : 5} ]
};
var json = JSON.stringify(obj);

這樣就可以得到方才所示範的JSON字串,如果要用非同步物件傳送JSON
則可以如下:
var request = xhr();     // xhr() 會建立非同步物件
request.onreadystatechange = handleStateChange;  // handleStateChange 參考至函式
request.open('POST', url);
request.setRequestHeader('Content-Type', 'application/json');
request.send(json);

請求標頭'Content-Typ'建議設為'application/json'
當然,伺服端要能夠剖析JSON字串以取出資料,但無需親自撰寫
http://www.json.org/ 網站中提供許多語言實作的JSON剖析器協助剖析JSON取得結果

伺服端可以傳回JSON字串
可以使用eval()將JSON字串計值(evaluate)為JavaScript物件
例如:
var obj = eval(json);

然而eval()也會運算傳入的JavaScript程式碼,因此並不建議直接用eval()
如果只是要計值JSON為JavaScript物件,可以使用JSON.parse()。例如:
var obj = JSON.parse(json);

如果是Firefox 3.1、Internet Explorer 8以外沒有內建JSON支援的瀏覽器
可以在 http://www.json.org/ 下載 json2.js
將之包括在網頁中,即可獲得上述的相關JSON方法:
<script type="text/javascript" src="json2.js">
如果遇到瀏覽器已內建JSON支援,json2.js什麼都不作

2010年11月12日 星期五

[轉載] javascript :onsubmit=return false阻止form表單提交

return false 阻止表單提交不起今天這個問題困擾了我很久,在網上找了很多資料,基本上關於onsubmit=return false有以下幾點要注意的地方:

1. return 的返回值問題,函數中return一旦有返回值,就不在執行下面的語句,直接跳到函數調用 的地方。如下PHP函數代碼,第一個if條件符合則函數值返回布爾型false,可以返回一個函數的值,並且跳出這個函數;只要遇到return語句,程序就在那一行代碼停止執行,執行控制將立刻返回到調用該程序的代碼處。
 function chkinput(form)
 {
   if(form.title.value=="")
    {
      alert("請輸入文章標題!");
   form.title.select();
   return false;              //注意不能寫成 return(false);
    }

   if(form.content.value=="")
    {
      alert("文章正文不能為空@!!");
   form.content.select();
   return false;
    }

    return true;
 }

2.form的onsubmit屬性的觸發問題,onsubmit 事件什麼時候觸發?onsubmit 事件會在表單中的確認按鈕被點擊時發生。不觸發的原因有一般如下:
A. onsubmit屬性的觸發時機是在form用input:submit這樣的button提交時才會觸發,否則不會觸發。如果是用一個普通input:button,則在onclick屬性中指定一個javascript函數,在這個函數裡面再執行form的submit()函數,而不是onsubmit屬性。
B. 先看一段代碼:
<form action="index.jsp" method="post" onsubmit="submitTest();">

<INPUT value="www">

<input type="submit" value="submit">

</form>
<SCRIPT LANGUAGE="JavaScript">

<!--

function submitTest() {

// 一些邏輯判斷return false;

}

//--></SCRIPT>
點擊submit按鈕該表單並未提交。因為有一處應該改為(紅色字體)
<form action="index.jsp" method="post" onsubmit="return submitTest();">
原來onsubmit屬性就像是<form>這個html對象的一個方法名,其值(一字符串)就是其方法體,默認返回true;
和Java一樣,在該方法體中你可以寫任意多個語句,包括內置函數和自定義函數。
在這裡submitTest()雖然返回false,但我們只執行了此函數,沒有對其結果進行任何處理。
onsubmit="return submitTest()利用到了它的返回值,達到了預期效果。
 
3.事件處理函數返回false的問題,在大多數情況下,為事件處理函數返回false,可以防止默認的事件行為.
例如,默認情況下點擊一個<a>元素,頁面會跳轉到該元素href屬性指定的頁.   
Return False 就相當於終止符,Return True 就相當於執行符。  
在js中return false的作用一般是用來取消默認動作的。比如你單擊一個鏈接除了觸發你的  
onclick時間(如果你指定的話)以外還要觸發一個默認事件就是執行頁面的跳轉。所以如果  
你想取消對象的默認動作就可以return false。return false應用比較多的場合有:  

<body>  
1, <a href="a.JSP" mce_href="a.JSP" onclick=test();>超級鏈接 </a>  
2, <input type="button" onclick=test() value="提交">  
3, <form name="form1" onsubmIT="return test();">  
內容  
<input type="submIT" value="提交">  
</form>  
</body>  

    
<input type="submit" onclick="submitAction(); return false;" />  
submitAction 方法裡面有提交表單的動作。如果不加 return false,
在執行完 submitAction 之後,submit 按鈕還會繼續執行它的默認事件,
就會再次提交表單。這可能就是很多錯誤的根源。   
的確,return false的含義不是阻止事件繼續向頂層元素傳播,
而是阻止瀏覽器對事件的默認處理。你可以這樣試驗:
首先將所有的js腳本註釋掉,在IE瀏覽器中嘗試拖動一下圖片,
你會發現鼠標會成為禁止操作的樣式,圖片是被禁止拖動的,
它是瀏覽器針對mousemove事件所提供的默認行為。
return false就是為了去掉這種行為,否則就會出現你描述的中斷事件連續執行。   
另外,和return false等效的語句為:
window.event.returnValue = false,
你可以把return false替換為此語句並進行驗證。   
最後說明一下,此種方式只適用於IE瀏覽器。
在js中return false的作用一般是用來取消默認動作的。比如你單擊一個鏈接除了觸發你的 
onclick時間(如果你指定的話)以外還要觸發一個默認事件就是執行頁面的跳轉。所以如果
你想取消對象的默認動作就可以return false。return false應用比較多的場合有:
<form name="form1"  onsubmit="return youfunction();">...... </form>
<a href="www.***.com" onclick="...;return false;">dddd </a>

2010年11月7日 星期日

自訂編碼標籤範例

如果不將Eclipse的環境編碼改成UTF-8,似乎轉碼會有問題
因應這種情況寫了自訂標籤來協助處理

encrypt.tld=========

<?xml version="1.0" encoding="UTF-8" ?>
<taglib xmlns="http://java.sun.com/xml/ns/j2ee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-jsptaglibrary_2_0.xsd"
    version="2.0">

    <tlib-version>1.2</tlib-version>            
    <short-name>encryptlib</short-name>    
    <uri>encrypt-tags</uri>                              
    <tag>
      <description>URL  get/post encrypting</description>
      <name>encrypt</name>
      <tag-class>tw.vencs.EncrptTag</tag-class>
      <body-content>empty</body-content>
    
      <attribute>
          <name>encryptValue</name>
          <required>true</required>
          <rtexprvalue>true</rtexprvalue>
      </attribute>
    </tag>

</taglib>

EncrptTag.java===============

package tw.vencs;

import javax.servlet.jsp.tagext.SimpleTagSupport;
import javax.servlet.jsp.JspException;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;

public class EncrptTag extends SimpleTagSupport{
    private String encryptValue; 
   
    public void doTag() throws JspException { 
        try { 
            encryptValue = new String(encryptValue.getBytes("iso-8859-1"),"UTF-8"); 
            getJspContext().getOut().write(encryptValue);
        } catch (IOException e) { 
              Logger.getLogger(EncrptTag.class.getName()).log(Level.SEVERE, null, e);
              throw new RuntimeException();
        }    
    } 
    public void setEncryptValue(String encryptValue) { 
        this.encryptValue = encryptValue; 
    } 
}

Demo.jsp===============

.....
<%@ taglib prefix="e" uri="encrypt-tags" %>
.....
<body>
.....
....=<e:encrypt  encryptValue="${param.itemName}"/>
</body>
.....

2010年11月5日 星期五

[轉錄]Tomcat Post / Get 中文編碼處理方法 (中文亂碼問題)

最近在把原本佈署在 Resin 的 Java Web Application移植到 Tomcat 時,有許多原本正常的中文get / post 功能,都變的異常,只要是透過 get / post 得到的資料,就會變亂碼
剛好也驗證了 Resin 在Character Encoding的部分做的較好
而看看網路上大家的問題,也可以知道 Tomcat 在編碼處理的部分 真是讓很多人頭痛

主要的原因是,TOMCAT 預設都是用 ISO-8859-1 的編碼方式來傳遞資訊
這個問題,可以解決的方式整理如下:

1. JSP 頁面的編碼宣告需與實際儲存檔案時用的編碼一致

這是個很好玩的問題~ 有些人 在頁面宣告用 big5 or UTF-8 or... 字集
但是,檔案實際儲存的方式與該編碼不同,則 頁面當然會出現亂碼的問題

2. 自行轉碼
form method=POST or GET 時 因為Tomcat預設編碼是ISO-8859-1的關係
直接用 <%=request.getParameter("firstname")%> 取值,中文字會變亂碼
(你傳的明明是UTF-8, 但是讀取時 確用 ISO-8859-1來解譯.. 當然有問題囉)
要使用 <%=new String(request.getParameter("firstname").getBytes( "ISO-8859-1"), "UTF-8")%>這種方式來轉換編碼才會正常 (假設,頁面用的編碼為UTF-8時)

3. 透過設定 server.xml URIEncoding的方式 來處理 GET 亂碼問題
在 tomcat 的設定檔內 ./conf/server.xml 修改下列設定的話,則透過GET的方式傳遞參數
的話,Tomcat會用你指定的編碼方式來對待用get方法傳遞的參數,而不是使用預設的ISO-8859-1 (get 的方式是透過 url parameter傳遞參數)
但是,因為只處理 URIEncoding, 所以若使用 Post 傳遞,還是會有問題
URIEncoding: This specifies the character encoding used to decode the URI bytes, after %xx decoding the URL. If not specified, ISO-8859-1 will be used.

P.S. 若使用 GET Method, 且設定了URIEncoding 的話就不用再做上述二的自行轉碼動作,如果多加該處理,一樣會有亂碼的問題

P.S.2  如果整合apache和Tomcar的話,需要將兩者溝通用的8009 port也設置URIEncoding

4. 使用 request.setCharacterEncoding 來處理 GET / POST 的亂碼問題
在處理的頁面,加上下列的處理即可,明確的告訴Tomcat request的編碼為何,不要一廂情願的使用ISO-8859-1來解讀~

<%request.setCharacterEncoding("UTF-8");%>

javax.servlet.servletRequest.setCharacterEncoding(string env)
Overrides the name of the character encoding used in the body of this request. This method must be called prior to reading request parameters or reading input using getReader(). Otherwise, it has no effect.

5. 使用 filter 來處理 GET / POST 的亂碼問題
這個方法是最方便的,只要在 web.xml 裡面 透過 filter 的設定,讓每個網頁都能
透過 filter 的處理一體適用所有編碼問題的解決
方法如下:

a. 目前 Tomcat 提供的範例裡面,就有現成的 Encoding Filter, 位置如下:
.\Tomcat 6.0\webapps\examples\WEB-INF\classes\filters\SetCharacterEncodingFilter.class

b. 所以,只要把該 class 檔,copy 到你的 web application 的
.\WEB-INF\classes\filter 下,再搭配 web.xml 的filter 設定,
就可以讓所有符合 url pattern 的網頁,都能自動apply所想要的編碼設定(透過 encoding這個 parameter來設定),是不是很方便呢 ?
(這個功能 就像是自動把上述第四點的 setCharacterEncoding 語句 自動apply到各頁面囉)

------------
以上~ 就從 Resin Porting 到 Tomcat 的經驗,當然是使用 filter 的方式 一次解決~ 不然的話 就得每個網頁每個網頁去改囉~

延伸資料:
FAQ/CharacterEncoding
http://jim.blogsome.com/2005/05/27/jsp-chinese-character-solution/
http://www.javaworld.com.tw/confluence/pages/viewpage.action?pageId=752