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

項目結(jié)構更新:


結(jié)構圖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 {

//關閉連接

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


函數(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 {

//涉及我司有關內(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了

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

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

友情鏈接更多精彩內(nèi)容