在使用httpclient做接口相关测试的过程中,遇到过一个障碍:如何处理多用户同时登陆。之前用户身份凭证一般都是做公参里面处理或者在header中单独定义一个或者几个字段,cookie都是使用httpclient自带的管理器自动管理的。


后来用户凭证存到了cookie里,这里就有了一些障碍,经过翻看资料得到了解决。

首先取消cookie的自动管理

设置如下:

  /**
2 * 获取请求超时控制器
3 * <p>
4 * cookieSpec:即cookie策略。参数为cookiespecs的一些字段。作用:
5 * 1、如果网站header中有set-cookie字段时,采用默认方式可能会被cookie reject,无法写入cookie。将此属性设置成CookieSpecs.STANDARD_STRICT可避免此情况。
6 * 2、如果要想忽略cookie访问,则将此属性设置成CookieSpecs.IGNORE_COOKIES。
7 * </p>
8 *
9 * @return
10 */
11 private static RequestConfig getRequestConfig() {
12 return RequestConfig.custom().setConnectionRequestTimeout(HttpClientConstant.CONNECT_REQUEST_TIMEOUT).setConnectTimeout(HttpClientConstant.CONNECT_TIMEOUT).setSocketTimeout(HttpClientConstant.SOCKET_TIMEOUT).setCookieSpec(CookieSpecs.IGNORE_COOKIES).setRedirectsEnabled(false).build();
13 }

这里说明一点,这个requestconfig既可以在对HTTPrequestbase进行设置,也可以对CloseableHttpClient进行设置,由于在各个项目中都采取了单独处理cookie的设置,所以我是直接放在了连接池CloseableHttpClient对象的设置里面,如下:

 /**
2 * 通过连接池获取https协议请求对象
3 * <p>
4 * 此处会默认添加一天defaultcookiesstore,会处理响应头中的set-cookie字段
5 * 增加默认的请求控制器
6 * </p>
7 *
8 * @return
9 */
10 private static CloseableHttpClient getCloseableHttpsClients() {
11 // 创建自定义的httpsclient对象
12 CloseableHttpClient client = HttpClients.custom().setConnectionManager(connManager).setRetryHandler(httpRequestRetryHandler).setDefaultRequestConfig(requestConfig).build();
13// CloseableHttpClient client = HttpClients.createDefault();//非连接池创建
14 return client;
15 }

其次处理set-cookie信息

我的方案是在处理响应的时候,只用closeablehttpresponse对象接收响应的,然后在单独在header里面遍历set-cookie字段的值,在处理json对象作为返回体的时候添加进去,如下:

   /**
2 * 响应结束之后,处理响应头信息,如set-cookien内容
3 *
4 * @param response 响应内容
5 * @return
6 */
7 private static JSONObject afterResponse(CloseableHttpResponse response) {
8 JSONObject cookies = new JSONObject();
9 List<Header> headers = Arrays.asList(response.getHeaders("Set-Cookie"));
10 if (headers.size() == 0) return cookies;
11 headers.forEach(x -> {
12 String[] split = x.getValue().split(";")[0].split("=", 2);
13 cookies.put(split[0], split[1]);
14 });
15 return cookies;
16 }
17
18
19 /**
20 * 根据解析好的content,转化json对象
21 *
22 * @param content
23 * @return
24 */
25 private static JSONObject getJsonResponse(String content, JSONObject cookies) {
26 JSONObject jsonObject = new JSONObject();
27 try {
28 jsonObject = JSONObject.fromObject(content);
29 } catch (Exception e) {
30 jsonObject.put("content", content);
31 jsonObject.put("code", TEST_ERROR_CODE);
32 logger.warn("响应体非json格式,已经自动转换成json格式!");
33 } finally {
34 if (!cookies.isEmpty()) jsonObject.put(HttpClientConstant.COOKIE, cookies);
35 return jsonObject;
36 }
37 }

最后处理多用户保存和携带cookie

在每个项目的base对象接收到响应之后存储cookie以便子类继承,在每次发送请求的时候带上当前对象的cookie,以对象形式存储每一个用户,达到多用户同时登录的目的,如下:

 @Override
2 public JSONObject getResponse(HttpRequestBase httpRequestBase) {
3 setHeaders(httpRequestBase);
4 JSONObject response = FanLibrary.getHttpResponse(httpRequestBase);
5 handleResponseHeader(response);
6 return response;
7 }
8
9 @Override
10 public void setHeaders(HttpRequestBase httpRequestBase) {
11 httpRequestBase.addHeader(Common.REQUEST_ID);
12 httpRequestBase.addHeader(FanLibrary.getHeader("token", token));
13 if (!cookies.isEmpty()) httpRequestBase.addHeader(FanLibrary.getCookies(cookies));
14 }
15
16 @Override
17 public void handleResponseHeader(JSONObject response) {
18 if (!response.containsKey(HttpClientConstant.COOKIE)) return;
19 cookies.putAll(response.getJSONObject(HttpClientConstant.COOKIE));
20 response.remove(HttpClientConstant.COOKIE);
21 }
  • 在多用户多线程请求的场景下,在初始化每一个对象的时候小概率会发生一些问题:可能同一个对象会被初始化多次,这样在第二次初始化之前创建的子类对象存储的cookie会失效,由于没有做通知改变功能(多线程编程搞不定),所以测试的时候统一采用了线程绑定用户的模式,一个线程对应一个用户,防止发生混乱。