`
coconut_zhang
  • 浏览: 540764 次
  • 性别: Icon_minigender_1
  • 来自: 天津
社区版块
存档分类
最新评论
阅读更多

1.urlencode和decode

字符的编码和解码在有中文和特殊符号的情况下,常常是一个头疼的问题。url的encode和decode是解决这个问题的一个分支,通过简单的算法将特殊字符编码,其大致算法如下:

  • The alphanumeric characters “a” through “z”, “A” through “Z” and “0″ through “9″ remain the same.
  • The special characters “.”, “-”, “*”, and “_” remain the same.
  • The space character ” ” is converted into a plus sign “+”.
  • All other characters are unsafe and are first converted into one or more bytes using some encoding scheme. Then each byte is represented by the 3-character string “%xy”, where xy is the two-digit hexadecimal representation of the byte. The recommended encoding scheme to use is UTF-8. However, for compatibility reasons, if an encoding is not specified, then the default encoding of the platform is used。
简单来讲,就是将一个非英文的字符先用一定的编码方式(比如UTF-8)编码得到3个字节,然后每个字节的8位用两个16进制的字符来表示,前面再加上%。java处理伪代码描述如下:

 

 

Java代码 复制代码 收藏代码
  1. StringBuilder sb = new StringBuilder();   
  2.       for (int i = 0; i < s.length();) {   
  3.           int c = (int) s.charAt(i);   
  4.           if (c == ' ') {   
  5.               sb.append('+');   
  6.           }else if( c == '%') {   
  7.               sb.append("%25");   
  8.           }else if(c 符合前面第4条的描述,是特殊字符){   
  9.               byte[] ba = str.getBytes(charset);   
  10.               for (int j = 0; j < ba.length; j++) {   
  11.                   String ts = Integer.toHexString(b)取后两位;   
  12.                   sb.append("%").append(ts);   
  13.               }   
  14.           }else {   
  15.               sb.append(c);   
  16.           }   
  17.       }   
  18.       String result = sb.toString();   
  StringBuilder sb = new StringBuilder();
        for (int i = 0; i < s.length();) {
            int c = (int) s.charAt(i);
            if (c == ' ') {
                sb.append('+');
            }else if( c == '%') {
                sb.append("%25");
            }else if(c 符合前面第4条的描述,是特殊字符){
                byte[] ba = str.getBytes(charset);
                for (int j = 0; j < ba.length; j++) {
                    String ts = Integer.toHexString(b)取后两位;
                    sb.append("%").append(ts);
                }
            }else {
                sb.append(c);
            }
        }
        String result = sb.toString(); 

 

通过这样的方式,比如 “a中国” 就会变成”a%E4%B8%AD%E5%9B%BD”,在发送端编码,在接受方使用相反的算法解码即可。但是这里面的几个特殊字符,比如%,常常会带来一些隐晦的问题。

2. 问题一:apache的rewrite

在做统一域名迁移的时候,遇到了一例这样的问题,现象是以前传递过来的一个正确参数现在超长了,排查后发现,由于为了兼容两个域名,我们对于某些url做了一个rewrite,而apache的rewrite模块默认会对%这样的字符转换为%25,再发送rewrite的响应到浏览器,因此,参数就由%252BeNh变成了%25252BeNh,导致超长了。

解决办法,修改apache的rewrite参数,添加一个NE, 如下:

Ruby代码 复制代码 收藏代码
  1. RewriteRule ^/martini/(.*)$    /eve/$1 [L,R,NE]   
RewriteRule ^/martini/(.*)$    /eve/$1 [L,R,NE] 

 

 

1
RewriteRule ^/martini/(.*)$    /eve/$1 [L,R,NE]

问题得到解决,更多apache的rewrite配置可以参考:

http://httpd.apache.org/docs/2.2/mod/mod_rewrite.html

 

. 问题二:容器的自动decode

在eve的tracelog模块中,会将目标url作为一个参数,经过UrlEncoder.encode后,包装到eve的url中,然后发送邮件给客户,这样客户点击时,就可以从eve进行跳转,从而记录下相关访问数据。如目标url为http://www.taobao.com,包装后变成http://eve.alibaba.com/dispatch?targetUrl=http%3A%2F%2Fwww.taobao.com&logid=12345.

但是在遇到了一次问题,当目标url本身也有参数,而且是经过编码的中文参数的时候,就出现了问题。eve的使用方先用gbk字符将目标url编码成了如下样式:

http://www.taobao.com?user=%EE%E2

然后eve像平常一样,将这个url用utf-8再次encode,拼装得到url如下:

http://eve.alibaba.com/dispatch?targetUrl=http%3A%2F%2Fwww.taobao.com%3Fuser%3D%25EE%25E2&logid=12345

结果,客户点击跳转链接,出错了。

在eve的跳转处理servlet中,大概处理逻辑是如下:

Java代码 复制代码 收藏代码
  1. String url = req.getParameter(TracelogDto.TARGET_URL);   
  2. url = URLDecoder.decode(url, "utf-8");   
String url = req.getParameter(TracelogDto.TARGET_URL);
url = URLDecoder.decode(url, "utf-8"); 

 

理想情况下处理出来的url是http://www.taobao.com?user=%EE%E2
实际却是:http://www.taobao.com?user=?

开始怀疑是apache转发的问题,后来排查发现是jboss的req.getParameter()时,对该参数已经进行了一次decode了,所以照成这个问题。之后查了一下代码,发现tomcat和jetty都在getParameter时都做了这个事情,找了下servlet规范和相关java的接口,没有明确规定在.getParameter时需要先decode,网上倒是有文章说,但事实上,主流容器都做了这个事情。

  • 目前request经过解析了的方法有:parsePostData(int, ServletInputStream),parseQueryString(String),getContextPath(),
  • getPathInfo()没有解析的方法有:getRequestURI()

 

因此,最终的解决方案是去掉跳转代码中的decode就可以了,这本来是一个很简单的问题,但因为不知道容器做了一次decode,而带来了一些困扰。

附:

jetty的getParameter decode的调用流程:

1
2
3
4
--UrlEncoded.decodeUtf8To(..);
--org.eclipse.jetty.http.HttpURI.decodeQueryTo(MultiMap parameters)
--org.eclipse.jetty.server.Request. extractParameters()
--org.eclipse.jetty.server.Request.getParameter(String name)

tomcat的getParameter decode的调用流程:

1
2
3
4
5
6
7
--org.apache.tomcat.util.http.Parameters.urlDecode
--org.apache.tomcat.util.http.Parameters.processParameters
--org.apache.tomcat.util.http.Parameters.handleQueryParameters
--org.apache.catalina.connector.Request.handleQueryParameters
--org.apache.catalina.connector.Request.parseParameters
--org.apache.catalina.connector.Request.getParameter(String name)
--org.apache.catalina.connector.RequestFacade.getParameter(String name)

另外,encode和decode都需要指定一个字符集,如果UTF-8,GBK,或者ISO-8859-1,tomcat在不指定的情况下,queryString和body都是用ISO-8859-1来做decode的。

 

2.   容器的编码问题

对于tomcat在getParameter时的字符处理,代码如下:

Java代码 复制代码 收藏代码
  1. String enc = getCharacterEncoding();   
  2.        boolean useBodyEncodingForURI = connector.getUseBodyEncodingForURI();   
  3.        if (enc != null) {   
  4.            parameters.setEncoding(enc);   
  5.            if (useBodyEncodingForURI) {   
  6.                parameters.setQueryStringEncoding(enc);   
  7.            }   
  8.        } else {   
  9.            parameters.setEncoding                (org.apache.coyote.Constants.DEFAULT_CHARACTER_ENCODING);   
  10.            if (useBodyEncodingForURI) {   
  11.                parameters.setQueryStringEncoding   
  12.                    (org.apache.coyote.Constants.DEFAULT_CHARACTER_ENCODING);   
  13.            }   
  14.        }   
 String enc = getCharacterEncoding();
        boolean useBodyEncodingForURI = connector.getUseBodyEncodingForURI();
        if (enc != null) {
            parameters.setEncoding(enc);
            if (useBodyEncodingForURI) {
                parameters.setQueryStringEncoding(enc);
            }
        } else {
            parameters.setEncoding                (org.apache.coyote.Constants.DEFAULT_CHARACTER_ENCODING);
            if (useBodyEncodingForURI) {
                parameters.setQueryStringEncoding
                    (org.apache.coyote.Constants.DEFAULT_CHARACTER_ENCODING);
            }
        } 

 

该段代码表明,如果设置了request的(可以通过在filter中设置request.setCharacterEncoding(charset),或者在http头中设置Content-type: application/x-www-form-urlencoded; charset=UTF-8;那么就用指定的编码解析请求body中的paramter。

如果之前响应到浏览器的HTML代码里有类似<meta http-equiv=”Content-Type” content=”text/html; charset=GBK” />,那么此HTML的form表单将以html指定的的编码方式提交数据(但注意,ie虽然以这个编码,但是并不将Content-type: application/x-www-form-urlencoded; charset=UTF-8 这个charset设置上去,所以在容器中request.getCharacterEncoding()是null的。事实上,我们的LocaleFilter就是解决这个问题。)则body的解码使用对应的charset。

对于QueryString是否使用,还要看看useBodyEncodingForURI的设置.这个设置是由connector来设置的,也就是容器的配置,不是每次请求可以变的,如果没有设置,queryString就会以ISO-8859-1的方式来解码(包括urldecode)。

对于浏览器来说,queryStirng如果是在window下,ie就以GBK编码传输,firefox以GBK编码做urlEncode后传输。

然后对于容器来说,一般还有一个URIencoding可以设置,是控制整个uri的编码方式的

3. jetty的处理

1.url的处理

jetty对于url和querystr,在每个HttpConnection初始化的时候,有如下代码

Java代码 复制代码 收藏代码
  1. _uri = StringUtil.__UTF8.equals(URIUtil.__CHARSET)?new HttpURI():new EncodedHttpURI(URIUtil.__CHARSET);   
 _uri = StringUtil.__UTF8.equals(URIUtil.__CHARSET)?new HttpURI():new EncodedHttpURI(URIUtil.__CHARSET); 

 

在URIUtil中:

Java代码 复制代码 收藏代码
  1. final String __CHARSET=System.getProperty("org.eclipse.jetty.util.URI.charset",StringUtil.__UTF8);   
final String __CHARSET=System.getProperty("org.eclipse.jetty.util.URI.charset",StringUtil.__UTF8); 

 

因此我们可以看到,对于url,默认使用UTF-8处理,如果设置了org.eclipse.jetty.util.URI.charset,就用设置的字符编码处理。

2.querystr的处理

先看代码

Java代码 复制代码 收藏代码
  1.   if (_uri!=null && _uri.hasQuery())   
  2. {   
  3.             if (_queryEncoding==null)   
  4.                 _uri.decodeQueryTo(_baseParameters);   
  5.             else  
  6.             {   
  7.                 _uri.decodeQueryTo(_baseParameters,_queryEncoding);   
  8.             }   
  9. }   
  if (_uri!=null && _uri.hasQuery())
{
            if (_queryEncoding==null)
                _uri.decodeQueryTo(_baseParameters);
            else
            {
                _uri.decodeQueryTo(_baseParameters,_queryEncoding);
            }
} 

 

可以看到,如果设置了queryEncoding,就会按照设置的编码来解析,在Request中,有方法
publicvoid setQueryEncoding(String queryEncoding);
也可以通过request.setAttribute来设置

Java代码 复制代码 收藏代码
  1. publicvoid setAttribute(String name, Object value) {   
  2.         if ("org.eclipse.jetty.server.Request.queryEncoding".equals(name))   
  3.             setQueryEncoding(value==null?null:value.toString());   
  4. }   
publicvoid setAttribute(String name, Object value) {
        if ("org.eclipse.jetty.server.Request.queryEncoding".equals(name))
            setQueryEncoding(value==null?null:value.toString());
} 

 

如果没有设置queryEncoding,会是什么情况呢?
在EncodedHttpUri中,有如下代码

Java代码 复制代码 收藏代码
  1. public void decodeQueryTo(MultiMap parameters)   
  2. {   
  3. if (_query==_fragment)   
  4. return;   
  5. UrlEncoded.decodeTo(StringUtil.toString(_raw,_query+1,_fragment-_query-1,_encoding),parameters,_encoding);   
  6. }  
public void decodeQueryTo(MultiMap parameters)
{
if (_query==_fragment)
return;
UrlEncoded.decodeTo(StringUtil.toString(_raw,_query+1,_fragment-_query-1,_encoding),parameters,_encoding);
}

 

可以看到,会使用_encoding参数,这个就是前面new出EncdodeHttpUri的传入参数,即org.eclipse.jetty.util.URI.charset设置的参数。

因此,对于queryStr,如果请求中设置了_queryEncoding,就用他的编码,否则用系统参数org.eclipse.jetty.util.URI.charset设置的编码,否则用默认编码UTF-8

 

 

3.body部分

默认使用 UTF-8 编码,当然可以在使用之前使用 request.set CharacterEncoding 设定编码.

注:网上有资料说POST 参数默认使用 Content-type 中的 Charset 编码,但看了下源码,tomcat是有这个功能的,在getCharacterEncoding的时候,有一个如果为null则去ContentType中取的动作,但jetty好像没有)

分享到:
评论

相关推荐

    网址编码解码,jQuery版Url.encode,Url.decode,Url.parse.rar

    网址编码解码,jQuery版Url.encode,Url.decode,Url.parse,一个来自jQuery的URL Decoder解码、编码插件,为你的URL安全定义一道安全防线,示例文件本身就是一个值得学习的实例,自带的示例将告诉你如何使用这款URL...

    URL encode 与 URL decode 的C语言实现.zip

    为了解决这个问题,URL编码(URL encode)和解码(URL decode)机制应运而生。这个压缩包文件"URL encode 与 URL decode 的C语言实现.zip"显然提供了用C语言编写的函数,用于对URL进行编码和解码操作。 URL编码是一...

    Encode_ Decode Tools URL编码解码工具

    `Encode_ Decode Tools`是一款用于URL编码和解码的工具,它可以帮助开发者和用户方便地处理这些编码问题。这款工具可能提供了简单的用户界面,允许用户输入URL或字符串,然后点击按钮进行编码或解码操作。它支持对...

    java net unicode / native2ascii / url decode / url encode / UTF8 / js url code

    标题中的“java net unicode / native2ascii / url decode / url encode / UTF8 / js url code”涉及了Java网络编程中的几个关键概念,这些概念在处理字符编码时非常重要。以下是对这些概念的详细解释: 1. **...

    HTML码URL-encode码转换器

    自己动手写了一个HTML码URL-encode码转换器,给大家分享一下。

    [工具查询]渝海URL编码转换工具 v1.1_yh_url_encode.zip

    【渝海URL编码转换工具 v1.1_yh_url_encode】是一个专用于处理URL编码问题的实用工具。在互联网通信中,URL(统一资源定位符)是用于标识资源的字符串,它包含网络协议、主机名、路径、查询参数等部分。然而,URL中...

    HtmlEncode编码与解码用法定义

    除了HtmlEncode与HtmlDecode外,文中还提到了`UrlEncode`和`UrlDecode`的概念。这两个操作主要用于处理URL中的特殊字符,确保URL能够被正确解析并传输。 **UrlEncode**的目的是将URL中的特殊字符转换为对应的百分比...

    PB 进制转换 url编码 urlencode urldecode 数组排序

    hexencode 将字符串指定字符集进行编码成16进制字符串 hexstring 将字符串指定字符集进行编码成16进制字符串 power 求数字的幂函数 replace 将指定字符串中的源字符替换为目标字符 todec 将指定进制的数字转换成10...

    JavaScript的URL encode decode源代码,ip验证,TRim

    在标题提到的“JavaScript的URL encode decode源代码,ip验证,TRim”中,我们将探讨这三个关键概念。 1. URL编码与解码: 在JavaScript中,我们经常需要对URL中的特殊字符进行编码,以确保它们在HTTP请求中被正确...

    encode加密解密.rar

    2. URL编码:URL编码(又称百分号编码)是为了解决URL中不能包含某些特殊字符的问题,它将非字母数字字符转换为“%”加两位十六进制数的形式。 3. Hex编码:Hex编码是将每个字节转换为两个16进制数字的表示,便于...

    Url Encode Decode

    C++的UrlEncode UrlDecode代码。

    url传递的参数值中包含&时,url自动截断问题的解决方法

    在做一个公告浏览功能时,只要通过url传递的某参数值中包含 & 或 ,就会出现问题–该变量的值无法显示。 问题定位结果: 遇到&时,该参数的值会自动截断,导致参数值传递有误。 二、问题的解决 java代码中做如下...

    Arduino URLCode 编码解码解析 URL字符串

    使用这个库可以很方便的编码解码URLCode。里面带了ESP8266看门狗开关,可以在调用这个库之前宏定义ESP8266就可以开启库的看门狗设置。避免触发看门狗。建议配合我主页的教程一起食用。哪怕是小白也可以轻松使用。

    EnCode/DeCode转换工具

    EnCode/DeCode转换工具是一款专门针对这一需求设计的小巧实用软件,它能够处理ANSI和UTF-8这两种常见的字符编码格式,帮助开发者解决字符转换的问题。 首先,我们要理解编码与解码的基本概念。编码是将可读的文本...

    linux c++ url和base64编解码

    linux系统c++实现的url和base64编解码 在webserver中会使用 自己实现的一个webserver,详见https://download.csdn.net/download/k117470154/10234299

    字符串转EnCode_c#

    字符串转EnCode_c# 字符串转EnCode_c# 字符串转EnCode_c# 字符串转EnCode_c# 字符串转EnCode_c# 字符串转EnCode_c# 字符串转EnCode_c# 字符串转EnCode_c#

    java中URLEncoder.encode与URLDecoder.decode处理url特殊参数的方法

    `URLEncoder.encode()` 和 `URLDecoder.decode()` 是Java标准库中的两个关键方法,分别用于对URL参数进行编码和解码,以确保数据能够正确无误地在网络间传输。 **`URLEncoder.encode()`** 方法接受两个参数:需要...

    Java实现url加密处理的方法示例

    在Java中,可以使用`java.net.URLEncoder.encode()`方法对加密字符串进行编码,解码则使用`java.net.URLDecoder.decode()`。 7. **异常处理**:在实际应用中,应更全面地处理可能抛出的异常,包括捕获并记录异常...

    字符串的encode/escape

    在这个例子中,`URLEncoder.encode()`用于URL编码,`StringEscapeUtils.escapeHtml4()`用于HTML转义,然后分别有对应的解码和unescape方法。运行这段代码,你会看到原始字符串如何经过编码和转义,以及如何恢复到...

    JavaScript给url网址进行encode编码的方法

    这是因为URL中包含的某些字符可能会导致解析问题或错误。在本文中,我们将深入探讨JavaScript中用于URL编码的主要方法——`encodeURIComponent()`函数,并通过实例来理解其用法。 `encodeURIComponent()`函数是...

Global site tag (gtag.js) - Google Analytics