encodeURI之URL中文参数问题

 

********************************************************************
*                                                 版权声明
*
* 本文以Creative Commons的知识共享署名-非商业性使用-相同方式共享发布,请严格遵循该授权协议。
* 本文首发于博客园, 此声明为本文章中不可或缺的一部分。
* 作者网名:    浪子
* 作者EMAILdayichen (at)163.com
* 作者BLOG:  Http://Www.Cnblogs.Com/Walkingboy
*
********************************************************************

encodeURI之URL中文参数问题

-Written by 浪子@cnblogs.com  (07-01-26)

摘要:

我们在页面和页面间传递参数,最常用的一种方式就是通过url传递,如果涉及到中文或者其他特殊字符的时候,一般会使用encodeURI在Client端进行编码,Server端直接使用QueryString[]获取值(asp.net会自动帮我们进行解码),却无法料到,这样子也会出现错误,发现form的action地址竟然和url地址不符?asp.net在为form设置action的时候似乎进行了一定的编码(我还未知)......

 

一、Client端的QueryString:

自从使用了CallbackPlus之后,我们的编程模式越来越多的转向了Client端编码,而参数又经常通过url的形势传递,所以就写了个客户端的QueryString集中分析参数值

 

function QueryString(paraName){
    var url = window.location.search;
    var para = url.substr(url.indexOf(paraName));
    var andIndex = para.indexOf("&");
    var paras;
    if(andIndex>-1){
        paras = para.substr(0,para.indexOf("&"));
    }else{
        paras = para;
    }
    var paraValue = paras.split("=")[1];
    if(paraValue){
        return decodeURI(paraValue);
    }else{return "";};
}

 

原来一直都使用的好好,今天一个同事突然跟我说,QueryString的时候,出现“被解码的URI不是合法的编码”。问题很奇怪,因为第一遍进入的时候,已经在Client端正确识别到了该参数,但是触发某按钮执行PostBack之后,就会出现该问题。经过检查,发现第一次进入页面的时候,IE URL和form的action地址不一样,PostBack之后IE URL被修改为跟from的action一样(参考demo代码的encodeURI.aspx)。经过测试之后,发现这个问题只存在于中文或者特殊字符编码的URL上。在网上查找了很多资料都没有得到正确答案(比如设置web.config 的requestEncoding,responseEncodeing.),与阿不沟通后也没能得到有效的解决方法。后来想,既然正常的URL不会出问题,那就把中文编码为非特殊的字串就好了......

二、自定义的编码和解码方法: 

查找了很多编码和解码方法,一直都没有合适的,所以只能自己写一个这样的函数。因为这个编码和解码Client和Server都需要调用,所以也没有打算写得很复杂。

在查找资料的时候,从birdshome解决WebControl的doPostBack参数传递问题 那里得到了一个按长度分割编码后字串的思路,觉得相对比较简单和好写,所以就决定暂时采用这种思路进行设计。

修改函数如下:

Client :

function QueryString(paraName,isCusEncode){
    var url = window.location.search;
    
    var para = url.substr(url.indexOf(paraName));
    var andIndex = para.indexOf("&");
    var paras;
    if(andIndex>-1){
        paras = para.substr(0,para.indexOf("&"));
    }else{
        paras = para;
    }
    var paraValue = paras.split("=")[1];
    if(paraValue){
        return kdecode(paraValue,isCusEncode);
    }else{return "";};
}
function kencode(unzipStr,isCusEncode){
    if(isCusEncode){
        var zipArray = new Array();
        var zipstr = "";
        var lens = new Array();
        for(var i=0;i<unzipStr.length;i++){
         var ac = unzipStr.charCodeAt(i);
         zipstr += ac;
         lens = lens.concat(ac.toString().length);
        }
        zipArray = zipArray.concat(zipstr);
        zipArray = zipArray.concat(lens.join("O"));
        return zipArray.join("N");
    }else{
        return encodeURI(unzipStr);
    }
}
function kdecode(zipStr,isCusEncode){
        if(isCusEncode){
        var zipArray = zipStr.split("N");
        var zipSrcStr = zipArray[0];
        var zipLens = zipArray[1].split("O");
        var uzipStr = "";
        
        for(var j=0;j<zipLens.length;j++){
            var charLen = parseInt(zipLens[j]);
            uzipStr+= String.fromCharCode(zipSrcStr.substr(0,charLen));
            zipSrcStr = zipSrcStr.slice(charLen,zipSrcStr.length);
        }        
        return uzipStr;
    }else{
        return decodeURI(zipStr);
    }
}

        当然Server端也是需要对应的函数的:

 

