原文鏈接
第3章:HTTP 狀態(tài)管理
最初融蹂,HTTP被設(shè)計(jì)為無(wú)狀態(tài)灶伊,面向請(qǐng)求/響應(yīng)的協(xié)議该编,對(duì)于跨越幾個(gè)邏輯相關(guān)的請(qǐng)求/響應(yīng)交換的有狀態(tài)會(huì)話沒有特殊規(guī)定向抢。隨著HTTP協(xié)議的普及和采用越來(lái)越多的系統(tǒng)開始將其用于應(yīng)用程序认境,它從未打算用于電子商務(wù)應(yīng)用程序的傳輸。因此挟鸠,對(duì)狀態(tài)管理的支持成為必要元暴。
Netscape Communications當(dāng)時(shí)是Web客戶端和服務(wù)器軟件的領(lǐng)先開發(fā)商,它基于專有規(guī)范在其產(chǎn)品中實(shí)現(xiàn)了對(duì)HTTP狀態(tài)管理的支持兄猩。后來(lái)茉盏,Netscape嘗試通過發(fā)布規(guī)范草案來(lái)標(biāo)準(zhǔn)化該機(jī)制。這些努力促成了通過RFC標(biāo)準(zhǔn)軌道定義的正式規(guī)范枢冤。但是鸠姨,大量應(yīng)用程序中的狀態(tài)管理仍然主要基于Netscape草案,并且與官方規(guī)范不兼容淹真。 Web瀏覽器的所有主要開發(fā)人員都不得不保持與這些應(yīng)用程序的兼容性讶迁,這極大地促成了標(biāo)準(zhǔn)合規(guī)性的碎片化。
3.1. HTTP cookies
HTTP cookie是HTTP代理和目標(biāo)服務(wù)器可以交換以維護(hù)會(huì)話的令牌或短狀態(tài)信息包核蘸。 Netscape的工程師曾經(jīng)把它稱為“神奇的餅干”巍糯,這個(gè)名字被沿用至今。
HttpClient使用Cookie接口表示抽象cookie令牌客扎。 在其最簡(jiǎn)單的形式中祟峦,HTTP cookie僅僅是名稱/值對(duì)。 通常徙鱼,HTTP cookie還包含許多屬性宅楞,例如有效的域针姿,指定此cookie適用的源服務(wù)器上的URL子集的路徑,以及cookie有效的最長(zhǎng)時(shí)間厌衙。
SetCookie接口表示由源服務(wù)器發(fā)送到HTTP代理的Set-Cookie響應(yīng)頭距淫,以便維持會(huì)話狀態(tài)。
ClientCookie接口擴(kuò)展了Cookie接口婶希,具有其他特定于客戶端的功能榕暇,例如能夠完全檢索原始服務(wù)器指定的原始cookie屬性。 這對(duì)于生成Cookie標(biāo)頭很重要喻杈,因?yàn)槟承〤ookie規(guī)范要求Cookie標(biāo)頭只有在Set-Cookie標(biāo)頭中指定時(shí)才應(yīng)包含某些屬性彤枢。
以下是創(chuàng)建客戶端cookie對(duì)象的示例:
BasicClientCookie cookie = new BasicClientCookie("name", "value");
// Set effective domain and path attributes
cookie.setDomain(".mycompany.com");
cookie.setPath("/");
// Set attributes exactly as sent by the server
cookie.setAttribute(ClientCookie.PATH_ATTR, "/");
cookie.setAttribute(ClientCookie.DOMAIN_ATTR, ".mycompany.com");
3.2. Cookie 規(guī)范
CookieSpec接口表示cookie管理規(guī)范。 cookie管理規(guī)范應(yīng)強(qiáng)制執(zhí)行:
- 解析Set-Cookie標(biāo)頭的規(guī)則奕塑。
- 解析cookie的驗(yàn)證規(guī)則堂污。
- 格式化給定主機(jī)家肯,端口和原始路徑的Cookie標(biāo)頭龄砰。
HttpClient附帶了幾個(gè)CookieSpec實(shí)現(xiàn):
- Standard strict: 狀態(tài)管理策略符合RFC 6265第4節(jié)定義的行為良好的配置文件的語(yǔ)法和語(yǔ)義。
- Standard: 狀態(tài)管理策略符合RFC 6265定義的更寬松的配置文件讨衣,第4節(jié)旨在與不符合良好行為的配置文件的現(xiàn)有服務(wù)器進(jìn)行互操作换棚。
- Netscape draft (已過時(shí)): 此政策符合Netscape Communications發(fā)布的原始規(guī)范草案。 除非絕對(duì)有必要與遺留代碼兼容反镇,否則應(yīng)該避免使用它固蚤。
- RFC 2965 (已過時(shí)): 狀態(tài)管理策略符合RFC 2965定義的過時(shí)狀態(tài)管理規(guī)范。請(qǐng)不要在新應(yīng)用程序中使用歹茶。
- RFC 2109 (已過時(shí)): 狀態(tài)管理策略符合RFC 2109定義的過時(shí)狀態(tài)管理規(guī)范夕玩。請(qǐng)不要在新應(yīng)用程序中使用。
- Browser compatibility (已過時(shí)): 此策略致力于嚴(yán)密模仿舊版本的瀏覽器應(yīng)用程序(如Microsoft Internet Explorer和Mozilla FireFox)的(錯(cuò)誤)行為惊豺。 請(qǐng)不要在新的應(yīng)用程序中使用燎孟。
- Default: 默認(rèn)cookie策略是一種綜合策略,它根據(jù)與HTTP響應(yīng)一起發(fā)送的cookie的屬性(例如版本屬性尸昧,現(xiàn)在已過時(shí))選擇RFC 2965揩页,RFC 2109或Netscape草案兼容實(shí)現(xiàn)。 在HttpClient的下一個(gè)次要版本中烹俗,將棄用此策略以支持標(biāo)準(zhǔn)(符合RFC 6265)實(shí)現(xiàn)爆侣。
- Ignore cookies: 所有cookie都被忽略。
強(qiáng)烈建議在新應(yīng)用程序中使用標(biāo)準(zhǔn)(Standard)或嚴(yán)格標(biāo)準(zhǔn)(Standard strict)策略幢妄。 應(yīng)使用過時(shí)的規(guī)范與舊系統(tǒng)兼容兔仰。 在HttpClient的下一個(gè)主要版本中將刪除對(duì)過時(shí)規(guī)范的支持。
3.3. 選擇cookie策略
如果需要蕉鸳,可以在HTTP客戶端設(shè)置Cookie策略斋陪,并在HTTP請(qǐng)求級(jí)別覆蓋。
RequestConfig globalConfig = RequestConfig.custom()
.setCookieSpec(CookieSpecs.DEFAULT)
.build();
CloseableHttpClient httpclient = HttpClients.custom()
.setDefaultRequestConfig(globalConfig)
.build();
RequestConfig localConfig = RequestConfig.copy(globalConfig)
.setCookieSpec(CookieSpecs.STANDARD_STRICT)
.build();
HttpGet httpGet = new HttpGet("/");
httpGet.setConfig(localConfig);
3.4. 自定義cookie策略
為了實(shí)現(xiàn)自定義cookie策略,應(yīng)該創(chuàng)建CookieSpec接口的自定義實(shí)現(xiàn)无虚,創(chuàng)建CookieSpecProvider實(shí)現(xiàn)以創(chuàng)建和初始化自定義規(guī)范的實(shí)例缔赠,并使用HttpClient注冊(cè)工廠。 一旦注冊(cè)了自定義規(guī)范友题,就可以像標(biāo)準(zhǔn)cookie規(guī)范一樣激活它嗤堰。
PublicSuffixMatcher publicSuffixMatcher = PublicSuffixMatcherLoader.getDefault();
Registry<CookieSpecProvider> r = RegistryBuilder.<CookieSpecProvider>create()
.register(CookieSpecs.DEFAULT,
new DefaultCookieSpecProvider(publicSuffixMatcher))
.register(CookieSpecs.STANDARD,
new RFC6265CookieSpecProvider(publicSuffixMatcher))
.register("easy", new EasySpecProvider())
.build();
RequestConfig requestConfig = RequestConfig.custom()
.setCookieSpec("easy")
.build();
CloseableHttpClient httpclient = HttpClients.custom()
.setDefaultCookieSpecRegistry(r)
.setDefaultRequestConfig(requestConfig)
.build();
3.5. Cookie持久性
HttpClient可以與實(shí)現(xiàn)CookieStore接口的持久性cookie存儲(chǔ)的任何物理表示一起使用。 名為BasicCookieStore的默認(rèn)CookieStore實(shí)現(xiàn)是一個(gè)由java.util.ArrayList支持的簡(jiǎn)單實(shí)現(xiàn)度宦。 當(dāng)容器對(duì)象被垃圾收集時(shí)踢匣,存儲(chǔ)在BasicClientCookie對(duì)象中的Cookie將丟失。 如有必要戈抄,用戶可以提供更復(fù)雜的實(shí)現(xiàn)离唬。
// Create a local instance of cookie store
CookieStore cookieStore = new BasicCookieStore();
// Populate cookies if needed
BasicClientCookie cookie = new BasicClientCookie("name", "value");
cookie.setDomain(".mycompany.com");
cookie.setPath("/");
cookieStore.addCookie(cookie);
// Set the store
CloseableHttpClient httpclient = HttpClients.custom()
.setDefaultCookieStore(cookieStore)
.build();
3.6. HTTP狀態(tài)管理和執(zhí)行上下文
在HTTP請(qǐng)求執(zhí)行過程中,HttpClient將以下與狀態(tài)管理相關(guān)的對(duì)象添加到執(zhí)行上下文中:
- Lookup 表示實(shí)際的cookie規(guī)范注冊(cè)表划鸽。 在本地上下文中設(shè)置的此屬性的值優(yōu)先于默認(rèn)值输莺。
- CookieSpec 表示實(shí)際的cookie規(guī)范。
- CookieOrigin 表示原始服務(wù)器的實(shí)際細(xì)節(jié)裸诽。
-
CookieStore 實(shí)際的cookie存儲(chǔ)庫(kù)(CookieStore)嫂用。 在本地上下文中設(shè)置的此屬性的值優(yōu)先于默認(rèn)值。
本地HttpContext對(duì)象可用于在請(qǐng)求執(zhí)行之前自定義HTTP狀態(tài)管理上下文丈冬,或在請(qǐng)求執(zhí)行后檢查其狀態(tài)嘱函。 還可以使用單獨(dú)的執(zhí)行上下文來(lái)實(shí)現(xiàn)每個(gè)用戶(或每個(gè)線程)狀態(tài)管理。 在本地上下文中定義的cookie規(guī)范注冊(cè)表和cookie存儲(chǔ)將優(yōu)先于在HTTP客戶端級(jí)別設(shè)置的默認(rèn)值埂蕊。
CloseableHttpClient httpclient = <...>
Lookup<CookieSpecProvider> cookieSpecReg = <...>
CookieStore cookieStore = <...>
HttpClientContext context = HttpClientContext.create();
context.setCookieSpecRegistry(cookieSpecReg);
context.setCookieStore(cookieStore);
HttpGet httpget = new HttpGet("http://somehost/");
CloseableHttpResponse response1 = httpclient.execute(httpget, context);
<...>
// Cookie origin details
CookieOrigin cookieOrigin = context.getCookieOrigin();
// Cookie spec used
CookieSpec cookieSpec = context.getCookieSpec();