在测试过程中,有一个重要的工作就是保存记录“现场”,以方便开发人员更快发现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”,禁止第三方(腾讯云除外)转载、发表。

技术类文章精选

非技术文章精选

如何保存HTTPrequestbase和CloseableHttpResponse的更多相关文章

  1. HttpClient的get+post请求使用

    啥都不说,先上代码 import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReade ...

  2. java后台中处理图片辅助类汇总(上传图片到服务器,从服务器下载图片保存到本地,缩放图片,copy图片,往图片添加水印图片或者文字,生成二维码,删除图片等)

    最近工作中处理小程序宝箱活动,需要java画海报,所以把这块都快百度遍了,记录一下处理的方法,百度博客上面也有不少坑! 获取本地图片路径: String bgPath = Thread.current ...

  3. Spring Boot+微信小程序_保存微信登录者的个人信息

    1. 前言 微信小程序开发平台,提供有一类 API,可以让开发者获取到微信登录用户的个人数据.这类 API 统称为开放接口. Tip:微信小程序开发平台,会把微信登录用户的个人信息分为明文数据和敏感数 ...

  4. 使用Python保存屏幕截图(不使用PIL)

    起因 在极客学院讲授<使用Python编写远程控制程序>的课程中,涉及到查看被控制电脑屏幕截图的功能. 如果使用PIL,这个需求只需要三行代码: from PIL import Image ...

  5. C# 对象实例化 用json保存 泛型类 可以很方便的保存程序设置

    参考页面: http://www.yuanjiaocheng.net/webapi/test-webapi.html http://www.yuanjiaocheng.net/webapi/web-a ...

  6. C# 对象实例化 用json保存 泛型类 可以很方便的保存程序设置

    用于永久化对象,什么程序都行,依赖NewtonSoft.用于json序列化和反序列化. using Newtonsoft.Json; using System; using System.Collec ...

  7. MATLAB中绘制质点轨迹动图并保存成GIF

    工作需要在MATLAB中绘制质点轨迹并保存成GIF以便展示. 绘制质点轨迹动图可用comet和comet3命令,使用例子如下: t = 0:.01:2*pi;x = cos(2*t).*(cos(t) ...

  8. mono中发送邮件并保存本次收件人的地址

    在ios端mono开发中,发送邮件可以选择调用ios原生email程序.有两种方式实现这种功能,一是程序跳转到ipad中email程序,另外一种是将发送邮件的界面在自己应用里弹出. 首先第一种方式的代 ...

  9. mono for android 获取手机照片或拍照并裁剪保存

    axml <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android ...

随机推荐

  1. jenkins集成错误 标签: 发布 2016-01-10 20:45 747人阅读 评论(21) 收藏

    进入ITOO的项目以后,终于要将自己负责的模块在jenkins上面集成发布了.首先自己按照文档要求一步一步的将配置完成,然后构建,不错所料出错了,经过修改,终于构建成功!构建成功以后就没再管了,结果第 ...

  2. include 语句中使用双引号与括号有什么区别?

    Include 的语法 你在学习如何构造函数时,看到了不同的 include 语句: # include <iostream> # include "distance.h&quo ...

  3. HZOJ 题

    首先对于n<=100的点,直接暴力dp,f[i][j][k]表示时间为i,在i,j位置的方案数,枚举转移即可,期望得分40. ) { ) { f[][][]=; ;i<=n;i++) ;x ...

  4. hdu 5744 Keep On Movin (2016多校第二场)

    Keep On Movin Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)Tot ...

  5. HTML的优化

    HTML的优化 : 1).h标签的使用: 要注意的是,不论任何页面,h1标签只能出现一次,它是当前页面的主标题,权重最高, 所以要慎用 . 一般情况下,如果有关键词的话最好是在h1里面出现. h2是表 ...

  6. Android Button点击效果(按钮背景变色、文字变色)

    一. 说明 Android Button的使用过程中,我们会需要为Button添加点击效果,不仅仅按钮的背景色需要变化,而且有时,我们连文字的颜色都希望变化,我们可以使用StateListDrawab ...

  7. PTA 6-2 多项式求值

    PTA 6-2 多项式求值 本题要求实现一个函数 本题要求实现一个函数,计算阶数为n,系数为a[0] ... a[n]的多项式f(x)=∑i=0n(a[i]×xi)" role=" ...

  8. python基础十五之递归函数

    递归函数,在函数中调用自身函数,就会形成一个递归函数.例如: def recursion(n): n += 1 print(n) recursion(n) 由于递归函数的结构,在函数调用时,它会一直调 ...

  9. H3C 主机名与IP地址映射需求

  10. WPF 元素裁剪 Clip 属性

    本文介绍如何在 WPF 使用 Clip 裁剪元素 在 WPF 的 UIElement 提供了 Clip 属性,这个属性默认是空,但是如果设置了这个属性就会对元素进行裁剪 这个属性是一个 Geometr ...