如何保存HTTPrequestbase和CloseableHttpResponse
在测试过程中,有一个重要的工作就是保存记录“现场”,以方便开发人员更快发现BUG解决问题。在接口测试中更是如此,如果开发人员能够根据BUG的信息直接复现请求,是一件很方便的事情。为此我想了一个再框架中增加保存HTTPrequestbase和CloseableHttpResponse两个对象的功能,其中主要是HTTPrequestbase的信息,CloseableHttpResponse以响应内容为主,因为每次请求我都会把必要信息(host,API,HTTP code,响应code,响应时间等等记录)。
下面是更新过的funrequest类的代码,更新内容时后面几个静态方法:
package com.fun.frame.httpclient
import com.fun.base.bean.RequestInfo
import com.fun.base.exception.RequestException
import com.fun.config.HttpClientConstant
import com.fun.config.RequestType
import com.fun.frame.Save
import com.fun.utils.Time
import net.sf.json.JSONObject
import org.apache.commons.lang3.StringUtils
import org.apache.http.Header
import org.apache.http.HttpEntity
import org.apache.http.client.methods.HttpPost
import org.apache.http.client.methods.HttpRequestBase
import org.apache.http.util.EntityUtils
import org.slf4j.Logger
import org.slf4j.LoggerFactory
/**
* 重写FanLibrary,使用面对对象思想
*/
public class FunRequest extends FanLibrary implements Serializable, Cloneable {
private static final long serialVersionUID = -4153600036943378727L;
static Logger logger = LoggerFactory.getLogger(FunRequest.class)
/**
* 请求类型,true为get,false为post
*/
RequestType requestType
/**
* 请求对象
*/
HttpRequestBase request
/**
* host地址
*/
String host = EMPTY
/**
* 接口地址
*/
String apiName = EMPTY
/**
* 请求地址,如果为空则由host和apiname拼接
*/
String uri = EMPTY
/**
* header集合
*/
List<Header> headers = new ArrayList<>()
/**
* get参数
*/
JSONObject args = new JSONObject()
/**
* post参数,表单
*/
JSONObject params = new JSONObject()
/**
* json参数
*/
JSONObject json = new JSONObject()
/**
* 响应,若没有这个参数,从将funrequest对象转换成json对象时会自动调用getresponse方法
*/
JSONObject response = new JSONObject()
/**
* 构造方法
*
* @param requestType
*/
private FunRequest(RequestType requestType) {
this.requestType = requestType
}
/**
* 获取get对象
*
* @return
*/
static FunRequest isGet() {
new FunRequest(RequestType.GET)
}
/**
* 获取post对象
*
* @return
*/
static FunRequest isPost() {
new FunRequest(RequestType.POST)
}
/**
* 设置host
*
* @param host
* @return
*/
FunRequest setHost(String host) {
this.host = host
this
}
/**
* 设置接口地址
*
* @param apiName
* @return
*/
FunRequest setApiName(String apiName) {
this.apiName = apiName
this
}
/**
* 设置uri
*
* @param uri
* @return
*/
FunRequest setUri(String uri) {
this.uri = uri
this
}
/**
* 添加get参数
*
* @param key
* @param value
* @return
*/
FunRequest addArgs(Object key, Object value) {
args.put(key, value)
this
}
/**
* 添加post参数
*
* @param key
* @param value
* @return
*/
FunRequest addParam(Object key, Object value) {
params.put(key, value)
this
}
/**
* 添加json参数
*
* @param key
* @param value
* @return
*/
FunRequest addJson(Object key, Object value) {
json.put(key, value)
this
}
/**
* 添加header
*
* @param key
* @param value
* @return
*/
FunRequest addHeader(Object key, Object value) {
headers << getHeader(key.toString(), value.toString())
this
}
/**
* 添加header
*
* @param header
* @return
*/
public FunRequest addHeader(Header header) {
headers.add(header)
this
}
/**
* 批量添加header
*
* @param header
* @return
*/
FunRequest addHeader(List<Header> header) {
header.each {h -> headers << h}
this
}
/**
* 增加header中cookies
*
* @param cookies
* @return
*/
FunRequest addCookies(JSONObject cookies) {
headers << getCookies(cookies)
this
}
FunRequest setHeaders(List<Header> headers) {
this.headers.addAll(headers)
this
}
FunRequest setArgs(JSONObject args) {
this.args.putAll(args)
this
}
FunRequest setParams(JSONObject params) {
this.params.putAll(params)
this
}
FunRequest setJson(JSONObject json) {
this.json.putAll(json)
this
}
/**
* 获取请求响应,兼容相关参数方法,不包括file
*
* @return
*/
JSONObject getResponse() {
response = response.isEmpty() ? getHttpResponse(request == null ? getRequest() : request) : response
response
}
/**
* 获取请求对象
*
* @return
*/
HttpRequestBase getRequest() {
if (request != null) request;
if (StringUtils.isEmpty(uri))
uri = host + apiName
switch (requestType) {
case RequestType.GET:
request = FanLibrary.getHttpGet(uri, args)
break
case RequestType.POST:
request = !params.isEmpty() ? FanLibrary.getHttpPost(uri + changeJsonToArguments(args), params) : !json.isEmpty() ? getHttpPost(uri + changeJsonToArguments(args), json.toString()) : getHttpPost(uri + changeJsonToArguments(args))
break
}
for (Header header in headers) {
request.addHeader(header)
}
logger.debug("请求信息:{}", new RequestInfo(this.request).toString())
request
}
@Override
FunRequest clone() {
def fun = new FunRequest()
fun.setRequest(cloneRequest(getRequest()))
fun
}
@Override
public String toString() {
return "{" +
"requestType='" + requestType.getName() + '\'' +
", host='" + host + '\'' +
", apiName='" + apiName + '\'' +
", uri='" + uri + '\'' +
", headers=" + header2Json(headers).toString() +
", args=" + args.toString() +
", params=" + params.toString() +
", json=" + json.toString() +
", response=" + getResponse().toString() +
'}';
}
/**
* 从requestbase对象从初始化funrequest
* @param base
* @return
*/
static FunRequest initFromRequest(HttpRequestBase base) {
FunRequest request = null;
String method = base.getMethod();
RequestType requestType = RequestType.getRequestType(method);
String uri = base.getURI().toString();
List<Header> headers = Arrays.asList(base.getAllHeaders());
if (requestType == requestType.GET) {
request = FunRequest.isGet().setUri(uri).setHeaders(headers);
} else if (requestType == RequestType.POST || requestType == RequestType.FUN) {
HttpPost post = (HttpPost) base;
HttpEntity entity = post.getEntity();
String value = entity.getContentType().getValue();
String content = null;
try {
content = EntityUtils.toString(entity);
} catch (IOException e) {
logger.error("解析响应失败!", e)
fail();
}
if (value.equalsIgnoreCase(HttpClientConstant.ContentType_TEXT.getValue()) || value.equalsIgnoreCase(HttpClientConstant.ContentType_JSON.getValue())) {
request = FunRequest.isPost().setUri(uri).setHeaders(headers).setJson(JSONObject.fromObject(content));
} else if (value.equalsIgnoreCase(HttpClientConstant.ContentType_FORM.getValue())) {
request = FunRequest.isPost().setUri(uri).setHeaders(headers).setParams(getJson(content.split("&")));
}
} else {
RequestException.fail("不支持的请求类型!");
}
return request;
}
/**
* 拷贝HttpRequestBase对象
* @param base
* @return
*/
static HttpRequestBase cloneRequest(HttpRequestBase base) {
return initFromRequest(base).getRequest()
}
/**
* 保存请求和响应
* @param base
* @param response
*/
public static void save(HttpRequestBase base, JSONObject response) {
FunRequest request = initFromRequest(base)
request.setResponse(response);
Save.info("/request/" + Time.getDate().substring(8) + SPACE_1 + request.getUri().replace(OR, PART), request.toString());
}
}
然后在框架是添加一个key来控制是否保存响应,然后调用保存方法:
if (SAVE_KEY) FunRequest.save(request, res);
其中,res是响应内容,已经解析为json格式,对于非json格式响应做了兼容。同事在保存路径和保存量也做配置初始化的过程中做了校验,这个太简单就不发了。
其中一个header2Json方法是为了解决保存header时候不必须信息太多的问题,内容如下:
/**
* 将header转成json对象
*
* @param headers
* @return
*/
public static JSONObject header2Json(List<Header> headers) {
JSONObject h = new JSONObject();
headers.forEach(x -> h.put(x.getName(), x.getValue()));
return h;
}
这个是内容:
{requestType='post', host='', apiName='', uri='https://cn.bing.com/search', headers={"Content-Type":"application/x-www-form-urlencoded; charset=UTF-8"}, args={}, params={"q":"fun"}, json={}, response={"3242":"234"}}
这样做的好处就是,如果想复现某个出现问题的request,直接从文件中读取保存的request信息,借由funrequest类对象即可复现这个请求,还可以跟记录的response做对比。
- 郑重声明:文章首发于公众号“FunTester”,禁止第三方(腾讯云除外)转载、发表。
技术类文章精选
- java一行代码打印心形
- Linux性能监控软件netdata中文汉化版
- 性能测试框架第二版
- 如何在Linux命令行界面愉快进行性能测试
- 图解HTTP脑图
- 将swagger文档自动变成测试代码
- 五行代码构建静态博客
- 基于java的直线型接口测试框架初探
- Selenium 4.0 Alpha更新日志
- Selenium 4.0 Alpha更新实践
非技术文章精选
- 为什么选择软件测试作为职业道路?
- 写给所有人的编程思维
- 成为自动化测试的7种技能
- 如何在DevOps引入自动化测试
- Web端自动化测试失败原因汇总
- 如何在DevOps引入自动化测试
- 测试人员常用借口
- 2019年浏览器市场份额排行榜
- API测试基础
- API自动化测试指南
如何保存HTTPrequestbase和CloseableHttpResponse的更多相关文章
- HttpClient的get+post请求使用
啥都不说,先上代码 import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReade ...
- java后台中处理图片辅助类汇总(上传图片到服务器,从服务器下载图片保存到本地,缩放图片,copy图片,往图片添加水印图片或者文字,生成二维码,删除图片等)
最近工作中处理小程序宝箱活动,需要java画海报,所以把这块都快百度遍了,记录一下处理的方法,百度博客上面也有不少坑! 获取本地图片路径: String bgPath = Thread.current ...
- Spring Boot+微信小程序_保存微信登录者的个人信息
1. 前言 微信小程序开发平台,提供有一类 API,可以让开发者获取到微信登录用户的个人数据.这类 API 统称为开放接口. Tip:微信小程序开发平台,会把微信登录用户的个人信息分为明文数据和敏感数 ...
- 使用Python保存屏幕截图(不使用PIL)
起因 在极客学院讲授<使用Python编写远程控制程序>的课程中,涉及到查看被控制电脑屏幕截图的功能. 如果使用PIL,这个需求只需要三行代码: from PIL import Image ...
- C# 对象实例化 用json保存 泛型类 可以很方便的保存程序设置
参考页面: http://www.yuanjiaocheng.net/webapi/test-webapi.html http://www.yuanjiaocheng.net/webapi/web-a ...
- C# 对象实例化 用json保存 泛型类 可以很方便的保存程序设置
用于永久化对象,什么程序都行,依赖NewtonSoft.用于json序列化和反序列化. using Newtonsoft.Json; using System; using System.Collec ...
- MATLAB中绘制质点轨迹动图并保存成GIF
工作需要在MATLAB中绘制质点轨迹并保存成GIF以便展示. 绘制质点轨迹动图可用comet和comet3命令,使用例子如下: t = 0:.01:2*pi;x = cos(2*t).*(cos(t) ...
- mono中发送邮件并保存本次收件人的地址
在ios端mono开发中,发送邮件可以选择调用ios原生email程序.有两种方式实现这种功能,一是程序跳转到ipad中email程序,另外一种是将发送邮件的界面在自己应用里弹出. 首先第一种方式的代 ...
- mono for android 获取手机照片或拍照并裁剪保存
axml <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android ...
随机推荐
- iOS编译错误#ld: warning: ignoring file# 之 Undefined symbols for architecture x86_64 - ld: symbol(s) not found for architecture x86_64
ld: warning: ignoring file xxxPath/libbaidumapapi.a, missing required architecture x86_64 in file xx ...
- react-cli
更新日志: v1.2.0 ...未完待续 v1.1.0 添加editorconfig 配置ESLint 集成prettier 集成 lint-staged 实现细节: 添加editorconfig e ...
- 使用css制作三角
1. 字符实现三角效果关于字符实现三角我早在09年的时候就介绍了:使用字符实现兼容性的圆角尖角效果.一转眼两年过去了,这个技术开始被越来越多的人所熟知.使用的字符是正棱形“◆”字符,编码表示为◆ . ...
- BKDRhash
哈希: 字符串(数字同理): 例如有100000个字符串,现在要插入一些字符串,插入前比较是否已经存在避免含有重复数据 用暴力计较的话会比较慢,在某字符串插入时,最好的情况是在第一个位置就遇见该字符 ...
- Android 使用Toolbar+DrawerLayout快速实现仿“知乎APP”侧滑导航效果
在以前,做策划导航的时候,最常用的组件便是SlidingMenu了,当初第一次用它的时候觉得那个惊艳啊,体验可以说是非常棒. 后来,Android自己推出了一个可以实现策划导航的组件DrawerLay ...
- Hadoop入门进阶步步高(三)-配置Hadoop
三.配置Hadoop 1.设置$HADOOP_HOME/conf/hadoop-env.sh 这个文件里设置的是Hadoop运行时须要的环境变量,在1.2.1版中共同拥有19个环境变量.例如以下: ...
- TensorFlow 中的卷积网络
TensorFlow 中的卷积网络 是时候看一下 TensorFlow 中的卷积神经网络的例子了. 网络的结构跟经典的 CNNs 结构一样,是卷积层,最大池化层和全链接层的混合. 这里你看到的代码与你 ...
- Nacos: Namespace 和 Endpoint 在生产环境下的最佳实践
随着使用 Nacos 的企业越来越多,遇到的最频繁的两个问题就是:如何在我的生产环境正确的来使用 namespace 以及 endpoint.这篇文章主要就是针对这两个问题来聊聊使用 nacos 过程 ...
- CRF(条件随机场)与Viterbi(维特比)算法原理详解
摘自:https://mp.weixin.qq.com/s/GXbFxlExDtjtQe-OPwfokA https://www.cnblogs.com/zhibei/p/9391014.html C ...
- Python--day62--编辑出版社功能
1,Django项目主要用到的文件