你寫了一個 JSP 網頁,使用 Big5 編碼:
<%@ page contentType="text/html; charset=Big5"
pageEncoding="Big5"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="Big5">
<title>JSP 網頁</title>
</head>
<body>
名稱:<input type="text" name="name" value="${name}">
</body>
</html>
其中 ${name}
會是資料庫中撈取出來的名稱,放在請求範圍屬性中再轉發至這個 JSP 頁面,如果實際上資料庫中的名稱是「王大犇」,在伺服端的 JVM 中撈取出來的名稱也確認是正確的顯示,但再經由這個 JSP 網頁,卻會是亂碼:
這是自然地,因為你的網頁採 Big5 編碼,對瀏覽器而言,看到的 HTML 是:
<!DOCTYPE html>
<html>
<head>
<meta charset="Big5">
<title>JSP 網頁</title>
</head>
<body>
名稱:<input type="text" name="name" value="王大?">
</body>
</html>
在〈Big 5 網頁難字〉中介紹過,可以將特殊字元用實體編號表示,所以可以在伺服端 JVM 中使用 StringEscapeUtils.escapeHtml()
之類的程式庫,將「王大犇」處理為「王大犇
」:
request.setAttribute("name", StringEscapeUtils.escapeHtml("王大犇"));
這是因為瀏覽器看到:
名稱:<input type="text" name="name" value="王大犇">
再將「王大犇
」轉為實際的「王大犇」。然而,如果這麼撰寫:
<%@ page contentType="text/html; charset=Big5"
pageEncoding="Big5"%>
<!DOCTYPE html>
<html>
<head>
<script type="text/javascript">
window.onload = function() {
document.getElementById('name').value = '${name}';
};
</script>
<meta charset="Big5">
<title>JSP 網頁</title>
</head>
<body>
名稱:<input id="name" type="text" name="name">
</body>
</html>
如果 ${name}
是將「王大犇」處理為「王大犇
」的結果,那會看到:
這個問題在〈JavaScript 編碼基礎〉中略談過,瀏覽器對於收到的 HTML 會進行剖析,這是實體編號被剖析為對應字元的時間點,這也是為什麼:
名稱:<input type="text" name="name" value="王大犇">
最後會看到「王大犇」,瀏覽器完成 HTML 剖析後,會建立 JavaScript 環境中對應的 DOM 物件,如果直接將字串指定給 DOM 的特性,那就會以實際的字串值設定給 DOM 特性,而不會再經由剖析,不過〈JavaScript 編碼基礎〉基礎提過,innerHTML
特性是個例外,指定給 innerHTML
的字串,會嘗試再進行 HTML 剖析,所以如果這麼撰寫:
<%@ page contentType="text/html; charset=Big5"
pageEncoding="Big5"%>
<!DOCTYPE html>
<html>
<head>
<script type="text/javascript">
window.onload = function() {
var span = document.createElement('span');
span.innerHTML = '${name}';
document.getElementById('name').value = span.innerHTML;
};
</script>
<meta charset="Big5">
<title>JSP 網頁</title>
</head>
<body>
名稱:<input id="name" type="text" name="name">
</body>
</html>
那就會看到正確的「王大犇」。
你會想,誰會那麼無聊,使用 JSP 將字串產生為 JavaScript,再用 JavaScript 指定給輸入欄位呢?有的時候,尤其是維護舊系統為主的公司,有些部份是你可以修改的,但有些剖份是你不能修改的(例如也許是另一個部份負責的子系統),就會遇到類似的情況。
例如,也許另一個子系統傳回的字串一徑作實體編號,你的 JSP 中,使用 Ajax 進行查詢,像是某個查詢人名,另一個子系統直接將查得的人名作實體編號傳回以下結果:
王大犇
如果你這麼撰寫:
request.onreadystatechange = function() {
if(request.readyState === 4) {
if(request.status === 200) {
document.getElementById('name').value = request.responseText;
}
}
};
那文字欄位就會出現「王大犇
」,但如果你這麼撰寫:
<%@ page contentType="text/html; charset=Big5"
pageEncoding="Big5"%>
<!DOCTYPE html>
<html>
<head>
<script type="text/javascript">
window.onload = function() {
var xhr = window.XMLHttpRequest &&
(window.location.protocol !== 'file:'
|| !window.ActiveXObject) ?
function() {
return new XMLHttpRequest();
} :
function() {
try {
return new ActiveXObject('Microsoft.XMLHTTP');
} catch(e) {
throw new Error('XMLHttpRequest not supported');
}
};
var request = xhr();
request.onreadystatechange = function() {
if(request.readyState === 4) {
if(request.status === 200) {
var span = document.createElement('span');
span.innerHTML = request.responseText;
document.getElementById('name').value = span.innerHTML;
}
}
};
request.open('GET', 'response');
request.send(null);
};
</script>
<meta charset="Big5">
<title>JSP 網頁</title>
</head>
<body>
名稱:<input id="name" type="text" name="name"><br>
</body>
</html>
那就可以得到正確的顯示結果。事實上,這些作法,只不過是〈Servlet 中文處理(Tomcat)〉、〈Big 5 網頁難字〉、〈JSP 的轉譯〉、〈JavaScript 編碼基礎〉等基礎觀念的綜合應用,許多程式設計人員在這些基礎不足下,自然就無法變化應用。
再看一個學員來信的例子:
問:若 db 的資料內容若有單引號或雙引號,撈出來會造成 html 顯示不正常,怎麼解決呢?
如果有個輸入欄位:
<input type="text" value="${data}">
如果資料庫中的資料有雙引號,例如 123"456 結果就會是:
<input type="text" value="123"456">
這個 HTML 當然有問題,也許你會想,那就這樣:
<input type="text" value='${data}'>
如果資料庫中的資料有雙引號,例如 123"456 結果就會是:
<input type="text" value='123"456'>
但現在的情況中,資料庫中的資料是雙引號或單引號都有,如果資料實際上是 123'456,你怎麼辦?
簡單的解決方式之一可以用 JSTL,例如:
<%@page contentType="text/html" pageEncoding="UTF-8"%>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>JSP Page</title>
</head>
<body>
<h1>Hello World!</h1>
<%
String text1 = "test'test";
String text2 = "test\"test";
%>
<input type="text" value="<c:out value="<%= text1 %>"/>"><br>
<input type="text" value="<c:out value="<%= text2 %>"/>">
</body>
</html>
<c:out>
預設 escapeXML
是 true
,所以可解決問題,但這位學員又有問題了:
問:不過還真的有一好沒兩好,當我們想要
document.getElementById('xxx').value
給值的時候,卻會老老實實的顯示 html 內碼ex.會顯示 123'456 或 123"456
我不太知道他要用 <c:out>
產生值給 JavaScript 的原因,但要同時滿足兩個需求也非不行:
<%@page contentType="text/html" pageEncoding="UTF-8"%>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html>
<%
String text1 = "test'test";
String text2 = "test\"test";
%>
<html>
<head>
<meta charset="UTF-8">
<title>JSP Page</title>
<script type="text/javascript">
window.onload = function() {
document.getElementById('i3').value =
document.getElementById('i1').value;
document.getElementById('i4').value =
document.getElementById('i2').value;
};
</script>
</head>
<body>
<h1>Hello World!</h1>
<input id="i1" type="hidden" value="<c:out value="<%= text1 %>"/>"><br>
<input id="i2" type="hidden" value="<c:out value="<%= text2 %>"/>"><br>
<input id="i3" type="text"><br>
<input id="i4" type="text">
</body>
</html>
使用 hidden
欄位也可以,或者也可以像先前用動態產生 DOM 元素的方式,這都是先前例子的再變化應用。