URL 中,查詢字符串與HTML實體沖突,可能帶來的問題.


此問題相關信息(我不放在最前面,似乎有些朋友會找不到的樣子.)

IE10+, Safari5.17+, Firefox4.0+,Opera12+, Chrome7+ 已經按新標准實現. 所以就沒有這個問題了.
 
參考標准 :  http://www.w3.org/html/ig/zh/wiki/HTML5/tokenization  新標准明確提到,如果實體后面遇到的不是;且下一個是= 那么就不處理的.就是為了解決這個坑爹的問題的.

 

我們來看demo :

<a href="http://www.baidu.com?a=1&reg=2&reg_a=3" >悲劇</a>

 
部分瀏覽器(對應上面已經按新標准實現的版本之下的,各個瀏覽器.)
點上面的鏈接, 會自動把  &reg 轉意成® (部分瀏覽器會自動對轉意后的字符進行編碼) .  
 
這個bug.的本質,就是當HTML中出現相關HTML實體(HTML character entity)時.就自動轉意處理了. 所以理論上, 用腳本,動態創建的資源則沒有這個問題,比如 new Image().src = 'http://www.baidu.com?a=1&reg=2'; 甚至動態創建的iframe.亦如此.
 
IE9- 有兩個問題比其他瀏覽器更嚴重:
1. 用腳本跳轉當前頁比如 location.href = xxx,或 location.replace(xxx) .又或者是調用 window.open(xxx);如果查詢字符串中包含這些html實體, 仍然會觸發這個問題... 
2. ,參見標准, 你會知道實體+"其他字符"   ,    "其他字符中",哪些與實體連接在一起,是沒有這個問題的. 比如 &rega  , &reg1     其中a, 1 與 &reg 連接就不會有這種問題,從標准角度,甚至是  &reg_a 也不應有問題. 但是IE9-又一次打敗了我們.  至於其他特殊字符如 # ~ 等.在各個瀏覽器中表現各異. 考慮我們在設計字段名時,不大可能出現那些字符.我們也不再糾結其他瀏覽器在此處實現的差異.
 
 
 
所以,理論上,這個問題應該是后端的同學,在輸出html時.更加要注意的問題.  而前端同學,要注意的則是跳轉或彈窗時的url中是否有相關的字段包含一個無分號即為html實體的情況.
 
至於IE為啥這么特殊...我也沒想明白...
 
那么,無論后端同學也好,前端同學也罷,我們可能更改已經定好的字段成本比較高.  所以其實最妥善的辦法,應該是這樣子: (感謝 @辰光未然 的提醒.)
var fixURL = function (url) {
    return url.replace(/&/g,'&amp;');
};
//使用fixURL 去替換url中的&.然后再輸出給html, 或者跳轉鏈接,又或者彈窗... 當然,前端的同學在js代碼中之所以要這樣做.主要是受IE的拖累...

  

 
 
 
 
 
 
 
那么大概,很多HTML 實體都會出問題:
 
 
這個表里, 沒有分號結尾的,都是隱患...  也就是下面這106個: (感謝 @kenny 提供的最新的list 地址. 我花了點時間寫了個腳本.把需要處理的,都抓了出來.)
 
 
我們可以用下面這個腳本來幫忙做檢測 :
 
   var checkURL = function () {
    var list = [ //106
            '&Aacute',
            '&aacute',
            '&Acirc',
            '&acirc',
            '&acute',
            '&AElig',
            '&aelig',
            '&Agrave',
            '&agrave',
            '&AMP',
            '&amp',
            '&Aring',
            '&aring',
            '&Atilde',
            '&atilde',
            '&Auml',
            '&auml',
            '&brvbar',
            '&Ccedil',
            '&ccedil',
            '&cedil',
            '&cent',
            '&COPY',
            '&copy',
            '&curren',
            '&deg',
            '&divide',
            '&Eacute',
            '&eacute',
            '&Ecirc',
            '&ecirc',
            '&Egrave',
            '&egrave',
            '&ETH',
            '&eth',
            '&Euml',
            '&euml',
            '&frac12',
            '&frac14',
            '&frac34',
            '&GT',
            '&gt',
            '&Iacute',
            '&iacute',
            '&Icirc',
            '&icirc',
            '&iexcl',
            '&Igrave',
            '&igrave',
            '&iquest',
            '&Iuml',
            '&iuml',
            '&laquo',
            '&LT',
            '&lt',
            '&macr',
            '&micro',
            '&middot',
            '&nbsp',
            '&not',
            '&Ntilde',
            '&ntilde',
            '&Oacute',
            '&oacute',
            '&Ocirc',
            '&ocirc',
            '&Ograve',
            '&ograve',
            '&ordf',
            '&ordm',
            '&Oslash',
            '&oslash',
            '&Otilde',
            '&otilde',
            '&Ouml',
            '&ouml',
            '&para',
            '&plusmn',
            '&pound',
            '&QUOT',
            '&quot',
            '&raquo',
            '&REG',
            '&reg',
            '&sect',
            '&shy',
            '&sup1',
            '&sup2',
            '&sup3',
            '&szlig',
            '&THORN',
            '&thorn',
            '&times',
            '&Uacute',
            '&uacute',
            '&Ucirc',
            '&ucirc',
            '&Ugrave',
            '&ugrave',
            '&uml',
            '&Uuml',
            '&uuml',
            '&Yacute',
            '&yacute',
            '&yen',
            '&yuml'
        ];
       
    return function (url) {
        var l = list;
        var i = l.length;
        var matchIndex;
        var current;
        var nextchar;
        var errors = [];
        for (; i--;){
            matchIndex = url.indexOf(l[i]);
            current = l[i];
            if(matchIndex > -1){
                if((current === '&amp' || current === '&AMP') && url.charAt(matchIndex + 4) === ';'){
                    //如果是 &amp; 或 &AMP; 我們就認為是故意要輸出 & ,比如是一個調用fixURL方法修正過的URL.里面的& 會被我們替換為 amp;
                    //所以,我們要跳過它,去檢查后面.
                    continue;
                }
                nextchar = url.charAt(matchIndex + current.length);
                if(!/[a-zA-Z0-9]/.test(nextchar)){
                    //此處我們只要發現任意一個 ,如 &reg后面緊隨字符不在 a-z,A-Z,0-9范圍內.就算有問題.
                    //這樣處理實際和標准的細節以及瀏覽器實現有細微差異. 但是本着任何瀏覽器來跑case,都能發現潛在威脅的原則.和實現復雜度的考慮.
                    // 我們姑且粗暴的這樣處理了. 似乎還不錯.
                    
                    errors.push(current + nextchar);
                }
            }
        }
        if(errors.length){
            throw Error('contains : \n' + errors.join('\n'));
        }
    };
}();

  

 
 
 
 
test case 1: 
  var url  = '//www.baidu.com?a=1&amp=2&lt=3&reg=4';           
  document.onclick = function () { //IE9-好了.證明我們的修正是ok的了.
        window.open(fixURL(url))
  };

  


 
 test case 2:
  var url  = '//www.baidu.com?a=1&amp=2&lt=3&reg=4';     
    try{
        checkURL(url);
    }catch(e){
        alert(e.message)
    }
 
 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM