HttpClient 教程 (六)

第六章 高級(jí)主題

6.1 自定義客戶端連接

在特定條件下,也許需要來定制HTTP報(bào)文通過線路傳遞,越過了可能使用的HTTP參數(shù)來處理非標(biāo)準(zhǔn)不兼容行為的方式。比如,對(duì)于Web爬蟲,它可能需要強(qiáng)制HttpClient接受格式錯(cuò)誤的響應(yīng)頭部信息,來搶救報(bào)文的內(nèi)容。

通常插入一個(gè)自定義的報(bào)文解析器的過程或定制連接實(shí)現(xiàn)需要幾個(gè)步驟:

提供一個(gè)自定義LineParser/LineFormatter接口實(shí)現(xiàn)。如果需要,實(shí)現(xiàn)報(bào)文解析/格式化邏輯。

class MyLineParser extends BasicLineParser {

@Override

public Header parseHeader(

final CharArrayBuffer buffer) throws ParseException {

try {

return super.parseHeader(buffer);

} catch (ParseException ex) {

// 壓制ParseException異常

return new BasicHeader("invalid", buffer.toString());

}

}

}

提過一個(gè)自定義的OperatedClientConnection實(shí)現(xiàn)。替換需要自定義的默認(rèn)請(qǐng)求/響應(yīng)解析器,請(qǐng)求/響應(yīng)格式化器。如果需要,實(shí)現(xiàn)不同的報(bào)文寫入/讀取代碼。

class MyClientConnection extends DefaultClientConnection {

@Override

protected HttpMessageParser createResponseParser(

final SessionInputBuffer buffer,

final HttpResponseFactory responseFactory,

final HttpParams params) {

return new DefaultResponseParser(buffer,

new MyLineParser(),responseFactory,params);

}

}

為了創(chuàng)建新類的連接,提供一個(gè)自定義的ClientConnectionOperator接口實(shí)現(xiàn)。如果需要,實(shí)現(xiàn)不同的套接字初始化代碼。

class MyClientConnectionOperator extends

DefaultClientConnectionOperator {

public MyClientConnectionOperator(

final SchemeRegistry sr) {

super(sr);

}

@Override

public OperatedClientConnection createConnection() {

return new MyClientConnection();

}

}

為了創(chuàng)建新類的連接操作,提供自定義的ClientConnectionManager接口實(shí)現(xiàn)。

class MyClientConnManager extends SingleClientConnManager {

public MyClientConnManager(

final HttpParams params,

final SchemeRegistry sr) {

super(params, sr);

}

@Override

protected ClientConnectionOperator createConnectionOperator(

final SchemeRegistry sr) {

return new MyClientConnectionOperator(sr);

}

}

6.2 有狀態(tài)的HTTP連接

HTTP規(guī)范假設(shè)session狀態(tài)信息通常是以HTTP cookie格式嵌入在HTTP報(bào)文中的,因此HTTP連接通常是無狀態(tài)的,這個(gè)假設(shè)在現(xiàn)實(shí)生活中通常是不對(duì)的。也有一些情況,當(dāng)HTTP連接使用特定的用戶標(biāo)識(shí)或特定的安全上下文來創(chuàng)建時(shí),因此不能和其它用戶共享,只能由該用戶重用。這樣的有狀態(tài)的HTTP連接的示例就是NTLM認(rèn)證連接和使用客戶端證書認(rèn)證的SSL連接。

6.2.1 用戶令牌處理器

HttpClient依賴UserTokenHandler接口來決定給定的執(zhí)行上下文是否是用戶指定的。如果這個(gè)上下文是用戶指定的或者如果上下文沒有包含任何資源或關(guān)于當(dāng)前用戶指定詳情而是null,令牌對(duì)象由這個(gè)處理器返回,期望唯一地標(biāo)識(shí)當(dāng)前的用戶。用戶令牌將被用來保證用戶指定資源不會(huì)和其它用戶來共享或重用。

如果它可以從給定的執(zhí)行上下文中來獲得,UserTokenHandler接口的默認(rèn)實(shí)現(xiàn)是使用主類的一個(gè)實(shí)例來代表HTTP連接的狀態(tài)對(duì)象。UserTokenHandler將會(huì)使用基于如NTLM或開啟的客戶端認(rèn)證SSL會(huì)話認(rèn)證模式的用戶的主連接。如果二者都不可用,那么就不會(huì)返回令牌。

如果默認(rèn)的不能滿足它們的需要,用戶可以提供一個(gè)自定義的實(shí)現(xiàn):

DefaultHttpClient httpclient = new DefaultHttpClient();

httpclient.setUserTokenHandler(new UserTokenHandler() {

public Object getUserToken(HttpContext context) {

return context.getAttribute("my-token");

}

});

6.2.2 用戶令牌和執(zhí)行上下文

在HTTP請(qǐng)求執(zhí)行的過程中,HttpClient添加了下列和用戶標(biāo)識(shí)相關(guān)的對(duì)象到執(zhí)行上下文中:

'http.user-token':對(duì)象實(shí)例代表真實(shí)的用戶標(biāo)識(shí),通常期望Principle接口的實(shí)例。

我們可以在請(qǐng)求被執(zhí)行后,通過檢查本地HTTP上下文的內(nèi)容,發(fā)現(xiàn)是否用于執(zhí)行請(qǐng)求的連接是有狀態(tài)的。

DefaultHttpClient httpclient = new DefaultHttpClient();

HttpContext localContext = new BasicHttpContext();

HttpGet httpget = new HttpGet("http://localhost:8080/");

HttpResponse response = httpclient.execute(httpget, localContext);

HttpEntity entity = response.getEntity();

if (entity != null) {

entity.consumeContent();

}

Object userToken = localContext.getAttribute(ClientContext.USER_TOKEN);

System.out.println(userToken);

6.2.2.1 持久化有狀態(tài)的連接

請(qǐng)注意帶有狀態(tài)對(duì)象的持久化連接僅當(dāng)請(qǐng)求被執(zhí)行時(shí),相同狀態(tài)對(duì)象被綁定到執(zhí)行上下文時(shí)可以被重用。所以,保證相同上下文重用于執(zhí)行隨后的相同用戶,或用戶令牌綁定到之前請(qǐng)求執(zhí)行上下文的HTTP請(qǐng)求是很重要的。

DefaultHttpClient httpclient = new DefaultHttpClient();

HttpContext localContext1 = new BasicHttpContext();

HttpGet httpget1 = new HttpGet("http://localhost:8080/");

HttpResponse response1 = httpclient.execute(httpget1, localContext1);

HttpEntity entity1 = response1.getEntity();

if (entity1 != null) {

entity1.consumeContent();

}

Principal principal = (Principal) localContext1.getAttribute(

ClientContext.USER_TOKEN);

HttpContext localContext2 = new BasicHttpContext();

localContext2.setAttribute(ClientContext.USER_TOKEN, principal);

HttpGet httpget2 = new HttpGet("http://localhost:8080/");

HttpResponse response2 = httpclient.execute(httpget2, localContext2);

HttpEntity entity2 = response2.getEntity();

if (entity2 != null) {

entity2.consumeContent();

}

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容