基礎(chǔ)的HTTPClient搭建注意事項(中)

項目結(jié)構(gòu)更新:


結(jié)構(gòu)圖V1.1

修改了replaceBodyParamsreplaceParams,更加直觀

新加了asserExpected函數(shù)作為判斷斷言類似的,接口測試不只要測成功的例子蹲坷,也要測失敗的例子。當初真的是想當然了,這個函數(shù)我遇到了些麻煩,會在文章結(jié)尾說明下举娩。


首先,放上該項目所用到的包依賴,以免用錯包就尷尬了

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


上面用到了兩個我司自定義的包铜涉,已經(jīng)標注出來了

com.mhc.wey.dal.model.HttpInterfaceCaseDO為接口測試用例數(shù)據(jù)(數(shù)據(jù)庫導入)

com.subaru.common.entity.BizResult為消息返回的對象


execute代碼如下

BizResult bizResult =null;

//執(zhí)行請求

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實體時出現(xiàn)Exception");

}

finally {

try {

//關(guān)閉連接

? ? ? ? response.close();

? ? ? ? client.close();

? ? }catch (Exception e) {

bizResult = BizResult.create("BOOM!!!",true,"1000","HTTPClient沒有關(guān)閉");

? ? }

if (bizResult ==null) {

bizResult = BizResult.create("OK",true,"1000","接口測試通過");

? ? }

}

return bizResult;


函數(shù)的邏輯十分清晰

- 先執(zhí)行請求

- 斷言返回值是否符合預期

- 滿足斷言智玻,則處理Response, 獲取響應對象中部分參數(shù)的值


executeEncapsulation代碼如下

//參數(shù)替換

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());

}

//參數(shù)校驗

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取值異常");

? ? ? ? ? ? ? ? ? ? ? ? }

//? ? ? ? ? ? ? ? ? ? ? ? 替換請求體內(nèi)的變量

? ? ? ? ? ? ? ? ? ? ? ? bodyObject.replace(key, value);

? ? ? ? ? ? ? ? ? ? }else {

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

? ? ? ? ? ? ? ? ? ? }

}

}

}

return BizResult.create(str,true,"1000","替換RequestBody里面的轉(zhuǎn)義字符成功");


這里是直接傳待轉(zhuǎn)換字符串進來吊奢,這個字符串為了方便默認是JSONString,然后就是正則處理下纹烹。

這里要注意的是

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

因為會有其他測試人員在接口測試時页滚,打錯了變量名,這樣的場景是我們要去避免的


init代碼如下

? ??if (! checkParams(httpInterfaceCaseDO)) {

return BizResult.create("CheckParameters not pass",false,"9999","Request參數(shù)缺失");

}

//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))) {

//如果沒有參數(shù)類型铺呵,默認設置為Srting

? ? ? ? httpInterfaceCaseDO.setDataFormat(HttpEnum.STRING);

? ? }

}

getSessionId(httpInterfaceCaseDO);

return BizResult.create("continue",true,"6666","初始化測試數(shù)據(jù)成功");


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 {

//涉及我司有關(guān)內(nèi)容,已刪除

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保持連接的特性片挂,在單線程內(nèi)邦马,只要不close掉的話,會一直存儲Cookie和其他需要的值


executeHttpRequest代碼如下

? ??CloseableHttpResponse response =null;

? ? ? ? HttpEntity entity =null;

? ? ? ? //請求協(xié)議

? ? ? ? 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為轉(zhuǎn)換后的請求體

? ? ? ? ? ? ? ? ? ? ? ? StringBuilder url =new StringBuilder();

//? ? ? ? ? ? ? ? ? ? 判斷請求體是否是json格式的數(shù)據(jù)

? ? ? ? ? ? ? ? ? ? ? ? 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);

? ? ? ? ? ? ? ? ? ? }

//執(zhí)行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字段");

? ? ? ? ? ? ? ? ? ? }

// 創(chuàng)建請求參數(shù)

? ? ? ? ? ? ? ? ? ? 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);

? ? ? ? ? ? ? ? ? ? }

//執(zhí)行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方式,是還沒寫完的,不好意思社牲,寫到一半的時候出牧,被告知我司的接口需要用到該場景的次數(shù)極少,故不了了之蛾扇,代碼長就長在對String的處理攘烛,其實像

JSONObject jsonObject = JSON.parseObject(body);

for (String key :

headerObject.keySet()) {

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

}

這種代碼是可以的整合的,但寫完就不想動了镀首。坟漱。。

簡書不是支持MarkDown的么更哄,寫完一發(fā)芋齿,看到格式不對,真是嗶到gou了

暫時先到這成翩,再熬夜怕是要變強了??

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末觅捆,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子麻敌,更是在濱河造成了極大的恐慌栅炒,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,826評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異赢赊,居然都是意外死亡乙漓,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,968評論 3 395
  • 文/潘曉璐 我一進店門域携,熙熙樓的掌柜王于貴愁眉苦臉地迎上來簇秒,“玉大人,你說我怎么就攤上這事秀鞭∏鞴郏” “怎么了?”我有些...
    開封第一講書人閱讀 164,234評論 0 354
  • 文/不壞的土叔 我叫張陵锋边,是天一觀的道長皱坛。 經(jīng)常有香客問我,道長豆巨,這世上最難降的妖魔是什么剩辟? 我笑而不...
    開封第一講書人閱讀 58,562評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮往扔,結(jié)果婚禮上贩猎,老公的妹妹穿的比我還像新娘。我一直安慰自己萍膛,他們只是感情好吭服,可當我...
    茶點故事閱讀 67,611評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著蝗罗,像睡著了一般艇棕。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上串塑,一...
    開封第一講書人閱讀 51,482評論 1 302
  • 那天沼琉,我揣著相機與錄音,去河邊找鬼桩匪。 笑死打瘪,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的吸祟。 我是一名探鬼主播瑟慈,決...
    沈念sama閱讀 40,271評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼屋匕!你這毒婦竟也來了葛碧?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,166評論 0 276
  • 序言:老撾萬榮一對情侶失蹤过吻,失蹤者是張志新(化名)和其女友劉穎进泼,沒想到半個月后蔗衡,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,608評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡乳绕,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,814評論 3 336
  • 正文 我和宋清朗相戀三年绞惦,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片洋措。...
    茶點故事閱讀 39,926評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡济蝉,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出菠发,到底是詐尸還是另有隱情王滤,我是刑警寧澤,帶...
    沈念sama閱讀 35,644評論 5 346
  • 正文 年R本政府宣布滓鸠,位于F島的核電站雁乡,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏糜俗。R本人自食惡果不足惜踱稍,卻給世界環(huán)境...
    茶點故事閱讀 41,249評論 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望悠抹。 院中可真熱鬧珠月,春花似錦、人聲如沸楔敌。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,866評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽梁丘。三九已至,卻和暖如春旺韭,著一層夾襖步出監(jiān)牢的瞬間氛谜,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,991評論 1 269
  • 我被黑心中介騙來泰國打工区端, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留值漫,地道東北人。 一個月前我還...
    沈念sama閱讀 48,063評論 3 370
  • 正文 我出身青樓织盼,卻偏偏與公主長得像杨何,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子沥邻,可洞房花燭夜當晚...
    茶點故事閱讀 44,871評論 2 354