JAVA服务端的解码
JAVA服务端的解码
protected void convertURI(MessageBytes uri, org.apache.catalina.connector.Request request) throws Exception {ByteChunk bc = uri.getByteChunk();int length = bc.getLength();CharChunk cc = uri.getCharChunk();cc.allocate(length, -1);String enc = this.connector.getURIEncoding();if(enc != null) {B2CConverter bbuf = request.getURIConverter();try {if(bbuf == null) {bbuf = new B2CConverter(enc);request.setURIConverter(bbuf);}} catch (IOException var11) {log.error("Invalid URI encoding; using HTTP default");this.connector.setURIEncoding((String)null);}if(bbuf != null) {try {bbuf.convert(bc, cc);uri.setChars(cc.getBuffer(), cc.getStart(), cc.getLength());return;} catch (IOException var12) {log.error("Invalid URI character encoding; trying ascii");cc.recycle();}}}byte[] var13 = bc.getBuffer();char[] cbuf = cc.getBuffer();int start = bc.getStart();for(int i = 0; i < length; ++i) {cbuf[i] = (char)(var13[i + start] & 255);}uri.setChars(cbuf, 0, length);}
这句代码 String enc = this.connector.getURIEncoding()获取到了connector所设置的编码,如果取到了设置的编码则用B2CConverter的convert,将byte数组转换成char数组。若没有设置编码则用默认的解码方案,即认为它是 ISO-8859-1 编码的。设置URI的编码是在servlet.xml配置文件的Connector中:
public String getParameter(String name) {if(!this.parametersParsed) {this.parseParameters();}return this.coyoteRequest.getParameters().getParameter(name);}
若目前还没对参数进行解析,则会通过parseParameters方法,解析出所有的参数值。见如下代码:
protected void parseParameters() {this.parametersParsed = true;Parameters parameters = this.coyoteRequest.getParameters();String enc = this.getCharacterEncoding();boolean useBodyEncodingForURI = this.connector.getUseBodyEncodingForURI();if(enc != null) {parameters.setEncoding(enc);if(useBodyEncodingForURI) {parameters.setQueryStringEncoding(enc);}} else {parameters.setEncoding("ISO-8859-1");if(useBodyEncodingForURI) {parameters.setQueryStringEncoding("ISO-8859-1");}}parameters.handleQueryParameters();if(!this.usingInputStream && !this.usingReader) {if(this.getMethod().equalsIgnoreCase("POST")) {String contentType = this.getContentType();if(contentType == null) {contentType = "";}int semicolon = contentType.indexOf(59);if(semicolon >= 0) {contentType = contentType.substring(0, semicolon).trim();} else {contentType = contentType.trim();}if("application/x-www-form-urlencoded".equals(contentType)) {int len = this.getContentLength();if(len > 0) {int formData = this.connector.getMaxPostSize();if(formData > 0 && len > formData) {if(this.context.getLogger().isDebugEnabled()) {this.context.getLogger().debug(sm.getString("coyoteRequest.postTooLarge"));}return;}Object e = null;byte[] e1;if(len < CACHED_POST_LEN) {if(this.postData == null) {this.postData = new byte[CACHED_POST_LEN];}e1 = this.postData;} else {e1 = new byte[len];}try {if(this.readPostBody(e1, len) != len) {return;}} catch (IOException var11) {if(this.context.getLogger().isDebugEnabled()) {this.context.getLogger().debug(sm.getString("coyoteRequest.parseParameters"), var11);}return;}parameters.processParameters(e1, 0, len);} else if("chunked".equalsIgnoreCase(this.coyoteRequest.getHeader("transfer-encoding"))) {Object formData1 = null;byte[] formData2;try {formData2 = this.readChunkedPostBody();} catch (IOException var10) {if(this.context.getLogger().isDebugEnabled()) {this.context.getLogger().debug(sm.getString("coyoteRequest.parseParameters"), var10);}return;}if(formData2 != null) {parameters.processParameters(formData2, 0, formData2.length);}}}}}}
下面具体分析下参数的解析的过程。首先,获得charEncoding, 得到 charEncoding的规则就是:若已经显示指定(通过Request的api)则使用指定的值,若未指定则尝试获取ContentType头部中指定的字符集,都获取不到返回null。见Coyote中Request的getCharacterEncoding方法
public String getCharacterEncoding() {if(this.charEncoding != null) {return this.charEncoding;} else {this.charEncoding = ContentType.getCharsetFromContentType(this.getContentType());return this.charEncoding;}}
然后, 若 charEncoding不为null则将charEncoding设置到Parameters的encoding中。否则将Parameters的encoding设置为默认编码ISO-8859-1。若声明了useBodyEncodingForURI为true,则将Parameters的queryStringEncoding也设置为与encoding相同的编码。useBodyEncodingForURI的使用同样是在Connector配置中:
接下来就是利用Parameters处理请求行中的查询字符串
public void handleQueryParameters() {if(!this.didQueryParameters) {this.didQueryParameters = true;if(this.queryMB != null && !this.queryMB.isNull()) {if(debug > 0) {this.log("Decoding query " + this.decodedQuery + " " + this.queryStringEncoding);}try {this.decodedQuery.duplicate(this.queryMB);} catch (IOException var2) {var2.printStackTrace();}this.processParameters(this.decodedQuery, this.queryStringEncoding);}}}
有上面的方法可以看出, 在解码请求行的查询字符串时使用的编码是queryStringEncoding。若没有设置queryStringEncoding的值,则使用ISO-8859-1, 具体的解码过程在Parameters的urlDecode方法中。
private String urlDecode(ByteChunk bc, String enc) throws IOException {if(this.urlDec == null) {this.urlDec = new UDecoder();}this.urlDec.convert(bc);String result = null;if(enc != null) {bc.setEncoding(enc);result = bc.toString();} else {CharChunk cc = this.tmpNameC;int length = bc.getLength();cc.allocate(length, -1);byte[] bbuf = bc.getBuffer();char[] cbuf = cc.getBuffer();int start = bc.getStart();for(int i = 0; i < length; ++i) {cbuf[i] = (char)(bbuf[i + start] & 255);}cc.setChars(cbuf, 0, length);result = cc.toString();cc.recycle();}return result;}
解析出来的参数就放在Parameters中,此时只处理完了请求行中的查询参数。对于GET方法只处理请求行就已经足够了,然而POST方法的查询参数还可能出现在请求体中,所以还要进一步的解析。所以接下来检查Content-Type是否为 "application/x-www-form-urlencoded", 只有 "application/x-www-form-urlencoded"类型的POST才会到请求体中解析查询参数。从请求体中读取到相关后使用Parameters的下面方法解析参数
public void processParameters(byte[] bytes, int start, int len) {this.processParameters(bytes, start, len, this.encoding);}
可以看到, 从请求体中解析查询参数使用的是encoding的编码,同样如果没有设置的话还是采用 ISO-8859-1。经过parseParameters处理过之后,所有的请求参数都已经包含在Parameters中, 查询参数来自于请求行和请求体,同名的参数值会作为数组保存起来。然后可以利用参数的名字通过 Parameters的getParameter方法获取参数的值:
public String getParameter(String name) {String[] values = this.getParameterValues(name);return values != null?(values.length == 0?"":values[0]):null;}
这个方法虽然只有两行,但是揭示了一个我们用Request的getParameter获取参数的一条重要规则。通常我们在传递参数的时候都是一个key对应一个value,当需要传递数组的时候会使用名字相同的key传递两次。如查询字符串为a=1&a=2,我们在服务端就可以通过getParameterValues方法获取到一个数组,数组的第一个和第二个元素分别为1和2,另外也可以通过getParameter方法获取第一个参数。
<filter><filter-name>characterEncodingFilter</filter-name><filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class><init-param><param-name>encoding</param-name><param-value>UTF-8</param-value></init-param><init-param><param-name>forceEncoding</param-name><param-value>true</param-value></init-param></filter>
若再与URIEncoding、useBodyEncodingForURI配置参数结合,则可以完全控制服务端的URI与参数解码。
private String urlDecode(ByteChunk bc, String enc) throws IOException {if(this.urlDec == null) {this.urlDec = new UDecoder();}this.urlDec.convert(bc);String result = null;if(enc != null) {bc.setEncoding(enc);result = bc.toString();} else {CharChunk cc = this.tmpNameC;int length = bc.getLength();cc.allocate(length, -1);byte[] bbuf = bc.getBuffer();char[] cbuf = cc.getBuffer();int start = bc.getStart();for(int i = 0; i < length; ++i) {cbuf[i] = (char)(bbuf[i + start] & 255);}cc.setChars(cbuf, 0, length);result = cc.toString();cc.recycle();}return result;}
JAVA服务端的解码相关推荐
- android+okhttp+java服务端(tomcat)+mysql实现登录注册
先上代码(文章结尾有遇到的问题和对应解决方案) android客户端 >android:app-build.gradle 1.添加okhttp依赖,具体根据自己的okhttp包来决定 depen ...
- Flex通信-Java服务端通信实例
Flex与Java通信的方式有很多种,比较常用的有以下方式: WebService:一种跨语言的在线服务,只要用特定语言写好并部署到服务器,其它语言就可以调用 HttpService:通过http请求 ...
- 聊一聊 Java 服务端中的乱象
点击上方"方志朋",选择"设为星标" 回复"666"获取新整理的面试文章 来源:阿里巴巴中间件 查尔斯·狄更斯在<双城记>中写道 ...
- 那些年,我们见过的 Java 服务端乱象
点击上方"方志朋",选择"设为星标" 做积极的人,而不是积极废人 Photo by The Book Tutor @Youtube 文 | 陈昌毅 导读 查尔斯 ...
- MobileIMSDK怎样修改服务端核心jar包的源码并替换掉Java服务端的jar包
场景 MobileIMSDK怎样将Java服务端运行起来以及打成jar包运行: https://blog.csdn.net/BADAO_LIUMANG_QIZHI/article/details/11 ...
- 人人都能掌握的Java服务端性能优化方案
转载自 人人都能掌握的Java服务端性能优化方案 作为一个Java后端开发,我们写出的大部分代码都决定着用户的使用体验.如果我们的代码性能不好,那么用户在访问我们的网站时就要浪费一些时间等待服务器的响 ...
- 那些年,我们见过的 Java 服务端“问题”
导读 明代著名的心学集大成者王阳明先生在<传习录>中有云: 道无精粗,人之所见有精粗.如这一间房,人初进来,只见一个大规模如此.处久,便柱壁之类,一一看得明白.再久,如柱上有些文藻,细细都 ...
- 那些年,我们见过的Java服务端乱象
导读 查尔斯·狄更斯在<双城记>中写道:"这是一个最好的时代,也是一个最坏的时代."移动互联网的快速发展,出现了许多新机遇,很多创业者伺机而动:随着行业竞争加剧,互联网 ...
- QtJava笔记-Qt与Java进行SSL双向认证(Qt客户端,Java服务端)
这里使用Java作为服务端,使用Qt作为客户端. 程序运行截图如下: 这里的证书Qt使用的p12,Java使用的jks,看以前的博文生成. 源码打包下载地址: https://github.com/f ...
最新文章
- 2014百度面试题目---“求比指定整数大且最小的不重复数”解答
- 20175330 数据结构-单链表(选做)
- java cellformat_java使用jxl对excel表导出进行样式的设置
- 房东收电费1.5犯法吗_深圳房东租客少了,感到很困难压力很大,几栋楼房间空着!...
- oracle+去括号,关于001 TK的几个问题,请大家一起讨论一下
- 常用的adb命令收集
- RUNOOB python练习题 39 数组排序
- How to test software requirements specification (SRS)?
- RHEL 4 安装单实例oracle 10g
- 40线性映射07——线性变换的矩阵表示、线性变换与基的关系、线性变换坐标间的关系、线性变换在不同基下矩阵之间的关系、相似矩阵
- Unity3D游戏资源的提取
- Windchill 零件操作通用类
- HCIA 学习笔记 准备考试
- 通信(一) 串口通信
- 解决电脑右键菜单反应慢问题
- linux执行可执行命令程序ls,linux运行可执行程序命令
- 真·电子二胡 (ESP32配合库乐队APP实现的电子制作)
- Word无法插入页码怎么办?Word页码选项变灰,无法使用解决方法
- 开启VPN和NAT服务
- Fiddler抓手机app的包