基础的HTTPClient搭建注意事项(中)

项目结构更新:


结构图V1.1

修改了replaceBodyParamsreplaceParams,更加直观

新加了asserExpected函数作为判断断言类似的,接口测试不只要测成功的例子,也要测失败的例子。当初真的是想当然了,这个函数我遇到了些麻烦,会在文章结尾说明下。


首先,放上该项目所用到的包依赖,以免用错包就尴尬了

package com.mhc.wey.core.httpInterface;


import com.alibaba.fastjson.JSON;

import com.alibaba.fastjson.JSONObject;

import com.mhc.wey.dal.model.HttpInterfaceCaseDO;

import com.subaru.common.entity.BizResult;

import jdk.nashorn.internal.runtime.regexp.joni.exception.ValueException;

import lombok.extern.slf4j.Slf4j;

import org.apache.commons.lang3.StringUtils;

import org.apache.http.*;

import org.apache.http.client.HttpRequestRetryHandler;

import org.apache.http.client.config.RequestConfig;

import org.apache.http.client.entity.UrlEncodedFormEntity;

import org.apache.http.client.methods.CloseableHttpResponse;

import org.apache.http.client.methods.HttpGet;

import org.apache.http.client.methods.HttpPost;

import org.apache.http.client.protocol.HttpClientContext;

import org.apache.http.conn.ConnectTimeoutException;

import org.apache.http.conn.ssl.SSLConnectionSocketFactory;

import org.apache.http.conn.ssl.X509HostnameVerifier;

import org.apache.http.cookie.Cookie;

import org.apache.http.entity.StringEntity;

import org.apache.http.impl.client.CloseableHttpClient;

import org.apache.http.impl.client.HttpClients;

import org.apache.http.impl.client.LaxRedirectStrategy;

import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;

import org.apache.http.message.BasicHeader;

import org.apache.http.message.BasicNameValuePair;

import org.apache.http.protocol.HttpContext;

import org.apache.http.ssl.SSLContextBuilder;

import org.apache.http.ssl.TrustStrategy;

import org.apache.http.util.EntityUtils;

import org.springframework.context.annotation.Lazy;

import org.springframework.stereotype.Service;


import javax.net.ssl.*;

import java.io.IOException;

import java.io.InterruptedIOException;

import java.net.UnknownHostException;

import java.security.GeneralSecurityException;

import java.security.cert.CertificateException;

import java.security.cert.X509Certificate;

import java.util.*;


用到的装饰器:

@Service

@Slf4j

@Lazy


上面用到了两个我司自定义的包,已经标注出来了

com.mhc.wey.dal.model.HttpInterfaceCaseDO为接口测试用例数据(数据库导入)

com.subaru.common.entity.BizResult为消息返回的对象


execute代码如下

BizResult bizResult =null;

//执行请求

BizResult executeHttpRequestResult = executeEncapsulation(httpInterfaceCaseDO);

if (! executeHttpRequestResult.isSuccess()) {

return executeHttpRequestResult;

}

CloseableHttpResponse response = (CloseableHttpResponse) executeHttpRequestResult.getData();

log.info("开始解析Response");

//解析response

try {

if (assertExpected(response, httpInterfaceCaseDO)) {

setReplaceMap(httpInterfaceCaseDO.getResponseBodyTransferedParams(), response);

    }else {

bizResult = BizResult.create(null,false,"9999","assertExpected返回错误");

    }

}catch (NullPointerException e) {

bizResult = BizResult.create(e,false,"9999","获取response实体时出现Exception");

}

finally {

try {

//关闭连接

        response.close();

        client.close();

    }catch (Exception e) {

bizResult = BizResult.create("BOOM!!!",true,"1000","HTTPClient没有关闭");

    }

if (bizResult ==null) {

bizResult = BizResult.create("OK",true,"1000","接口测试通过");

    }

}

return bizResult;


函数的逻辑十分清晰

- 先执行请求

- 断言返回值是否符合预期

- 满足断言,则处理Response, 获取响应对象中部分参数的值


executeEncapsulation代码如下

//参数替换

BizResult replaceHeaderParamsResult = replaceParams(httpInterfaceCaseDO.getRequestHeader());

if (! replaceHeaderParamsResult.isSuccess()) {

return replaceHeaderParamsResult;

}else {

httpInterfaceCaseDO.setRequestHeader(replaceHeaderParamsResult.getData().toString());

}

BizResult replaceBodyParamsResult = replaceParams(httpInterfaceCaseDO.getRequestBody());

if (! replaceBodyParamsResult.isSuccess()) {

return replaceBodyParamsResult;

}else {

httpInterfaceCaseDO.setRequestBody(replaceBodyParamsResult.getData().toString());

}

