1、使用HttpClient发送Get/Post请求
a)Get请求
代码示例:
public static String getHttpResponse4Get() {
CloseableHttpClient httpclient = HttpClients.createDefault();
String url = "http://www.tmall.com/test.do?id=123";
GetMethod httpGet = new GetMethod(url);
httpGet.addRequestHeader("content-type", "text/html; charset=gbk");
httpGet.getParams().setParameter("http.socket.timeout", 20000);
try {
// 设置成了默认的恢复策略,在发生异常时候将自动重试3次,在这里你也可以设置成自定义的恢复策略
httpGet.getParams().setParameter(HttpMethodParams.RETRY_HANDLER,new DefaultHttpMethodRetryHandler());
if (httpclient.executeMethod(httpGet) != HttpStatus.SC_OK) {
System.out.println("httpGet(\"" + url + "\") failed: \n"+ httpGet.getStatusLine());
return null;
}
return httpGet.getResponseBodyAsString();
} catch (Exception e) {
System.out.println("httpGet(\"" + url + "\") failed: \n" + e.getMessage());
return null;
} finally {
httpGet.releaseConnection();
httpclient = null;
}
}
注意点:
1、传入参数值(或者参数串)需要进行encode,虽然httpClient中是有用URLCodec.encodeUrl()进行encode,但我实验发现,和URLEncoder.encode()进行encode的结果还是有区别,会对最终响应有影响,所以建议自行encode;
2、需要判断请求返回的状态码是否为200以后获取到的响应才是可靠的;
3、最终要释放掉链接,做好收尾工作。
b)Post请求
代码示例:
public static String getHttpResponse4Post() {
try {
HttpClient client = new DefaultHttpClient();
HttpPost request = new HttpPost("http://www.tmall.com/test.do");
// 使用NameValuePair来保存要传递的Post参数
List postParameters = new ArrayList();
// 添加要传递的参数
postParameters.add(new BasicNameValuePair("id", "12345"));
postParameters.add(new BasicNameValuePair("username", "dave"));
// 实例化UrlEncodedFormEntity对象
UrlEncodedFormEntity formEntity = new UrlEncodedFormEntity(
postParameters);
// 使用HttpPost对象来设置UrlEncodedFormEntity的Entity
request.setEntity(formEntity);
HttpResponse response = client.execute(request);
if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
String s = EntityUtils.toString(response.getEntity(), "utf-8");
System.out.println(s);
System.out.println("请求正常,结束http请求");
return s;
}
} catch (Exception e) {
System.out.println("请求发生异常,异常信息抛出");
e.printStackTrace();
} finally {
httpClient.getConnectionManager().shutdown();
}
return " ";
}
c)在线上环境发送get请求
要将在daily下运行的http接口测试脚本放到线上运行,需要做如下一些操作:
1、建立一个真实的登录session:发送post请求到:https://login.taobao.com/member/login.jhtml
2、然后发送get请求到http://i.taobao.com/my_taobao.htm从缓存中同步一次cookie
3、通过this.httpClientLogin.getState().getCookies();获取到cookie信息,然后解析到_tb_token_的值
4、把这个_tb_token_这个参数和值附加到测试请求的url中,通过get方式进行请求即可
5、注意:在登录的post参数设置时,需要将字符集设为:GBK
postMethod.getParams().setParameter(HttpMethodParams.HTTP_CONTENT_CHARSET, "GBK");
如果改为utf-8或者不set此param就可能导致获取的cookie值不足的问题
(本部分感谢浣碧同学的悉心指点!)
2、urlencode/urldecode
为什么url需要encode以后才能发送,是因为URL需要转化为ASCII字符集才能在被HTTP协议使用。ASCII字符集7位的字符集,包含128个字符,其中常见的有数字1-9,还有a-zA-Z以及一些特殊字符。当URL中包含非ASCII字符集的字符的时候,就需要encode为ASCII字符集,以便通过HTTP协议发送请求
Java中进行URL Encode和Decode的方法是:
URLEncoder.encode(valueOfUrlParam, "UTF-8");
URLDecoder.decode(urlParamStr, "UTF-8");
两个类都来自java.net的包,平时可以使用online的encode/decode网站进行操作
这里需要注意的是:
只能对URL的参数进行encode(可以把整个参数串进行encode,也可以只对参数值进行encode,不能对域名和URI进行encode,例如:
http://www.tmall.com/test.do?id=123,234,正确的encode以后的url为:
http://www.tmall.com/test.do?id%3d123%2c234 或者:
http://www.tmall.com/test.do?id=123%2c234
在用post发请求的时候,需要将参数内容组装成HttpEntity然后set到post对象中,此时会用到UrlEncodedFormEntity这个类,这类的构造函数里会把post的参数进行一次encode,所以自己不需要额外进行一次encode,否则应用服务器在一次decode情况下就会读取错误的参数值
3、mock
a) 参数mock
问题场景:在服务端应用程序里通常会有这样的片段:
public static Long getLoginUserId(TurbineRunData rundata) {
String userId = (String) rundata.getRequest().getSession()
.getAttribute(SessionKeeper.ATTRIBUTE_USER_ID_NUM);
if (StringUtils.isNumeric(userId)) {
return Long.parseLong(userId);
}
return null;
}
if (userId==null || userId <= 0) {
result.setSuccess(false);
result.setErrorCode(MallBrandResultCode.USER_NOT_LOGIN);
result.setErrorMsg("参数user_id缺失");
}
如果webx不能从session中获取到userID,那就会被业务逻辑控制住,不能继续往下走,解决办法有两个:
l.往session里塞userId
要实现这种方案,首先需要创建一个真实daily下用户登录的session,由于是在daily下,还需要解决安全认证的问题,这样便能解决这个问题。但明显这样做的成本太高,而且不是很必要。
l通过get参数mock掉这段代码逻辑
和开发约定一个参数和参数值,在程序中判断如果有这个参数值则直接返回一个指定的userId。
b)压缩mock
为了防止前端发送的json数据时http的get请求超长,前后端开发通过7zip进行压缩,但httpClient认为这样的压缩过的请求是BadRequest,所以只能试图把请求换成post,同时把解压缩的逻辑mock掉,以便于daily下的接口测试。具体详见:《http接口测试中有关URL长度限制的问题(Maximum URL Length)》
c)csrf控制mock
代码中为了防止csrf攻击,都会加入csrf的控制模块,在daily下可以直接让开发把这段逻辑注释掉,但如果脚本还要在系统上线后回归,那就需要和开发一起把这段逻辑mock掉,以简化测试应用的复杂度,降低测试脚本开发难度。