前言
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管理规范。这个规范有:
- Set-Cookie的传递规则和可选的Set-Cookie2头信息
- 已传递cookie的验证
- 对所给主机、端口和路径的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());
返回结果:
Apache HttpClient : Http Cookies,布布扣,bubuko.com