//参数校验

BizResult initResult = init(httpInterfaceCaseDO);

if (! initResult.isSuccess()) {

return initResult;

}

return executeHttpRequest(httpInterfaceCaseDO);


替换请求头和请求体的值,再进行值检验


replaceParams代码如下

if (StringUtils.isNoneBlank(str)) {

JSONObject bodyObject = JSON.parseObject(str);

            for (String key:

bodyObject.keySet()) {

String keyget = (String) bodyObject.get(key);

                if (keyget.startsWith("$")) {

String replacekey = keyget.substring(keyget.indexOf("{") +1, keyget.indexOf("}")).trim();

                    if (StringUtils.isNoneBlank(replacekey)) {

String value = (String)ReplaceMap.get(replacekey);

                        if (StringUtils.isBlank(value)) {

return BizResult.create(null,false,"9999","ReplaceMap取值异常");

                        }

//                        替换请求体内的变量

                        bodyObject.replace(key, value);

                    }else {

return BizResult.create(null,false,"9999","ReplaceMap取值异常");

                    }

}

}

}

return BizResult.create(str,true,"1000","替换RequestBody里面的转义字符成功");


这里是直接传待转换字符串进来,这个字符串为了方便默认是JSONString,然后就是正则处理下。

这里要注意的是

String value = (String)ReplaceMap.get(replacekey);

因为会有其他测试人员在接口测试时,打错了变量名,这样的场景是我们要去避免的


init代码如下

    if (! checkParams(httpInterfaceCaseDO)) {

return BizResult.create("CheckParameters not pass",false,"9999","Request参数缺失");

}

//RequestMethod检验

if (! (httpInterfaceCaseDO.getRequestMethod().toUpperCase().contentEquals(HttpEnum.GET ) || httpInterfaceCaseDO.getRequestMethod().toUpperCase().contentEquals(HttpEnum.POST ))) {

return BizResult.create("RequestMethod异常",false,"9999","RequestMethod异常");

}

if (StringUtils.isNotEmpty(httpInterfaceCaseDO.getDataFormat()) && StringUtils.isNotBlank(httpInterfaceCaseDO.getDataFormat().trim())) {

if (! (httpInterfaceCaseDO.getDataFormat().toUpperCase().contentEquals(HttpEnum.STRING) || httpInterfaceCaseDO.getDataFormat().toUpperCase().contentEquals(HttpEnum.JSON) || httpInterfaceCaseDO.getDataFormat().toUpperCase().contentEquals(HttpEnum.FORM) || httpInterfaceCaseDO.getDataFormat().toUpperCase().contentEquals(HttpEnum.MEDIA))) {

//如果没有参数类型,默认设置为Srting

        httpInterfaceCaseDO.setDataFormat(HttpEnum.STRING);

    }

}

getSessionId(httpInterfaceCaseDO);

return BizResult.create("continue",true,"6666","初始化测试数据成功");


getSessionId代码如下

    String sessionId ="";

        String body ="";

        try {

if (context ==null) {

log.info("context为空");

            }

List listOfCookies =context.getCookieStore().getCookies();

            for (Cookie cookie:

listOfCookies) {

if (“这里不给看哦”.equals(cookie.getName())) {

return BizResult.create("", true, "1000", "已有sessionId,直接通过");

                }

}

}catch (Exception e) {

log.info("日常没登陆");

        }

if (! httpInterfaceCaseDO.getRequestUrl().contains("login")) {

return BizResult.create(null, true, "9999", "未登录");

        }else {

//涉及我司有关内容,已删除

BizResult executeResult = executeHttpRequest(httpInterfaceCaseDO);

            if (! executeResult.isSuccess()) {

return executeResult;

            }

CloseableHttpResponse response = (CloseableHttpResponse) executeResult.getData();

            if (response.getStatusLine().getStatusCode() != HttpEnum.STATUS_OK) {

return BizResult.create(null, false, "9999", "请求失败");

            }

}

return BizResult.create(sessionId, true, "1000", "请求成功");


这里主要依赖了HTTPContext保持连接的特性,在单线程内,只要不close掉的话,会一直存储Cookie和其他需要的值


executeHttpRequest代码如下

    CloseableHttpResponse response =null;

        HttpEntity entity =null;

        //请求协议

        String requestType = httpInterfaceCaseDO.getRequestUrl().substring(0, httpInterfaceCaseDO.getRequestUrl().indexOf(":", 1)).trim();

//        实例化httpclient

        BizResult requestResult = switchType(requestType);

        if (!requestResult.isSuccess()) {

return requestResult;

        }