public static string KEncode(string unzipStr)
        {
            return KEncode(unzipStr, false);
        }
        public static string KEncode(string unzipStr,bool isCusEncode)
        {
            if (isCusEncode)
            {
                string zipstr = string.Empty;
                StringCollection lens = new StringCollection();
                char[] chars = unzipStr.ToCharArray();
                for (int i = 0; i < chars.Length; i++)
                {
                    int ac = (int)chars[i];
                    zipstr += ac.ToString();
                    lens.Add(ac.ToString().Length.ToString());
                }
                string len = string.Empty;
                for (int i = 0; i < lens.Count; i++)
                {
                    if (i+1 == lens.Count)
                    {
                        len += lens[i];
                    }
                    else
                    {
                        len += lens[i]+"O";
                    }
                }
                return zipstr + "N" + len;
            }
            else
            {
                return HttpUtility.UrlEncode(unzipStr);
            }
            
        }
        public static string KDecode(string zipStr)
        {
            return KDecode(zipStr, false);
        }
        public static string KDecode(string zipStr, bool isCusEncode)
        {
            if (isCusEncode)
            {
                string[] zipArray = zipStr.Split(new string[1] { "N" }, StringSplitOptions.RemoveEmptyEntries);
                string zipSourceStr = zipArray[0];
                string[] zipLens = zipArray[1].Split(new string[1] { "O" }, StringSplitOptions.RemoveEmptyEntries);//no Zero,is "O"
                string uzipStr = "";
                for (int j = 0; j < zipLens.Length; j++)
                {
                    int charLen = int.Parse(zipLens[j]);
                    uzipStr += (char)(int.Parse((zipSourceStr.Substring(0, charLen))));
                    zipSourceStr = zipSourceStr.Remove(0, charLen);
                }
                return uzipStr;
            }
            else
            {
                return HttpUtility.UrlDecode(zipStr);
            }
        }

三、后话:

可能这是最差的解决方案,不知道园子里是否有其他人发现这个问题,并且有更好的解决方案?

问题描述和解决方法demo代码示例:Download
  
Updated :本来还担心QueryString的长度限制问题,后来阿不提供的相关的资料:IE地址栏的最大长度(-Maximum URL length is 2,083 characters in Internet Explorer.),也就放心了,应该不会超过这个限制的。

posted @ 2007-01-26 13:45 浪子 阅读(13472) 评论(22) 编辑 收藏

 回复 引用 查看   
#1楼 2007-01-26 13:57 Jeffrey Zhao      
一直用encodeURI(encodeURICompoent?)似乎没有什么问题。应该也涉及到编码。
 回复 引用 查看   
#2楼[楼主] 2007-01-26 14:00 浪子      
尝试用encodeURICompoent来做,但是还是会出现相同的情况
服务端倒是一直可以正常取得QueryString内的值,但是客户端经过PostBack之后就取不到了。

不知道Jeffrey有没有其他好的方法?:)

 回复 引用 查看   
#3楼 2007-01-26 14:18 Jeffrey Zhao      
使用JS修改action,呵呵。
页面里加这么一句话。
document.getElementsByTagName('form')[0].action = window.location;

 回复 引用   
#4楼 2007-01-26 14:21 唐利民[未注册用户]
qqq
 回复 引用 查看   
#5楼[楼主] 2007-01-26 14:33 浪子      
@Jeffrey Zhao
这样似乎不是很好的说?
会不会破坏第一次和第二次PostBack之间的连续性?

 回复 引用 查看   
#6楼 2007-01-26 14:38 Jeffrey Zhao      
@浪子
不会的,您在您的encodeURI里最后添加这么一句话试试看就知道了。:)

 回复 引用 查看   
#7楼[楼主] 2007-01-26 14:42 浪子      
@Jeffrey Zhao
这就惨了,把父页面的action给搞了,呵呵...

 回复 引用 查看   
#8楼 2007-01-26 14:44 Jeffrey Zhao      
@浪子
父页面的action?我尝试过了没有什么问题啊。

 回复 引用 查看   
#9楼 2007-01-26 14:45 Jeffrey Zhao      
@浪子
啊,我的意思是在encodeURI.aspx里添加这句话,因为这是出问题的页面,不是吗?

 回复 引用 查看   
#10楼 2007-01-26 14:49 阿不      
确实是有这个问题,postback后,原来已被编码过的中文字符,又会被重新编码,本来这是没什么问题的,服务器端照常可以取得,可是他的一个麻烦就在于postback之后还想用js来解码,问题就来了。
 回复 引用 查看   
#11楼[楼主] 2007-01-26 14:50 浪子      
@Jeffrey Zhao
看成这个了,这个的话就加在父页面里了,你指的是弹出的页面里面加入document.getElementsByTagName('form')[0].action = window.location。
我理解错了:),这种好像是种最简单的解决方式。

///-------------------------------------------
不会的,您在您的encodeURI里最后添加这么一句话试试看就知道了。:)

 回复 引用 查看   
#12楼 2007-01-27 00:06 Cat Chen      
把你测试失败时输入的字符串以及encodeURIComponent后转变为的QueryString贴出来看看吧,这样比较方便了解问题的本质。现在单看这篇文章,看到了解决放案,却连问题样本都没看到。

另外把值防到QueryString里面其实不是一个好的办法,首先根据语义QueryString就是拿来Query的,不是用来Query的值最好放在Form里面提交。其次,理论上URL长度限制为256,你能保证你的值不会导致URL超长吗?超长的话,本身就是一个非法的URL,一台配置严谨的HTTP代理或者服务器可能直接丢弃非法URL并返回错误。

 回复 引用 查看   
#13楼[楼主] 2007-01-27 08:46 浪子      
@Cat Chen
1、问题的脚本在Download里面,请看后面的地址,里面有我的问题demo和我的解决方法的demo。

2、因为需要弹出来页面所以放到QueryString里面了。我也一直担心超长的问题,因为这种编码方式会使原来的值长度变得更长。但是看了文后提供的http://support.microsoft.com/default.aspx?scid=kb;EN-US;q208427,似乎不需要很担心。如果不是这样只能在对QueryString作其他压缩处理了。



 回复 引用 查看   
#14楼 2007-01-27 13:51 Jeffrey Zhao      
@Cat Chen
不过在FireFox和IE里似乎都支持大约2000左右的url。

 回复 引用 查看   
#15楼 2007-01-29 08:58 Cat Chen      
@浪子
浏览器支持长URL也有风险的,如果中间要经过一台严格的HTTP代理,它就可能直接返回错误,而根本不帮你请求服务器。我遇到过因此访问不了服务的情况,所以现在处理URL都很小心。

 回复 引用 查看   
#16楼[楼主] 2007-01-29 12:44 浪子      
@Cat Chen
嗯,风险肯定会有的。
但是目前还没有办法找到好的解决方案,我不知道为什么asp.net会对编码完的url再进行编码,并且这个编码如何在js里解码,只要知道它在js里面怎么解码的就比较好些了,因为我的自定义编码会使url变长很多,风险就更大些。

 回复 引用 查看   
#17楼 2007-05-30 14:11 阿不      
@浪子
今天刚好也遇到这个问题了,也找到解决办法了。
你可以看http://www.loverer.com/article.asp?id=63
看这两个函数:
function ascii(str){
return str.replace(/[^\u0000-\u00FF]/g,function($0){return escape($0).replace(/(%u)(\w{4})/gi,"\\u$2")});
}
function unascii(str){
return unescape(str.replace(/\\u/g,"%u"));
}
我的问题是解决了,不知道适不适合你。:)

 回复 引用 查看   
#18楼[楼主] 2007-05-30 14:26 浪子      
我找到这个问题的原因了,这是我的解决方案,不过没有你的简洁。

 回复 引用   
#19楼 2007-12-10 16:31 过路人[未注册用户]
谢谢,今天又学习了
 回复 引用   
#20楼 2007-12-28 09:18 jeffcn2[未注册用户]
谢谢了哈,其实我也没碰到过那个问题,只是在中文进行编码遇到了问题
发表评论

昵称: [登录] [注册]

主页:

邮箱:(仅博主可见)

评论内容:

  登录  注册

[使用Ctrl+Enter键快速提交评论]

0 631192 muwMBHvN7aw=