HttpClient:是一個接口php
首先須要先建立一個DefaultHttpClient的實例html
HttpClient httpClient=new DefaultHttpClient();java
發送GET請求:android
先建立一個HttpGet對象,傳入目標的網絡地址,而後調用HttpClient的execute()方法便可:數據庫
HttpGet HttpGet=new HttpGet(「http://www.baidu.com」);apache
httpClient.execute(httpGet);編程
發送POST請求:swift
建立一個HttpPost對象,傳入目標的網絡地址:瀏覽器
HttpPost httpPost=new HttpPost(「http://www.baidu.com」);tomcat
經過一個NameValuePair集合來存放待提交的參數,並將這個參數集合傳入到一個UrlEncodedFormEntity中,而後調用HttpPost的setEntity()方法將構建好的UrlEncodedFormEntity傳入:
List<NameValuePair>params=newArrayList<NameValuePair>();
Params.add(new BasicNameValuePair(「username」,」admin」));
Params.add(new BasicNameValuePair(「password」,」123456」));
UrlEncodedFormEntity entity=newUrlEncodedFormEntity(params,」utf-8」);
httpPost.setEntity(entity);
調用HttpClient的execute()方法,並將HttpPost對象傳入便可:
HttpClient.execute(HttpPost);
執行execute()方法以後會返回一個HttpResponse對象,服務器所返回的全部信息就保護在HttpResponse裏面.
先取出服務器返回的狀態碼,若是等於200就說明請求和響應都成功了:
If(httpResponse.getStatusLine().getStatusCode()==200){
//請求和響應都成功了
HttpEntityentity=HttpResponse.getEntity();//調用getEntity()方法獲取到一個HttpEntity實例
Stringresponse=EntityUtils.toString(entity,」utf-8」);//用EntityUtils.toString()這個靜態方法將HttpEntity轉換成字符串,防止服務器返回的數據帶有中文,因此在轉換的時候將字符集指定成utf-8就能夠了
}
Http協議的重要性相信不用我多說了,HttpClient相比傳統JDK自帶的URLConnection,增長了易用性和靈活性(具體區別,往後咱們再討論),它不只是客戶端發送Http請求變得容易,並且也方便了開發人員測試接口(基於Http協議的),即提升了開發的效率,也方便提升代碼的健壯性。所以熟練掌握HttpClient是很重要的必修內容,掌握HttpClient後,相信對於Http協議的瞭解會更加深刻。
1、簡介
HttpClient是Apache Jakarta Common下的子項目,用來提供高效的、最新的、功能豐富的支持HTTP協議的客戶端編程工具包,而且它支持HTTP協議最新的版本和建議。HttpClient已經應用在不少的項目中,好比Apache Jakarta上很著名的另外兩個開源項目Cactus和HTMLUnit都使用了HttpClient。
下載地址: http://hc.apache.org/downloads.cgi
2、特性
1. 基於標準、純淨的java語言。實現了Http1.0和Http1.1
2. 以可擴展的面向對象的結構實現了Http所有的方法(GET, POST, PUT, DELETE, HEAD, OPTIONS, and TRACE)。
3. 支持HTTPS協議。
4. 經過Http代理創建透明的鏈接。
5. 利用CONNECT方法經過Http代理創建隧道的https鏈接。
6. Basic, Digest, NTLMv1, NTLMv2, NTLM2 Session, SNPNEGO/Kerberos認證方案。
7. 插件式的自定義認證方案。
8. 便攜可靠的套接字工廠使它更容易的使用第三方解決方案。
9. 鏈接管理器支持多線程應用。支持設置最大鏈接數,同時支持設置每一個主機的最大鏈接數,發現並關閉過時的鏈接。
10. 自動處理Set-Cookie中的Cookie。
11. 插件式的自定義Cookie策略。
12. Request的輸出流能夠避免流中內容直接緩衝到socket服務器。
13. Response的輸入流能夠有效的從socket服務器直接讀取相應內容。
14. 在http1.0和http1.1中利用KeepAlive保持持久鏈接。
15. 直接獲取服務器發送的response code和 headers。
16. 設置鏈接超時的能力。
17. 實驗性的支持http1.1 response caching。
18. 源代碼基於Apache License 可免費獲取。
3、使用方法
使用HttpClient發送請求、接收響應很簡單,通常須要以下幾步便可。
1. 建立HttpClient對象。
2. 建立請求方法的實例,並指定請求URL。若是須要發送GET請求,建立HttpGet對象;若是須要發送POST請求,建立HttpPost對象。
3. 若是須要發送請求參數,可調用HttpGet、HttpPost共同的setParams(HetpParams params)方法來添加請求參數;對於HttpPost對象而言,也可調用setEntity(HttpEntity entity)方法來設置請求參數。
4. 調用HttpClient對象的execute(HttpUriRequest request)發送請求,該方法返回一個HttpResponse。
5. 調用HttpResponse的getAllHeaders()、getHeaders(String name)等方法可獲取服務器的響應頭;調用HttpResponse的getEntity()方法可獲取HttpEntity對象,該對象包裝了服務器的響應內容。程序可經過該對象獲取服務器的響應內容。
6. 釋放鏈接。不管執行方法是否成功,都必須釋放鏈接
4、實例
[java] view plain copy
- package com.test;
-
- import java.io.File;
- import java.io.FileInputStream;
- import java.io.IOException;
- import java.io.UnsupportedEncodingException;
- import java.security.KeyManagementException;
- import java.security.KeyStore;
- import java.security.KeyStoreException;
- import java.security.NoSuchAlgorithmException;
- import java.security.cert.CertificateException;
- import java.util.ArrayList;
- import java.util.List;
-
- import javax.net.ssl.SSLContext;
-
- import org.apache.http.HttpEntity;
- import org.apache.http.NameValuePair;
- import org.apache.http.ParseException;
- import org.apache.http.client.ClientProtocolException;
- import org.apache.http.client.entity.UrlEncodedFormEntity;
- import org.apache.http.client.methods.CloseableHttpResponse;
- import org.apache.http.client.methods.HttpGet;
- import org.apache.http.client.methods.HttpPost;
- import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
- import org.apache.http.conn.ssl.SSLContexts;
- import org.apache.http.conn.ssl.TrustSelfSignedStrategy;
- import org.apache.http.entity.ContentType;
- import org.apache.http.entity.mime.MultipartEntityBuilder;
- import org.apache.http.entity.mime.content.FileBody;
- import org.apache.http.entity.mime.content.StringBody;
- import org.apache.http.impl.client.CloseableHttpClient;
- import org.apache.http.impl.client.HttpClients;
- import org.apache.http.message.BasicNameValuePair;
- import org.apache.http.util.EntityUtils;
- import org.junit.Test;
-
- public class HttpClientTest {
-
- @Test
- public void jUnitTest() {
- get();
- }
-
- /**
- * HttpClient鏈接SSL
- */
- public void ssl() {
- CloseableHttpClient httpclient = null;
- try {
- KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
- FileInputStream instream = new FileInputStream(new File("d:\\tomcat.keystore"));
- try {
- // 加載keyStore d:\\tomcat.keystore
- trustStore.load(instream, "123456".toCharArray());
- } catch (CertificateException e) {
- e.printStackTrace();
- } finally {
- try {
- instream.close();
- } catch (Exception ignore) {
- }
- }
- // 相信本身的CA和全部自簽名的證書
- SSLContext sslcontext = SSLContexts.custom().loadTrustMaterial(trustStore, new TrustSelfSignedStrategy()).build();
- // 只容許使用TLSv1協議
- SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslcontext, new String[] { "TLSv1" }, null,
- SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);
- httpclient = HttpClients.custom().setSSLSocketFactory(sslsf).build();
- // 建立http請求(get方式)
- HttpGet httpget = new HttpGet("https://localhost:8443/myDemo/Ajax/serivceJ.action");
- System.out.println("executing request" + httpget.getRequestLine());
- CloseableHttpResponse response = httpclient.execute(httpget);
- try {
- HttpEntity entity = response.getEntity();
- System.out.println("----------------------------------------");
- System.out.println(response.getStatusLine());
- if (entity != null) {
- System.out.println("Response content length: " + entity.getContentLength());
- System.out.println(EntityUtils.toString(entity));
- EntityUtils.consume(entity);
- }
- } finally {
- response.close();
- }
- } catch (ParseException e) {
- e.printStackTrace();
- } catch (IOException e) {
- e.printStackTrace();
- } catch (KeyManagementException e) {
- e.printStackTrace();
- } catch (NoSuchAlgorithmException e) {
- e.printStackTrace();
- } catch (KeyStoreException e) {
- e.printStackTrace();
- } finally {
- if (httpclient != null) {
- try {
- httpclient.close();
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- }
- }
-
- /**
- * post方式提交表單(模擬用戶登陸請求)
- */
- public void postForm() {
- // 建立默認的httpClient實例.
- CloseableHttpClient httpclient = HttpClients.createDefault();
- // 建立httppost
- HttpPost httppost = new HttpPost("http://localhost:8080/myDemo/Ajax/serivceJ.action");
- // 建立參數隊列
- List<namevaluepair> formparams = new ArrayList<namevaluepair>();
- formparams.add(new BasicNameValuePair("username", "admin"));
- formparams.add(new BasicNameValuePair("password", "123456"));
- UrlEncodedFormEntity uefEntity;
- try {
- uefEntity = new UrlEncodedFormEntity(formparams, "UTF-8");
- httppost.setEntity(uefEntity);
- System.out.println("executing request " + httppost.getURI());
- CloseableHttpResponse response = httpclient.execute(httppost);
- try {
- HttpEntity entity = response.getEntity();
- if (entity != null) {
- System.out.println("--------------------------------------");
- System.out.println("Response content: " + EntityUtils.toString(entity, "UTF-8"));
- System.out.println("--------------------------------------");
- }
- } finally {
- response.close();
- }
- } catch (ClientProtocolException e) {
- e.printStackTrace();
- } catch (UnsupportedEncodingException e1) {
- e1.printStackTrace();
- } catch (IOException e) {
- e.printStackTrace();
- } finally {
- // 關閉鏈接,釋放資源
- try {
- httpclient.close();
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- }
-
- /**
- * 發送 post請求訪問本地應用並根據傳遞參數不一樣返回不一樣結果
- */
- public void post() {
- // 建立默認的httpClient實例.
- CloseableHttpClient httpclient = HttpClients.createDefault();
- // 建立httppost
- HttpPost httppost = new HttpPost("http://localhost:8080/myDemo/Ajax/serivceJ.action");
- // 建立參數隊列
- List<namevaluepair> formparams = new ArrayList<namevaluepair>();
- formparams.add(new BasicNameValuePair("type", "house"));
- UrlEncodedFormEntity uefEntity;
- try {
- uefEntity = new UrlEncodedFormEntity(formparams, "UTF-8");
- httppost.setEntity(uefEntity);
- System.out.println("executing request " + httppost.getURI());
- CloseableHttpResponse response = httpclient.execute(httppost);
- try {
- HttpEntity entity = response.getEntity();
- if (entity != null) {
- System.out.println("--------------------------------------");
- System.out.println("Response content: " + EntityUtils.toString(entity, "UTF-8"));
- System.out.println("--------------------------------------");
- }
- } finally {
- response.close();
- }
- } catch (ClientProtocolException e) {
- e.printStackTrace();
- } catch (UnsupportedEncodingException e1) {
- e1.printStackTrace();
- } catch (IOException e) {
- e.printStackTrace();
- } finally {
- // 關閉鏈接,釋放資源
- try {
- httpclient.close();
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- }
-
- /**
- * 發送 get請求
- */
- public void get() {
- CloseableHttpClient httpclient = HttpClients.createDefault();
- try {
- // 建立httpget.
- HttpGet httpget = new HttpGet("http://www.baidu.com/");
- System.out.println("executing request " + httpget.getURI());
- // 執行get請求.
- CloseableHttpResponse response = httpclient.execute(httpget);
- try {
- // 獲取響應實體
- HttpEntity entity = response.getEntity();
- System.out.println("--------------------------------------");
- // 打印響應狀態
- System.out.println(response.getStatusLine());
- if (entity != null) {
- // 打印響應內容長度
- System.out.println("Response content length: " + entity.getContentLength());
- // 打印響應內容
- System.out.println("Response content: " + EntityUtils.toString(entity));
- }
- System.out.println("------------------------------------");
- } finally {
- response.close();
- }
- } catch (ClientProtocolException e) {
- e.printStackTrace();
- } catch (ParseException e) {
- e.printStackTrace();
- } catch (IOException e) {
- e.printStackTrace();
- } finally {
- // 關閉鏈接,釋放資源
- try {
- httpclient.close();
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- }
-
- /**
- * 上傳文件
- */
- public void upload() {
- CloseableHttpClient httpclient = HttpClients.createDefault();
- try {
- HttpPost httppost = new HttpPost("http://localhost:8080/myDemo/Ajax/serivceFile.action");
-
- FileBody bin = new FileBody(new File("F:\\image\\sendpix0.jpg"));
- StringBody comment = new StringBody("A binary file of some kind", ContentType.TEXT_PLAIN);
-
- HttpEntity reqEntity = MultipartEntityBuilder.create().addPart("bin", bin).addPart("comment", comment).build();
-
- httppost.setEntity(reqEntity);
-
- System.out.println("executing request " + httppost.getRequestLine());
- CloseableHttpResponse response = httpclient.execute(httppost);
- try {
- System.out.println("----------------------------------------");
- System.out.println(response.getStatusLine());
- HttpEntity resEntity = response.getEntity();
- if (resEntity != null) {
- System.out.println("Response content length: " + resEntity.getContentLength());
- }
- EntityUtils.consume(resEntity);
- } finally {
- response.close();
- }
- } catch (ClientProtocolException e) {
- e.printStackTrace();
- } catch (IOException e) {
- e.printStackTrace();
- } finally {
- try {
- httpclient.close();
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- }
- package com.jia.networktest;
import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.HttpClient;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;
public class MainActivity extends Activity implements OnClickListener {
public static final int SHOW_RESPONSE = 0;
public static final int SHOW_HTTPCLIENT = 1;
private Button send_request;
private TextView responseText;
private Button btn_httpClient;
private Handler handler = new Handler() {
public void handleMessage(Message msg) {
switch (msg.what) {
case SHOW_RESPONSE:
String response = (String) msg.obj;
// 在這裏進行UI操做,將結果顯示到界面上
responseText.setText(response);
case SHOW_HTTPCLIENT:
String m_httpClient = (String) msg.obj;
// 在這裏進行UI操做,將結果顯示到界面上
responseText.setText(m_httpClient);
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
send_request = (Button) findViewById(R.id.send_request);
responseText = (TextView) findViewById(R.id.response);
btn_httpClient = (Button) findViewById(R.id.HttpClient);
send_request.setOnClickListener(this);
btn_httpClient.setOnClickListener(this);
}
@Override
public void onClick(View v) {
if (v.getId() == R.id.send_request) {
sendRequestWithHttpURLConnection();
} else if (v.getId() == R.id.HttpClient) {
sendRequestWithHttpClient();
}
}
private void sendRequestWithHttpClient() {
new Thread(new Runnable() {
@Override
public void run() {
try {
HttpClient httpClient = new DefaultHttpClient();
HttpPost httpPost = new HttpPost("http://www.baidu.com");
List<NameValuePair> params = new ArrayList<NameValuePair>();
params.add(new BasicNameValuePair("username",
"yao_jiawei@hotmail.com"));
params.add(new BasicNameValuePair("password", "yaowentian"));
UrlEncodedFormEntity urlEncodedFormEntity = new UrlEncodedFormEntity(
params, "utf-8");
httpPost.setEntity(urlEncodedFormEntity);
HttpResponse httpResponse = httpClient.execute(httpPost);
if (httpResponse.getStatusLine().getStatusCode() == 200) {
// 請求和響應都成功了
HttpEntity entity = httpResponse.getEntity();// 獲取到一個HttpEntity實例
String response = EntityUtils.toString(entity, "utf-8");// 用EntityUtils.toString()這個方法將HttpEntity轉換成字符串
Message message = new Message();
message.what = SHOW_HTTPCLIENT;
// 將服務器返回的結果存放到Message中
message.obj = response.toString();
handler.sendMessage(message);
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}).start();
}
private void sendRequestWithHttpURLConnection() {
// 開啓線程來發起網絡請求
new Thread(new Runnable() {
@Override
public void run() {
HttpURLConnection connection = null;
try {
URL url = new URL("http://www.baidu.com");
connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("POST");
connection.setConnectTimeout(8000);
connection.setReadTimeout(8000);
DataOutputStream out = new DataOutputStream(connection
.getOutputStream());
out.writeBytes("username=yao_jiawei@hotmail.com&password=yaowentian");
InputStream in = connection.getInputStream();
// 下面對獲取到的輸入流進行讀取
BufferedReader reader = new BufferedReader(
new InputStreamReader(in));
StringBuilder response = new StringBuilder();
String line;
while ((line = reader.readLine()) != null) {
response.append(line);
}
Message message = new Message();
message.what = SHOW_RESPONSE;
// 將服務器返回的結果存放到message中
message.obj = response.toString();
handler.sendMessage(message);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
if (connection != null) {
connection.disconnect();
}
}
}
}).start();
}
}
首先要注意的有如下幾點:
一、httpclient鏈接後資源釋放問題很重要,就跟咱們用database connection要釋放資源同樣。
二、https網站採用ssl加密傳輸,證書導入要注意。
三、作這樣的項目最好先了解下http協義,好比302,301,200,404返回代碼的含義(這是最基本的),cookie,session的機制。
四、httpclient的redirect狀態默認是自動的,這在很大程度上給開發者很大的方便(如一些受權得到cookie),可是有時要手動管理下,好比
有時會遇到CircularRedirectException異常,出現這樣的狀況是由於返回的頭文件中location值指向以前重複(端口號能夠不一樣)地址,致使可能會出現死
循環遞歸重定向,這時能夠手動關閉:method.setFollowRedirects(false)
五、有的網站會先判別用戶的請求是不是來自瀏覽器,如不是,則返回不正確的文本,因此用httpclient抓取信息時在頭部加入以下信息:
header.put("User-Agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; QQDownload 1.7; .NET CLR 1.1.4322; CIBA; .NET CLR 2.0.50727)");
六、當post請求提交數據時要改變默認編碼,否則的話提交上去的數據會出現亂碼。重寫postMethod的setContentCharSet()方法就能夠了:
下面寫一個通用類來處理request請求返回的文本:
Java代碼
- /*
- * HttpRequestProxy.java
- *
- * Created on November 3, 2008, 9:53 AM
- */
-
- package cn.com.mozat.net;
-
- import java.io.BufferedReader;
- import java.io.IOException;
- import java.io.InputStream;
- import java.io.InputStreamReader;
- import java.util.HashMap;
- import java.util.Iterator;
- import java.util.Map;
- import java.util.Set;
-
- import org.apache.commons.httpclient.Header;
- import org.apache.commons.httpclient.HttpClient;
- import org.apache.commons.httpclient.HttpException;
- import org.apache.commons.httpclient.HttpMethod;
- import org.apache.commons.httpclient.NameValuePair;
- import org.apache.commons.httpclient.SimpleHttpConnectionManager;
- import org.apache.commons.httpclient.methods.GetMethod;
- import org.apache.commons.httpclient.methods.PostMethod;
-
- import cn.com.mozat.exception.CustomException;
-
- /**
- *
- * @author bird email:lihongfu-84@163.com
- *
- * 2008-11-4 09:49:48
- */
- public class HttpRequestProxy{
- //超時間隔
- private static int connectTimeOut = 60000;
- //讓connectionmanager管理httpclientconnection時是否關閉鏈接
- private static boolean alwaysClose = false;
- //返回數據編碼格式
- private String encoding = "UTF-8";
-
- private final HttpClient client = new HttpClient(new SimpleHttpConnectionManager(alwaysClose));
-
- public HttpClient getHttpClient(){
- return client;
- }
-
- /**
- * 用法:
- * HttpRequestProxy hrp = new HttpRequestProxy();
- * hrp.doRequest("http://www.163.com",null,null,"gbk");
- *
- * @param url 請求的資源URL
- * @param postData POST請求時form表單封裝的數據 沒有時傳null
- * @param header request請求時附帶的頭信息(header) 沒有時傳null
- * @param encoding response返回的信息編碼格式 沒有時傳null
- * @return response返回的文本數據
- * @throws CustomException
- */
- public String doRequest(String url,Map postData,Map header,String encoding) throws CustomException{
- String responseString = null;
- //頭部請求信息
- Header[] headers = null;
- if(header != null){
- Set entrySet = header.entrySet();
- int dataLength = entrySet.size();
- headers= new Header[dataLength];
- int i = 0;
- for(Iterator itor = entrySet.iterator();itor.hasNext();){
- Map.Entry entry = (Map.Entry)itor.next();
- headers[i++] = new Header(entry.getKey().toString(),entry.getValue().toString());
- }
- }
- //post方式
- if(postData!=null){
- PostMethod postRequest = new PostMethod(url.trim());
- if(headers != null){
- for(int i = 0;i < headers.length;i++){
- postRequest.setRequestHeader(headers[i]);
- }
- }
- Set entrySet = postData.entrySet();
- int dataLength = entrySet.size();
- NameValuePair[] params = new NameValuePair[dataLength];
- int i = 0;
- for(Iterator itor = entrySet.iterator();itor.hasNext();){
- Map.Entry entry = (Map.Entry)itor.next();
- params[i++] = new NameValuePair(entry.getKey().toString(),entry.getValue().toString());
- }
- postRequest.setRequestBody(params);
- try {
- responseString = this.executeMethod(postRequest,encoding);
- } catch (CustomException e) {
- throw e;
- } finally{
- postRequest.releaseConnection();
- }
- }
- //get方式
- if(postData == null){
- GetMethod getRequest = new GetMethod(url.trim());
- if(headers != null){
- for(int i = 0;i < headers.length;i++){
- getRequest.setRequestHeader(headers[i]);
- }
- }
- try {
- responseString = this.executeMethod(getRequest,encoding);
- } catch (CustomException e) {
- e.printStackTrace();
- throw e;
- }finally{
- getRequest.releaseConnection();
- }
- }
-
- return responseString;
- }
-
- private String executeMethod(HttpMethod request, String encoding) throws CustomException{
- String responseContent = null;
- InputStream responseStream = null;
- BufferedReader rd = null;
- try {
- this.getHttpClient().executeMethod(request);
- if(encoding != null){
- responseStream = request.getResponseBodyAsStream();
- rd = new BufferedReader(new InputStreamReader(responseStream,
- encoding));
- String tempLine = rd.readLine();
- StringBuffer tempStr = new StringBuffer();
- String crlf=System.getProperty("line.separator");
- while (tempLine != null)
- {
- tempStr.append(tempLine);
- tempStr.append(crlf);
- tempLine = rd.readLine();
- }
- responseContent = tempStr.toString();
- }else
- responseContent = request.getResponseBodyAsString();
-
- Header locationHeader = request.getResponseHeader("location");
- //返回代碼爲302,301時,表示頁面己經重定向,則從新請求location的url,這在
- //一些登陸受權取cookie時很重要
- if (locationHeader != null) {
- String redirectUrl = locationHeader.getValue();
- this.doRequest(redirectUrl, null, null,null);
- }
- } catch (HttpException e) {
- throw new CustomException(e.getMessage());
- } catch (IOException e) {
- throw new CustomException(e.getMessage());
-
- } finally{
- if(rd != null)
- try {
- rd.close();
- } catch (IOException e) {
- throw new CustomException(e.getMessage());
- }
- if(responseStream != null)
- try {
- responseStream.close();
- } catch (IOException e) {
- throw new CustomException(e.getMessage());
-
- }
- }
- return responseContent;
- }
-
-
- /**
- * 特殊請求數據,這樣的請求每每會出現redirect自己而出現遞歸死循環重定向
- * 因此單獨寫成一個請求方法
- * 好比如今請求的url爲:http://localhost:8080/demo/index.jsp
- * 返回代碼爲302 頭部信息中location值爲:http://localhost:8083/demo/index.jsp
- * 這時httpclient認爲進入遞歸死循環重定向,拋出CircularRedirectException異常
- * @param url
- * @return
- * @throws CustomException
- */
- public String doSpecialRequest(String url,int count,String encoding) throws CustomException{
- String str = null;
- InputStream responseStream = null;
- BufferedReader rd = null;
- GetMethod getRequest = new GetMethod(url);
- //關閉httpclient自動重定向動能
- getRequest.setFollowRedirects(false);
- try {
-
- this.client.executeMethod(getRequest);
- Header header = getRequest.getResponseHeader("location");
- if(header!= null){
- //請求重定向後的URL,count同時加1
- this.doSpecialRequest(header.getValue(),count+1, encoding);
- }
- //這裏用count做爲標誌位,當count爲0時才返回請求的URL文本,
- //這樣就能夠忽略全部的遞歸重定向時返回文本流操做,提升性能
- if(count == 0){
- getRequest = new GetMethod(url);
- getRequest.setFollowRedirects(false);
- this.client.executeMethod(getRequest);
- responseStream = getRequest.getResponseBodyAsStream();
- rd = new BufferedReader(new InputStreamReader(responseStream,
- encoding));
- String tempLine = rd.readLine();
- StringBuffer tempStr = new StringBuffer();
- String crlf=System.getProperty("line.separator");
- while (tempLine != null)
- {
- tempStr.append(tempLine);
- tempStr.append(crlf);
- tempLine = rd.readLine();
- }
- str = tempStr.toString();
- }
-
- } catch (HttpException e) {
- throw new CustomException(e.getMessage());
- } catch (IOException e) {
- throw new CustomException(e.getMessage());
- } finally{
- getRequest.releaseConnection();
- if(rd !=null)
- try {
- rd.close();
- } catch (IOException e) {
- throw new CustomException(e.getMessage());
- }
- if(responseStream !=null)
- try {
- responseStream.close();
- } catch (IOException e) {
- throw new CustomException(e.getMessage());
- }
- }
- return str;
- }
-
-
-
-
- public static void main(String[] args) throws Exception{
- HttpRequestProxy hrp = new HttpRequestProxy();
- Map header = new HashMap();
- header.put("User-Agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; QQDownload 1.7; .NET CLR 1.1.4322; CIBA; .NET CLR 2.0.50727)");
- String str = hrp.doRequest(
- "http://www.cma-cgm.com/en/eBusiness/Tracking/Default.aspx?BolNumber=GZ2108827",
- null, header,null);
- System.out.println(str.contains("row_CRXU1587647"));
- // System.out.println(str);
- }
-
- }
- HttpClient 是 Apache Jakarta Common 下的子項目,能夠用來提供高效的、最新的、功能豐富的支持 HTTP 協議的客戶端編程工具包,而且它支持 HTTP 協議最新的版本和建議。本文首先介紹 HTTPClient,而後根據做者實際工做經驗給出了一些常見問題的解決方法。HTTP 協議多是如今 Internet 上使用得最多、最重要的協議了,愈來愈多的 Java 應用程序須要直接經過 HTTP 協議來訪問網絡資源。雖然在 JDK 的 java.net 包中已經提供了訪問 HTTP 協議的基本功能,可是對於大部分應用程序來講,JDK 庫自己提供的功能還不夠豐富和靈活。HttpClient 是 Apache Jakarta Common 下的子項目,用來提供高效的、最新的、功能豐富的支持 HTTP 協議的客戶端編程工具包,而且它支持 HTTP 協議最新的版本和建議。HttpClient 已經應用在不少的項目中,好比 Apache Jakarta 上很著名的另外兩個開源項目 Cactus 和 HTMLUnit 都使用了 HttpClient。如今HttpClient最新版本爲 HttpClient 4.0-beta2
[編輯本段]2.HttpClient 功能介紹
如下列出的是 HttpClient 提供的主要的功能,要知道更多詳細的功能能夠參見 HttpClient 的主頁。
(1)實現了全部 HTTP 的方法(GET,POST,PUT,HEAD 等)
(2)支持自動轉向
(3)支持 HTTPS 協議
(4)支持代理服務器等
[編輯本段]3.HttpClient 基本功能的使用
(1) GET方法
使用 HttpClient 須要如下 6 個步驟:
1. 建立 HttpClient 的實例
2. 建立某種鏈接方法的實例,在這裏是 GetMethod。在 GetMethod 的構造函數中傳入待鏈接的地址
3. 調用第一步中建立好的實例的 execute 方法來執行第二步中建立好的 method 實例
4. 讀 response
5. 釋放鏈接。不管執行方法是否成功,都必須釋放鏈接
6. 對獲得後的內容進行處理
根據以上步驟,咱們來編寫用GET方法來取得某網頁內容的代碼。
大部分狀況下 HttpClient 默認的構造函數已經足夠使用。 HttpClient httpClient = new HttpClient();
建立GET方法的實例。在GET方法的構造函數中傳入待鏈接的地址便可。用GetMethod將會自動處理轉發過程,若是想要把自動處理轉發過程去掉的話,能夠調用方法setFollowRedirects(false)。 GetMethod getMethod = new GetMethod(".....");
調用實例httpClient的executeMethod方法來執行getMethod。因爲是執行在網絡上的程序,在運行executeMethod方法的時候,須要處理兩個異常,分別是HttpException和IOException。引發第一種異常的緣由主要多是在構造getMethod的時候傳入的協議不對,好比不當心將"http"寫成"htp",或者服務器端返回的內容不正常等,而且該異常發生是不可恢復的;第二種異常通常是因爲網絡緣由引發的異常,對於這種異常 (IOException),HttpClient會根據你指定的恢復策略自動試着從新執行executeMethod方法。HttpClient的恢復策略能夠自定義(經過實現接口HttpMethodRetryHandler來實現)。經過httpClient的方法setParameter設置你實現的恢復策略,本文中使用的是系統提供的默認恢復策略,該策略在碰到第二類異常的時候將自動重試3次。executeMethod返回值是一個整數,表示了執行該方法後服務器返回的狀態碼,該狀態碼能表示出該方法執行是否成功、須要認證或者頁面發生了跳轉(默認狀態下GetMethod的實例是自動處理跳轉的)等。 //設置成了默認的恢復策略,在發生異常時候將自動重試3次,在這裏你也能夠設置成自定義的恢復策略
getMethod.getParams().setParameter(HttpMethodParams.RETRY_HANDLER,
new DefaultHttpMethodRetryHandler());
//執行getMethod
int statusCode = client.executeMethod(getMethod);
if (statusCode != HttpStatus.SC_OK) {
System.err.println("Method failed: " + getMethod.getStatusLine());
}
在返回的狀態碼正確後,便可取得內容。取得目標地址的內容有三種方法:第一種,getResponseBody,該方法返回的是目標的二進制的byte流;第二種,getResponseBodyAsString,這個方法返回的是String類型,值得注意的是該方法返回的String的編碼是根據系統默認的編碼方式,因此返回的String值可能編碼類型有誤,在本文的"字符編碼"部分中將對此作詳細介紹;第三種,getResponseBodyAsStream,這個方法對於目標地址中有大量數據須要傳輸是最佳的。在這裏咱們使用了最簡單的getResponseBody方法。 byte[] responseBody = method.getResponseBody();
釋放鏈接。不管執行方法是否成功,都必須釋放鏈接。 method.releaseConnection();
處理內容。在這一步中根據你的須要處理內容,在例子中只是簡單的將內容打印到控制檯。 System.out.println(new String(responseBody));
下面是程序的完整代碼,這些代碼也可在附件中的test.GetSample中找到。
package test;
import java.io.IOException;
import org.apache.commons.httpclient.*;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.commons.httpclient.params.HttpMethodParams;
public class GetSample{
public static void main(String[] args) {
//構造HttpClient的實例
HttpClient httpClient = new HttpClient();
//建立GET方法的實例
GetMethod getMethod = new GetMethod("...");
//使用系統提供的默認的恢復策略
getMethod.getParams().setParameter(HttpMethodParams.RETRY_HANDLER,
new DefaultHttpMethodRetryHandler());
try {
//執行getMethod
int statusCode = httpClient.executeMethod(getMethod);
if (statusCode != HttpStatus.SC_OK) {
System.err.println("Method failed: "
+ getMethod.getStatusLine());
}
//讀取內容
byte[] responseBody = getMethod.getResponseBody();
//處理內容
System.out.println(new String(responseBody));
} catch (HttpException e) {
//發生致命的異常,多是協議不對或者返回的內容有問題
System.out.println("Please check your provided http address!");
e.printStackTrace();
} catch (IOException e) {
//發生網絡異常
e.printStackTrace();
} finally {
//釋放鏈接
getMethod.releaseConnection();
}
}
}
(2)POST方法
根據RFC2616,對POST的解釋以下:POST方法用來向目的服務器發出請求,要求它接受被附在請求後的實體,並把它看成請求隊列(Request-Line)中請求URI所指定資源的附加新子項。POST被設計成用統一的方法實現下列功能:
對現有資源的註釋(Annotation of existing resources)
向電子公告欄、新聞組,郵件列表或相似討論組發送消息
提交數據塊,如將表單的結果提交給數據處理過程
經過附加操做來擴展數據庫
調用HttpClient中的PostMethod與GetMethod相似,除了設置PostMethod的實例與GetMethod有些不一樣以外,剩下的步驟都差很少。在下面的例子中,省去了與GetMethod相同的步驟,只說明與上面不一樣的地方,並以登陸清華大學BBS爲例子進行說明。
構造PostMethod以前的步驟都相同,與GetMethod同樣,構造PostMethod也須要一個URI參數。在建立了PostMethod的實例以後,須要給method實例填充表單的值,在BBS的登陸表單中須要有兩個域,第一個是用戶名(域名叫id),第二個是密碼(域名叫passwd)。表單中的域用類NameValuePair來表示,該類的構造函數第一個參數是域名,第二參數是該域的值;將表單全部的值設置到PostMethod中用方法setRequestBody。另外因爲BBS登陸成功後會轉向另一個頁面,可是HttpClient對於要求接受後繼服務的請求,好比POST和PUT,不支持自動轉發,所以須要本身對頁面轉向作處理。具體的頁面轉向處理請參見下面的"自動轉向"部分。代碼以下:
String url = "....";
PostMethod postMethod = new PostMethod(url);
// 填入各個表單域的值
NameValuePair[] data = { new NameValuePair("id", "youUserName"),
new NameValuePair("passwd", "yourPwd") };
// 將表單的值放入postMethod中
postMethod.setRequestBody(data);
// 執行postMethod
int statusCode = httpClient.executeMethod(postMethod);
// HttpClient對於要求接受後繼服務的請求,象POST和PUT等不能自動處理轉發
// 301或者302
if (statusCode == HttpStatus.SC_MOVED_PERMANENTLY ||
statusCode == HttpStatus.SC_MOVED_TEMPORARILY) {
// 從頭中取出轉向的地址
Header locationHeader = postMethod.getResponseHeader("location");
String location = null;
if (locationHeader != null) {
location = locationHeader.getValue();
System.out.println("The page was redirected to:" + location);
} else {
System.err.println("Location field value is null.");
}
return;
}
[編輯本段]4 使用HttpClient過程當中常見的一些問題
下面介紹在使用HttpClient過程當中常見的一些問題。
字符編碼
某目標頁的編碼可能出如今兩個地方,第一個地方是服務器返回的http頭中,另一個地方是獲得的html/xml頁面中。
在http頭的Content-Type字段可能會包含字符編碼信息。例如可能返回的頭會包含這樣子的信息:Content-Type: text/html; charset=UTF-8。這個頭信息代表該頁的編碼是UTF-8,可是服務器返回的頭信息未必與內容能匹配上。好比對於一些雙字節語言國家,可能服務器返回的編碼類型是UTF-8,但真正的內容卻不是UTF-8編碼的,所以須要在另外的地方去獲得頁面的編碼信息;可是若是服務器返回的編碼不是UTF-8,而是具體的一些編碼,好比gb2312等,那服務器返回的多是正確的編碼信息。經過method對象的getResponseCharSet()方法就能夠獲得http頭中的編碼信息。
對於象xml或者html這樣的文件,容許做者在頁面中直接指定編碼類型。好比在html中會有<meta http-equiv="Content-Type" content="text/html; charset=gb2312"/>這樣的標籤;或者在xml中會有<?xml version="1.0" encoding="gb2312"?>這樣的標籤,在這些狀況下,可能與http頭中返回的編碼信息衝突,須要用戶本身判斷到底那種編碼類型應該是真正的編碼。
自動轉向
根據RFC2616中對自動轉向的定義,主要有兩種:301和302。301表示永久的移走(Moved Permanently),當返回的是301,則表示請求的資源已經被移到一個固定的新地方,任何向該地址發起請求都會被轉到新的地址上。302表示暫時的轉向,好比在服務器端的servlet程序調用了sendRedirect方法,則在客戶端就會獲得一個302的代碼,這時服務器返回的頭信息中location的值就是sendRedirect轉向的目標地址。
HttpClient支持自動轉向處理,可是象POST和PUT方式這種要求接受後繼服務的請求方式,暫時不支持自動轉向,所以若是碰到POST方式提交後返回的是301或者302的話須要本身處理。就像剛纔在POSTMethod中舉的例子:若是想進入登陸BBS後的頁面,必須從新發起登陸的請求,請求的地址能夠在頭字段location中獲得。不過須要注意的是,有時候location返回的多是相對路徑,所以須要對location返回的值作一些處理才能夠發起向新地址的請求。
另外除了在頭中包含的信息可能使頁面發生重定向外,在頁面中也有可能會發生頁面的重定向。引發頁面自動轉發的標籤是:<meta http-equiv="refresh" content="5; url=....">。若是你想在程序中也處理這種狀況的話得本身分析頁面來實現轉向。須要注意的是,在上面那個標籤中url的值也能夠是一個相對地址,若是是這樣的話,須要對它作一些處理後才能夠轉發。
處理HTTPS協議
HttpClient提供了對SSL的支持,在使用SSL以前必須安裝JSSE。在Sun提供的1.4之後的版本中,JSSE已經集成到JDK中,若是你使用的是JDK1.4之前的版本則必須安裝JSSE。JSSE不一樣的廠家有不一樣的實現。下面介紹怎麼使用HttpClient來打開Https鏈接。這裏有兩種方法能夠打開https鏈接,第一種就是獲得服務器頒發的證書,而後導入到本地的keystore中;另一種辦法就是經過擴展HttpClient的類來實現自動接受證書。
方法1,取得證書,並導入本地的keystore:
安裝JSSE (若是你使用的JDK版本是1.4或者1.4以上就能夠跳過這一步)。本文以IBM的JSSE爲例子說明。先到IBM網站上下載JSSE的安裝包。而後解壓開以後將ibmjsse.jar包拷貝到<java-home>\lib\ext\目錄下。
取得而且導入證書。證書能夠經過IE來得到:
1. 用IE打開須要鏈接的https網址,會彈出以下對話框:
2. 單擊"View Certificate",在彈出的對話框中選擇"Details",而後再單擊"Copy to File",根據提供的嚮導生成待訪問網頁的證書文件
3. 嚮導第一步,歡迎界面,直接單擊"Next",
4. 嚮導第二步,選擇導出的文件格式,默認,單擊"Next",
5. 嚮導第三步,輸入導出的文件名,輸入後,單擊"Next",
6. 嚮導第四步,單擊"Finish",完成嚮導
7. 最後彈出一個對話框,顯示導出成功
用keytool工具把剛纔導出的證書倒入本地keystore。Keytool命令在<java-home>\bin\下,打開命令行窗口,併到<java-home>\lib\security\目錄下,運行下面的命令:
keytool -import -noprompt -keystore cacerts -storepass changeit -alias yourEntry1 -file your.cer
其中參數alias後跟的值是當前證書在keystore中的惟一標識符,可是大小寫不區分;參數file後跟的是剛纔經過IE導出的證書所在的路徑和文件名;若是你想刪除剛纔導入到keystore的證書,能夠用命令:
keytool -delete -keystore cacerts -storepass changeit -alias yourEntry1
寫程序訪問https地址。若是想測試是否能連上https,只須要稍改一下GetSample例子,把請求的目標變成一個https地址。
GetMethod getMethod = new GetMethod("your url");
運行該程序可能出現的問題:
1. 拋出異常java.net.SocketException: Algorithm SSL not available。出現這個異常多是由於沒有加JSSEProvider,若是用的是IBM的JSSE Provider,在程序中加入這樣的一行:
if(Security.getProvider("com.ibm.jsse.IBMJSSEProvider") == null)
Security.addProvider(new IBMJSSEProvider());
或者也能夠打開<java-home>\lib\security\java.security,在行
security.provider.1=sun.security.provider.Sun
security.provider.2=com.ibm.crypto.provider.IBMJCE
後面加入security.provider.3=com.ibm.jsse.IBMJSSEProvider
2. 拋出異常java.net.SocketException: SSL implementation not available。出現這個異常多是你沒有把ibmjsse.jar拷貝到<java-home>\lib\ext\目錄下。
3. 拋出異常javax.net.ssl.SSLHandshakeException: unknown certificate。出現這個異常代表你的JSSE應該已經安裝正確,可是可能由於你沒有把證書導入到當前運行JRE的keystore中,請按照前面介紹的步驟來導入你的證書。
方法2,擴展HttpClient類實現自動接受證書
由於這種方法自動接收全部證書,所以存在必定的安全問題,因此在使用這種方法前請仔細考慮您的系統的安全需求。具體的步驟以下:
提供一個自定義的socket factory(test.MySecureProtocolSocketFactory)。這個自定義的類必須實現接口org.apache.commons.httpclient.protocol.SecureProtocolSocketFactory,在實現接口的類中調用自定義的X509TrustManager(test.MyX509TrustManager),這兩個類能夠在隨本文帶的附件中獲得
建立一個org.apache.commons.httpclient.protocol.Protocol的實例,指定協議名稱和默認的端口號 Protocol myhttps = new Protocol("https", new MySecureProtocolSocketFactory (), 443);
註冊剛纔建立的https協議對象 Protocol.registerProtocol("https ", myhttps);
而後按照普通編程方式打開https的目標地址,代碼請參見test.NoCertificationHttpsGetSample
[編輯本段]5 處理代理服務器
HttpClient中使用代理服務器很是簡單,調用HttpClient中setProxy方法就能夠,方法的第一個參數是代理服務器地址,第二個參數是端口號。另外HttpClient也支持SOCKS代理。
httpClient.getHostConfiguration().setProxy(hostName,port);
-
通常的狀況下咱們都是使用IE或者Navigator瀏覽器來訪問一個WEB服務器,用來瀏覽頁面查看信息或者提交一些數據等等。所訪問的這些頁面有的僅僅是一些普通的頁面,有的須要用戶登陸後方可以使用,或者須要認證以及是一些經過加密方式傳輸,例如HTTPS。目前咱們使用的瀏覽器處理這些狀況都不會構成問題。不過你可能在某些時候須要經過程序來訪問這樣的一些頁面,好比從別人的網頁中「偷」一些數據;利用某些站點提供的頁面來完成某種功能,例如說咱們想知道某個手機號碼的歸屬地而咱們本身又沒有這樣的數據,所以只好藉助其餘公司已有的網站來完成這個功能,這個時候咱們須要向網頁提交手機號碼並從返回的頁面中解析出咱們想要的數據來。若是對方僅僅是一個很簡單的頁面,那咱們的程序會很簡單,本文也就沒有必要大張旗鼓的在這裏浪費口舌。可是考慮到一些服務受權的問題,不少公司提供的頁面每每並非能夠經過一個簡單的URL就能夠訪問的,而必須通過註冊而後登陸後方可以使用提供服務的頁面,這個時候就涉及到COOKIE問題的處理。咱們知道目前流行的動態網頁技術例如ASP、JSP無不是經過COOKIE來處理會話信息的。爲了使咱們的程序能使用別人所提供的服務頁面,就要求程序首先登陸後再訪問服務頁面,這過程就須要自行處理cookie,想一想當你用java.net.HttpURLConnection來完成這些功能時是多麼恐怖的事情啊!何況這僅僅是咱們所說的頑固的WEB服務器中的一個很常見的「頑固」!再有如經過HTTP來上傳文件呢?不須要頭疼,這些問題有了「它」就很容易解決了!
咱們不可能列舉全部可能的頑固,咱們會針對幾種最多見的問題進行處理。固然了,正如前面說到的,若是咱們本身使用java.net.HttpURLConnection來搞定這些問題是很恐怖的事情,所以在開始以前咱們先要介紹一下一個開放源碼的項目,這個項目就是Apache開源組織中的httpclient,它隸屬於Jakarta的commons項目,目前的版本是2.0RC2。commons下原本已經有一個net的子項目,可是又把httpclient單獨提出來,可見http服務器的訪問絕非易事。
Commons-httpclient項目就是專門設計來簡化HTTP客戶端與服務器進行各類通信編程。經過它可讓原來很頭疼的事情如今輕鬆的解決,例如你再也不管是HTTP或者HTTPS的通信方式,告訴它你想使用HTTPS方式,剩下的事情交給httpclient替你完成。本文會針對咱們在編寫HTTP客戶端程序時常常碰到的幾個問題進行分別介紹如何使用httpclient來解決它們,爲了讓讀者更快的熟悉這個項目咱們最開始先給出一個簡單的例子來讀取一個網頁的內容,而後按部就班解決掉前進中的全部問題。
1. 讀取網頁(HTTP/HTTPS)內容
下面是咱們給出的一個簡單的例子用來訪問某個頁面
*
* Created on 2003-12-14 by skydong */ package http.demo; import java.io.IOException; import org.apache.commons.httpclient.*; import org.apache.commons.httpclient.methods.*;
在這個例子中首先建立一個HTTP客戶端(HttpClient)的實例,而後選擇提交的方法是GET或者POST,最後在HttpClient實例上執行提交的方法,最後從所選擇的提交方法中讀取服務器反饋回來的結果。這就是使用HttpClient的基本流程。其實用一行代碼也就能夠搞定整個請求的過程,很是的簡單!
2. 以GET或者POST方式向網頁提交參數
其實前面一個最簡單的示例中咱們已經介紹瞭如何使用GET或者POST方式來請求一個頁面,本小節與之不一樣的是多了提交時設定頁面所需的參數,咱們知道若是是GET的請求方式,那麼全部參數都直接放到頁面的URL後面用問號與頁面地址隔開,每一個參數用&隔開,例如:http://java.sun.com?name=liudong&mobile=123456,可是當使用POST方法時就會稍微有一點點麻煩。本小節的例子演示向如何查詢手機號碼所在的城市,代碼以下:
*
* Created on 2009-7-9 by skydong */ package http.demo; import java.io.IOException; import org.apache.commons.httpclient.*; import org.apache.commons.httpclient.methods.*;
在上面的例子中頁面http://www.imobile.com.cn/simcard.php須要一個參數是simcard,這個參數值爲手機號碼段,即手機號碼的前七位,服務器會返回提交的手機號碼對應的省份、城市以及其餘詳細信息。GET的提交方法只須要在URL後加入參數信息,而POST則須要經過NameValuePair類來設置參數名稱和它所對應的值
3. 處理頁面重定向
在JSP/Servlet編程中response.sendRedirect方法就是使用HTTP協議中的重定向機制。它與JSP中的的區別在於後者是在服務器中實現頁面的跳轉,也就是說應用容器加載了所要跳轉的頁面的內容並返回給客戶端;而前者是返回一個狀態碼,這些狀態碼的可能值見下表,而後客戶端讀取須要跳轉到的頁面的URL並從新加載新的頁面。就是這樣一個過程,因此咱們編程的時候就要經過HttpMethod.getStatusCode()方法判斷返回值是否爲下表中的某個值來判斷是否須要跳轉。若是已經確認須要進行頁面跳轉了,那麼能夠經過讀取HTTP頭中的location屬性來獲取新的地址。
狀態碼
對應HttpServletResponse的常量
詳細描述
301
SC_MOVED_PERMANENTLY
頁面已經永久移到另一個新地址
302
SC_MOVED_TEMPORARILY
頁面暫時移動到另一個新的地址
303
SC_SEE_OTHER
客戶端請求的地址必須經過另外的URL來訪問
307
SC_TEMPORARY_REDIRECT
同SC_MOVED_TEMPORARILY
下面的代碼片斷演示如何處理頁面的重定向
client.executeMethod(post);
System.out.println(post.getStatusLine().toString()); post.releaseConnection();
咱們能夠自行編寫兩個JSP頁面,其中一個頁面用response.sendRedirect方法重定向到另一個頁面用來測試上面的例子。
本小節應該說是HTTP客戶端編程中最常遇見的問題,不少網站的內容都只是對註冊用戶可見的,這種狀況下就必需要求使用正確的用戶名和口令登陸成功後,方可瀏覽到想要的頁面。由於HTTP協議是無狀態的,也就是鏈接的有效期只限於當前請求,請求內容結束後鏈接就關閉了。在這種狀況下爲了保存用戶的登陸信息必須使用到Cookie機制。以JSP/Servlet爲例,當瀏覽器請求一個JSP或者是Servlet的頁面時,應用服務器會返回一個參數,名爲jsessionid(因不一樣應用服務器而異),值是一個較長的惟一字符串的Cookie,這個字符串值也就是當前訪問該站點的會話標識。瀏覽器在每訪問該站點的其餘頁面時候都要帶上jsessionid這樣的Cookie信息,應用服務器根據讀取這個會話標識來獲取對應的會話信息。
對於須要用戶登陸的網站,通常在用戶登陸成功後會將用戶資料保存在服務器的會話中,這樣當訪問到其餘的頁面時候,應用服務器根據瀏覽器送上的Cookie中讀取當前請求對應的會話標識以得到對應的會話信息,而後就能夠判斷用戶資料是否存在於會話信息中,若是存在則容許訪問頁面,不然跳轉到登陸頁面中要求用戶輸入賬號和口令進行登陸。這就是通常使用JSP開發網站在處理用戶登陸的比較通用的方法。
這樣一來,對於HTTP的客戶端來說,若是要訪問一個受保護的頁面時就必須模擬瀏覽器所作的工做,首先就是請求登陸頁面,而後讀取Cookie值;再次請求登陸頁面並加入登陸頁所需的每一個參數;最後就是請求最終所需的頁面。固然在除第一次請求外其餘的請求都須要附帶上Cookie信息以便服務器能判斷當前請求是否已經經過驗證。說了這麼多,但是若是你使用httpclient的話,你甚至連一行代碼都無需增長,你只須要先傳遞登陸信息執行登陸過程,而後直接訪問想要的頁面,跟訪問一個普通的頁面沒有任何區別,由於類HttpClient已經幫你作了全部該作的事情了,太棒了!下面的例子實現了這樣一個訪問的過程
*
* Created on 2009-7-9 by skydong */ package http.demo; import org.apache.commons.httpclient.*; import org.apache.commons.httpclient.cookie.*; import org.apache.commons.httpclient.methods.*;
5. 提交XML格式參數
提交XML格式的參數很簡單,僅僅是一個提交時候的ContentType問題,下面的例子演示從文件文件中讀取XML信息並提交給服務器的過程,該過程能夠用來測試Web服務。
import java.io.File; import java.io.FileInputStream; import org.apache.commons.httpclient.HttpClient; import org.apache.commons.httpclient.methods.EntityEnclosingMethod; import org.apache.commons.httpclient.methods.PostMethod;
6. 經過HTTP上傳文件
httpclient使用了單獨的一個HttpMethod子類來處理文件的上傳,這個類就是MultipartPostMethod,該類已經封裝了文件上傳的細節,咱們要作的僅僅是告訴它咱們要上傳文件的全路徑便可,下面的代碼片斷演示如何使用這個類。
MultipartPostMethod filePost = new MultipartPostMethod(targetURL); filePost.addParameter("fileName", targetFilePath); HttpClient client = new HttpClient();
上面代碼中,targetFilePath即爲要上傳的文件所在的路徑。
7. 訪問啓用認證的頁面
咱們常常會碰到這樣的頁面,當訪問它的時候會彈出一個瀏覽器的對話框要求輸入用戶名和密碼後方可,這種用戶認證的方式不一樣於咱們在前面介紹的基於表單的用戶身份驗證。這是HTTP的認證策略,httpclient支持三種認證方式包括:基本、摘要以及NTLM認證。其中基本認證最簡單、通用但也最不安全;摘要認證是在HTTP 1.1中加入的認證方式,而NTLM則是微軟公司定義的而不是通用的規範,最新版本的NTLM是比摘要認證還要安全的一種方式。
下面例子是從httpclient的CVS服務器中下載的,它簡單演示如何訪問一個認證保護的頁面:
import org.apache.commons.httpclient.HttpClient; import org.apache.commons.httpclient.UsernamePasswordCredentials; import org.apache.commons.httpclient.methods.GetMethod; public class BasicAuthenticationExample ...{ public BasicAuthenticationExample() ...{ } public static void main(String[] args) throws Exception ...{ HttpClient client = new HttpClient(); client.getState().setCredentials( "www.verisign.com", "realm", new UsernamePasswordCredentials("username", "password") ); GetMethod get = new GetMethod("https://www.verisign.com/products/index.html";); get.setDoAuthentication( true ); int status = client.executeMethod( get ); System.out.println(status+""+ get.getResponseBodyAsString()); get.releaseConnection(); } }
8. 多線程模式下使用httpclient
多線程同時訪問httpclient,例如同時從一個站點上下載多個文件。對於同一個HttpConnection同一個時間只能有一個線程訪問,爲了保證多線程工做環境下不產生衝突,httpclient使用了一個多線程鏈接管理器的類:MultiThreadedHttpConnectionManager,要使用這個類很簡單,只須要在構造HttpClient實例的時候傳入便可,代碼以下:
MultiThreadedHttpConnectionManager connectionManager =
new MultiThreadedHttpConnectionManager()