Apache HttpClient : Http Cookies

前言

HttpClient已經被集成到Android的SDK裏,但在JDK裏面仍然需要HttpURLConnectionn發起HTTP請求。HttpClient可以看做是一個加強版的HttpURLConnection,但它的側重點是如何發送請求、接受相應和管理Http連接。


在介紹Http Cookies之前,筆者給出一個應用場景:你需要一個根據地理信息(城市名或者經緯度)獲取天氣的應用。可選的API很多,不幸的是,網上提到的Google天氣API已經停止服務了(不是被牆);雅虎是英文的,且需要得到其城市ID;其他各種知名或不知名的要麼收費,要麼不好用。實際上,百度推出的車聯網API也支持天氣服務,不過每個申請的AK原則上訪問次數有限(真要做天氣應用,推薦使用中國國家氣象局的API)。當在百度你申請了AK,發出訪問時,可能有這樣的提示:


其基本代碼如下:

CloseableHttpClient httpClient = HttpClients.createDefault();
		HttpGet get = new HttpGet("http://api.map.baidu.com/telematics/v3/weather?location=西安&output=json&ak=yourkey");
		CloseableHttpResponse response = httpClient.execute(get);
		System.out.println(EntityUtils.toString(response.getEntity()));
其實,做個爬蟲的朋友大多遇到這個警告:Cookie rejected。
好吧。Cookie rejected,下面我們詳細討論HttpClient的Cookie管理機制。


什麼是Cookie

一個Http Cookie就是一個令牌或者狀態信息的短包,用來在Http代理和目標服務器間維護一個session交互信息。這個名字最早來自於Netscape的工程師。

Httpclient使用Cookie的接口來表示抽象的cookie令牌。它最簡單的形式是一個鍵值對。通常一個Http cookie也包含大量屬性,比如版本號,合法的域,在原始服務器上的該cooki起作用的子集URL的路徑,cookie存活的最大時間等。

SetCookie接口表示一個由服務器發給Http代理的Set-Cookie響應頭,用以維護會話狀態。SetCookie2接口繼承了SetCookie,並有特定的Set-Cookie2方法。

ClientCookie接口繼承了Cookie接口,並添加了額外的特殊功能,比如獲取原始的Cookie屬性。這對產生Cookie頭很重要,因爲一些cookie說明要求Cookie頭包含只在Set-Cookie或Set-Cookie指定的特定屬性。


Cookie版本號

與Netscape草案兼容但不和官方規範兼容的被看做是版本0,標準的兼容cookie被看做是版本1。HttpClient根據版本號可能會以不同方式處理cookie。

下面是一個標準cookie重建的例子。注意:標準兼容cookie必須保留所有來自原始服務器的屬性。

BasicClientCookie2 stdCookie = new BasicClientCookie2("name", "value");
stdCookie.setVersion(1);
stdCookie.setDomain(".baidu.com");
stdCookie.setPorts(new int[] {80,8080});
stdCookie.setPath("/");
stdCookie.setSecure(true);
stdCookie.setAttribute(ClientCookie.VERSION_ATTR, "1");
stdCookie.setAttribute(ClientCookie.DOMAIN_ATTR, ".baidu.com");
stdCookie.setAttribute(ClientCookie.PORT_ATTR, "80,8080");


Cookie規範

CookieSpe接口表示一個Cookie管理規範。這個規範有:

  1. Set-Cookie的傳遞規則和可選的Set-Cookie2頭信息
  2. 已傳遞cookie的驗證
  3. 對所給主機、端口和路徑的Cookie頭的形式

下面有幾種Cookie的實現:

Netscape draft:由Netscape委員會發布的原始草案。除非爲了絕對的兼容,請避免使用。

Standard:RFC 2965 HTTP 狀態管理規範。

Browser compatibility:該實現竭力減小常見瀏覽器的差異性。

Best match:“元”cookie規範,依據Http響應cookie的格式得到一個cookie規範。基本上集合了上面的所有。

Ignore cookies:忽略所有。

強烈建議使用Best match。


選擇cookie 策略

cookie策略可以在Http客戶端設置,並在必要時可在Http requset級別上重寫。

RequestConfig globalConfig = RequestConfig.custom()
        .setCookieSpec(CookieSpecs.BEST_MATCH)
        .build();
CloseableHttpClient httpclient = HttpClients.custom()
        .setDefaultRequestConfig(globalConfig)
        .build();
RequestConfig localConfig = RequestConfig.copy(globalConfig)
        .setCookieSpec(CookieSpecs.BROWSER_COMPATIBILITY)
        .build();
HttpGet httpGet = new HttpGet("/");
httpGet.setConfig(localConfig);

自定義cookie策略

爲了實現一個定製的cookie策略,需要實現CookieSpec接口,創建一個CookieSpeProvier的實現來創建和初始化自定義規範並進行註冊。一旦定製的規範完成了註冊,它可以像標準的cookie規範一樣被激活。

CookieSpecProvider easySpecProvider = new CookieSpecProvider() {

    public CookieSpec create(HttpContext context) {

        return new BrowserCompatSpec() {
            @Override
            public void validate(Cookie cookie, CookieOrigin origin)
                    throws MalformedCookieException {
                // Oh, I am easy
            }
        };
    }

};
Registry<CookieSpecProvider> r = RegistryBuilder.<CookieSpecProvider>create()
        .register(CookieSpecs.BEST_MATCH,
            new BestMatchSpecFactory())
        .register(CookieSpecs.BROWSER_COMPATIBILITY,
            new BrowserCompatSpecFactory())
        .register("easy", easySpecProvider)
        .build();

RequestConfig requestConfig = RequestConfig.custom()
        .setCookieSpec("easy")
        .build();

CloseableHttpClient httpclient = HttpClients.custom()
        .setDefaultCookieSpecRegistry(r)
        .setDefaultRequestConfig(requestConfig)
        .build();

Cookie的持久化

HttpClien可以和任何實現CookieStrore接口的持久化cookie store的物理表示協作。默認的CookieStroe實現是BasicCookie,它是用一個ArrayList實現的。不幸的是,當垃圾回收時存儲在BasicClientCookie對象的數據會丟失。

// Create a local instance of cookie store
CookieStore cookieStore = new BasicCookieStore();
// Populate cookies if needed
BasicClientCookie cookie = new BasicClientCookie("name", "value");
cookie.setVersion(0);
cookie.setDomain(".mycompany.com");
cookie.setPath("/");
cookieStore.addCookie(cookie);
// Set the store
CloseableHttpClient httpclient = HttpClients.custom()
        .setDefaultCookieStore(cookieStore)
        .build();


現在HttpClient裏面的Cookie的問題基本介紹清楚了。我們使用標準的cookie策略,重新獲取天氣的代碼。

RequestConfig globalConfig = RequestConfig.custom().
				setCookieSpec(CookieSpecs.BEST_MATCH).build();
		
        CloseableHttpClient client = HttpClients.custom().
        		setDefaultRequestConfig(globalConfig).build();
        
        RequestConfig localConfig = RequestConfig.copy(globalConfig)
                .setCookieSpec(CookieSpecs.BROWSER_COMPATIBILITY)
                .build();
        HttpGet get = new HttpGet(sb.toString());
        get.setConfig(localConfig);
        
		CloseableHttpResponse response = client.execute(get);
		
		String weatherDetail = EntityUtils.toString(response.getEntity());

返回結果:



發佈了26 篇原創文章 · 獲贊 1 · 訪問量 9萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章