最近公司的项目在使用http接口作为其他项目的服务提供者,调用的时候需要使用
apache
的http-client
作为发起http请求的基础部件。使用过程中发现请求时无法携带cookie,上网查阅资料并查找源码,最终问题得以解决并记录如下。
起因
- 目前在公司负责的项目需要作为一个服务提供者,为公司的其他项目提供http接口服务;为了方便其他项目组的调用,使用
http-client
封装了一个jar包,外部项目调用的时候直接使用jar包封装好的方法进行调用,降低外部系统调用的难度,并可以保证调用方式的正确性。在封装并调试的过程中,发现了一个问题,就是直接使用http-client
调用的时候不会携带cookie,即使按照网上教程、官网文档的方式、依旧无法添加cookie。请求时如果不能携带cookie,则被调用方会因为没有cookie,而重新创建session;目前该项目的session是存储在redis里,虽然每个seesion占用的空间并不大,但是如果http-client
发起的请求过多,那么redis中存储的session依旧有可能占满redis的内存,同时对于系统问题的排除也比较不利,故需要解决。
解决过程
- 网上能找到最多的代码大概类似于下面这样
1 | BasicCookieStore cookieStore = new BasicCookieStore(); |
但是经过实际的测试,发现并不能添加cookie,遂继续google。直到找到了这篇blog,虽然这篇blog没有解决我的问题,但是阅读大致能够缕清
http-client
中对于cookie的处理方式,再结合官网doc上对于cookie处理部分的解释,大致有了思路:http-client
对于cookie的添加由所谓的cookie policy
决定,当没有设置cookie policy
是,会默认使用DefaultCookieSpecProvider
的create()
方法,创建一个cookie处理策略cookieSpec
。默认处理策略
cookieSpec
创建后,会调用其中的match()
方法判断cookie是否符合规则,符合规则后,再调用cookieSpec.formatCookies()
方法,格式化cookie,并最终添加到请求的header里,至此完成cookie的添加。源码如下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58//从上下文获取请求设置
final RequestConfig config = clientContext.getRequestConfig();
//获取cookie处理策略
String policy = config.getCookieSpec();
//cookie处理策略为空,则使用默认的处理策略
if (policy == null) {
policy = CookieSpecs.DEFAULT;
}
//此处省略无关代码
...
// Get an instance of the selected cookie policy
// 上面一行是源码带的注释,就是从 registry 获取 CookieSpecProvider
final CookieSpecProvider provider = registry.lookup(policy);
if (provider == null) {
if (this.log.isDebugEnabled()) {
this.log.debug("Unsupported cookie policy: " + policy);
}
return;
}
// 调用 CookieSpecProvider 获取 cookie 处理策略实例
final CookieSpec cookieSpec = provider.create(clientContext);
// Get all cookies available in the HTTP state
// 获取cookie
final List<Cookie> cookies = cookieStore.getCookies();
// Find cookies matching the given origin
final List<Cookie> matchedCookies = new ArrayList<Cookie>();
final Date now = new Date();
boolean expired = false;
for (final Cookie cookie : cookies) {
if (!cookie.isExpired(now)) {
// 逐一比较cookie是否符合规则,符合规则的才能添加,就是这里出了问题才导致cookie没有成功添加到请求
if (cookieSpec.match(cookie, cookieOrigin)) {
if (this.log.isDebugEnabled()) {
this.log.debug("Cookie " + cookie + " match " + cookieOrigin);
}
matchedCookies.add(cookie);
}
} else {
if (this.log.isDebugEnabled()) {
this.log.debug("Cookie " + cookie + " expired");
}
expired = true;
}
}
// Per RFC 6265, 5.3
// The user agent must evict all expired cookies if, at any time, an expired cookie
// exists in the cookie store
if (expired) {
cookieStore.clearExpired(now);
}
// Generate Cookie request headers
if (!matchedCookies.isEmpty()) {
// 使用cookie策略实例将cookie处理为headers,至此添加cookie完成
final List<Header> headers = cokieSpec.formatCookies(matchedCookies);
for (final Header header : headers) {
request.addHeader(header);
}
}
- 有了上面的处理过程就好说了,结合官网doc的说明,首先创建一个类实现
CookieSpecProvider
接口
1 | public class SingleCookieSpecProvider implements CookieSpecProvider { |
然后创建一个类实现CookieSpec
接口:
1 | public class SingleCookieSpec implements CookieSpec { |
最后,http-client
调用方法如下
1 | // 设置下cookie |
总结
遇到问题的时候,如果网上的资料不能解决问题,还是得看源码,程序的工作原理都在代码里。