1. content-type
场景:使用JMeter测试spring的REST接口
rest 接口定义如下
@RequestMapping (value = "/client/join" , method = RequestMethod.POST)
@ResponseBody
public String clientJoined(@RequestBody String requestBody, HttpServletResponse response)
{
... ...
}
调试时发现@RequestBody标注的requestBody参数值是被encode过的字符串
%3Ccontent%3E%0D%0A%09%3Cdict%3E%0D%0A%09%09%3CAPMacAddress%3E112211221122%3C%2FAPMacAddress%3E%0D%0A%09%09%3CUPID%3E%3C%2FUPID%3E%0D%0A%09%09%3CClientMacAddress%3E%3C%2FClientMacAddress%3E%0D%0A%09%09%3CIsProvisioning%3E%3C%2FIsProvisioning%3E%0D%0A%09%09%3CSecurityType%3E%3C%2FSecurityType%3E%0D%0A%09%09%3CSSID%3E%3C%2FSSID%3E%0D%0A%09%09%3CHMIPAddress%3E%3C%2FHMIPAddress%3E%0D%0A%09%09%3CHMHostName%3E%3C%2FHMHostName%3E%09%09%0D%0A%09%3C%2Fdict%3E%0D%0A%3C%2Fcontent%3E=
原因是未指定http头content-type时,JMeter默认使用application/x-www-formurlencoded提交http请求。用wireshark抓包可以看到该行为
因此为了能让让rest接口的@RequestBody注解获取到不经过encode的文本,
需要为http添加content-type,可以是 text/plain。
--------------------------
--------------------------
--------------------------
--------------------------
-
2.
@RequestBody注解
Spring3.2对注解@RequestBody的解析代码段是
public InputStream getBody() throws IOException {
if (isFormPost(this.servletRequest)) {
return getBodyFromServletRequestParameters(this.servletRequest);
}
else {
return this.servletRequest.getInputStream();
}
}
其中的if分支判断request是否有http header content-type并且content-type包含字符串
applicationx/x-www-form-urlencoded
。如果成立那么spring会从request的parameter map构造一个request body,否则直接返回request的input stream。
之所以有这个分支是因为以application/x-www-form-urlencoded提交的请求,web容器(如tomcat)需要读取request的input stream做参数的解析,消费了input stream的结果是得到了request的参数。
由于input stream只能读取一次,
因此当在web app层通过@RequestBody试图获取request body时,spring会遍历request参数重新构造request body,但参数的顺序可能会与原始http请求不同。图示如下