一、环境
web服务器Tomcat7.0.59、开发工具eclipse、web应用采用springMVC架构
https服务器单向认证,keystore为自制,仅供测试使用
二、传输数据(...表示展示部分数据)KJ881110_CORP_2012121212121200001 KJ881110 TEST00001 KJGGPT KJGGPT 20121212121212 1.0 待定
PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPEludGVybmF0aW9uYWxUcmFkZT4KCTxIZWFkPgoJCTxNZXNzYWdlSUQ+S0o4ODExMTBfQ09...
三、问题描述以下问题的发生是基于传输数据中存在特殊字符(例如+,不进行encode的话,网络传输后会变成空格),本人觉得需要对传输数据进行encode。
(1)采用httpspost方式使用HttpURLConnection调用,发现encode后的数据传到后台无需手动decode,却自动转化成原始数据
(2)在jsp页面通过js调用接口,发现前端无需对传输数据进行encode,后台也无需decode
四、详情
(1)https post传输数据,代码如下:
public static String sendPost(String path, String param) {
URL url = null;
OutputStreamWriter out = null;
BufferedReader in = null;
String result = "";
try {
url = new URL(path);
HttpURLConnection httpURLConnection = (HttpURLConnection) url.openConnection();
httpURLConnection.setRequestMethod("POST");
httpURLConnection.setDoOutput(true);
httpURLConnection.setDoInput(true);
out = new OutputStreamWriter(httpURLConnection.getOutputStream(), "UTF-8");
out.write(param);
out.flush();
in = new BufferedReader(new InputStreamReader(httpURLConnection.getInputStream(), "UTF-8"));
String line;
while ((line = in.readLine()) != null) {
result += line;
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (out != null) {
out.close();
}
if (in != null) {
in.close();
}
} catch (IOException ex) {
ex.printStackTrace();
}
}
return result;
}
public static void main(String[] args) throws DocumentException, UnsupportedEncodingException {
SAXReader reader = new SAXReader();
Document document = reader.read(new File("src/postdata.xml"));
String messageText = document.asXML();
messageText = URLEncoder.encode(messageText, "UTF-8");
// 发送 POST 请求
String sr = HttpRequest.sendPost2("https://localhost:8443/cbtserver/declaretest",
"messageType=123456789&messageText=" + messageText);
System.out.println(sr);
}
postdata.xml即为二中的数据,经编码后为:
%3C%3Fxml+version%3D%221.0%22+encoding%3D%22UTF-8%22%3F%3E%0D%0A%3C!DOCTYPE+html+PUBLIC+%22-%2F%2FW3C%2F%2FDTD+XHTML+1.0+Strict%2F%2FEN%22+%22http%...
后端接收数据方法为:
@RequestMapping(value = "/declaretest", method = RequestMethod.POST)
@ResponseBody
public JSONObject declaretest(String messageType, String messageText,
HttpServletRequest request) {
return internationalTradeService.declaretest(messageType, messageText);
}
加断点调试,发现messageText接收内容如下(内容过长,所以显示不全,除中文显示不对外,其他都对):KJ881110_CORP_2012121212121200001 KJ881110 TEST00001 KJGGPT KJGGPT 20121212121212 1.0 待定
PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPEludGVybmF0aW9uYWxUcmFkZT4KCTxIZWFkPgoJCTxNZXNzYWdlSUQ+S0o4ODExMTBfQ09...
这里就有个问题:数据传输前是通过encode的,但是后端方法接收前并没有明确的解码处理,但是数据确实是被解码了,这是如何发生的和在什么阶段发生的呢?
思路过程:
springMVC是交由DispatcherServlet处理各种请求的,所以第一时间考虑是不是DispatcherServlet中是不是对参数做了处理了。断点调试DispatcherServlet中的doService、doDispatch方法,发现request携带的参数数据早已被解码了。DispatcherServlet继承FrameworkServlet,往上溯源,调试FrameworkServlet的doPost方法,继而HttpServletBean、HttpServlet,但是结果都是参数数据已被解码了。
阅读这篇文章有利于更好的理解以下的内容:http://blog.csdn.net/cutesource/article/details/5091732
重点关注Request类,发现里面有两个类 org.apache.tomcat.util.http.Parameters和org.apache.tomcat.util.buf.UDecoder,Parameters应该是参数类,UDecoder是解码类了。请看Parameters类里重点代码:
private void processParameters(byte bytes[], int start, int len,
Charset charset) {
...
int pos = start;
int end = start + len;
while(pos < end) {
int nameStart = pos;
int nameEnd = -1;
int valueStart = -1;
int valueEnd = -1;
boolean parsingName = true;
boolean decodeName = false;
boolean decodeValue = false;
boolean parameterComplete = false;
do {
//检查是否含有特殊字符
switch(bytes[pos]) {
case '=':
if (parsingName) {
// Name finished. Value starts from next character
nameEnd = pos;
parsingName = false;
valueStart = ++pos;
} else {
// Equals character in value
pos++;
}
break;
case '&':
if (parsingName) {
// Name finished. No value.
nameEnd = pos;
} else {
// Value finished
valueEnd = pos;
}
parameterComplete = true;
pos++;
break;
case '%':
case '+':
// Decoding required
if (parsingName) {
decodeName = true;
} else {
decodeValue = true;
}
pos ++;
break;
default:
pos ++;
break;
}
} while (!parameterComplete && pos < end);
...
try {
String name;
String value;
if (decodeName) {
//解码参数名称
urlDecode(tmpName);
}
//默认是ISO-8859-1
tmpName.setCharset(charset);
name = tmpName.toString();
if (valueStart >= 0) {
if (decodeValue)
//解码参数值
urlDecode(tmpValue);
}
tmpValue.setCharset(charset);
value = tmpValue.toString();
} else {
value = "";
}
try {
addParameter(name, value);
} catch (IllegalStateException ise) {
...
}
} catch (IOException e) {
...
}
...
}
}
若检查到参数名和参数值含有特殊字符=、&、%、+,则会对其进行urlDecode解码操作,默认解码格式public static final String DEFAULT_ENCODING = "ISO-8859-1";
但是ISO-8859-1对中文产生的乱码问题,解决方法可以是在项目web.xml中配置CharacterEncodingFilter:
characterEncodingFilter
org.springframework.web.filter.CharacterEncodingFilter
encoding
UTF-8
forceEncoding
true
characterEncodingFilter
/*
(2)jsp页面的<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>或已经相当于java.net.URLEncoder.encode(content,"utf-8"),传输数据到了后端,tomcat自然自动解码了。
五、总结
对于传输数据中有特殊字符的,web服务器会预先decode一次。