//        判断请求类型

        try {

switch (httpInterfaceCaseDO.getRequestMethod().toUpperCase()) {

case HttpEnum.GET:

HttpGet httpGet =null;

//              判断是否有请求体

                    if (StringUtils.isNotEmpty(httpInterfaceCaseDO.getRequestBody()) && StringUtils.isNotBlank(httpInterfaceCaseDO.getRequestBody().trim())) {

String body = httpInterfaceCaseDO.getRequestBody().trim();

//                    url为转换后的请求体

                        StringBuilder url =new StringBuilder();

//                    判断请求体是否是json格式的数据

                        if (body.startsWith("{")) {

JSONObject jsonObject = JSON.parseObject(body);

                            for (String key :

jsonObject.keySet()) {

if (StringUtils.isNotEmpty(url)) {

url.append("&");

                                }

url.append(String.format("%s?%s", key, jsonObject.getString(key)));

                            }

}

//                          有请求体的GET

                        httpGet =new HttpGet(String.format("%s?%s", httpInterfaceCaseDO.getRequestUrl(), url.toString()));

                    }else {

//                        无请求体的GET

                        httpGet =new HttpGet(httpInterfaceCaseDO.getRequestUrl());

                    }

if (StringUtils.isNoneBlank(httpInterfaceCaseDO.getRequestHeader())) {

JSONObject headerObject = JSON.parseObject(httpInterfaceCaseDO.getRequestHeader());

                        for (String key :

headerObject.keySet()) {

httpGet.addHeader(key, (String) headerObject.get(key));

                        }

}

for (Header header :

defaultHeader()) {

httpGet.addHeader(header);

                    }

//执行http get请求

                    response =client.execute(httpGet, context);

break;

                case HttpEnum.POST:

HttpPost httpPost =new HttpPost(httpInterfaceCaseDO.getRequestUrl());

                    if (StringUtils.isEmpty(httpInterfaceCaseDO.getDataFormat())) {

//                  POST方式需要DataFormat字段,无此字段则抛出异常

                        throw new ValueException("POST没有DataFormat字段");

                    }

// 创建请求参数

                    switch (httpInterfaceCaseDO.getDataFormat().toUpperCase()) {

case HttpEnum.STRING:

entity =new StringEntity(httpInterfaceCaseDO.getRequestBody(), "utf-8");

break;

                        case HttpEnum.FORM:

List body =new ArrayList();

                            JSONObject jsonObject = JSON.parseObject(httpInterfaceCaseDO.getRequestBody());

                            for (String key :

jsonObject.keySet()) {

body.add(new BasicNameValuePair(key, jsonObject.getString(key)));

                            }

entity =new UrlEncodedFormEntity(body, "utf-8");

break;

//                      这里暂时不动,考虑下Entity的类型

                        case HttpEnum.MEDIA:

//获取分隔符

                            String[] array = httpInterfaceCaseDO.getRequestHeader().split("=");

                            String bound = array[1].substring(1, array[1].lastIndexOf("\""));

                            //添加分割线

//                      entity = new MultipartRequestEntity();

                            break;

                        default:

break;

                    }

httpPost.setEntity(entity);

                    if (StringUtils.isNoneBlank(httpInterfaceCaseDO.getRequestHeader())) {

JSONObject headerObject = JSON.parseObject(httpInterfaceCaseDO.getRequestHeader());

                        for (String key :

headerObject.keySet()) {

httpPost.addHeader(key, (String) headerObject.get(key));

                        }

}

for (Header header :

defaultHeader()) {

httpPost.addHeader(header);

                    }

//执行http post请求

                    response =client.execute(httpPost, context);

break;

                default:

break;

                }

}catch(IOException | NullPointerException e){

return BizResult.create(e, false, "9999", "请求异常 -  " + httpInterfaceCaseDO.getRequestMethod().toUpperCase());

        }

return BizResult.create(response, true, "1000", "请求成功");


这块代码也是我负责的项目中最长最重要的一块,你们可以看到我POST的MEDIA方式,是还没写完的,不好意思,写到一半的时候,被告知我司的接口需要用到该场景的次数极少,故不了了之,代码长就长在对String的处理,其实像

JSONObject jsonObject = JSON.parseObject(body);

for (String key :

headerObject.keySet()) {

httpPost.addHeader(key, (String) headerObject.get(key));

}

这种代码是可以的整合的,但写完就不想动了。。。

简书不是支持MarkDown的么,写完一发,看到格式不对,真是哔到gou了

暂时先到这,再熬夜怕是要变强了😭

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,884评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,347评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,435评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,509评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,611评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,837评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,987评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,730评论 0 267
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,194评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,525评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,664评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,334评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,944评论 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,764评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,997评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,389评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,554评论 2 349