网页中的字符集编码与乱码(3) |
您所在的位置:网站首页 › 静态网页文件类型 › 网页中的字符集编码与乱码(3) |
在上一篇说完了如何 通过文档内的编码声明来确定网页的编码 通过文档内的编码声明来确定网页的编码, 这一篇则开始具体讲述如何通过响应头下的 Content-Type 条目中的 charset 信息来确定文档的编码, 包括如何去配置这个响应头, 以及一些具体的实验, 还有它与文档内编码声明的优先级选择问题. content-type 中的 charset通过前面的介绍, 你已经知道了所谓的"响应头下的 Content-Type 条目中的 charset 信息", 就是这样: 或这样的东西: 它指出了这段响应流的编码. 响应头的优先级为什么更高?前面已经说到, 它的优先级是要高过文档内编码声明的. 这其实也很好理解, 因为它属于外部信息, 而文档内编码声明则属于内部信息. 上一篇说到, 浏览器还要通过嗅探算法才能获取到文档内的编码声明. 所以, 如果服务端的响应已经直接告诉你这段响应流的编码, 你都不用去"嗅探"了, 浏览器自然很乐意直接采用它. 也可以这样理解, 这个编码的头信息服务端是可以不发送的, 现在反而专门主动地告诉你, 怕你不知道, 自然它的可信度也是很高的. 浏览器优先采用它也不足为奇了, 还省略了嗅探的步骤, 提高了效率. 配置静态页面的响应头编码前面说到, 当在 tomcat8 缺省配置下运行时, 它不会为 Content-Type 中增加编码信息: 我们需要主动去配置这个信息. 具体而言, 可以去到工程的 web.xml 下, 在 web-app 根节点下增加一个 mime-mapping 配置: html text/html;charset=utf-8这里, extension 表示文件的扩展名(后缀名), 而 mime-type 中的内容则成为 Content-Type 的值, 如此, 就增加了编码的信息. web.xml 最终结果如下: 当然, 不同的 web server 间具体配置的语法会有所区别, 如果用的不是 tomcat, 你可以查询相应文档了解. 这样一来, 所有对 html 的静态请求, 响应头就会变成这样: 显然, 这样的一个配置是一个工程全局级别的, 注意: 如果你直接修改 tomcat server 的相关配置文件, 就会成为服务器级别的, 将影响它下面部署的所有工程! 这时你要保证工程下所有的 html 文件都是使用 utf-8 来编码的, 否则由于它具有更高的优先级, 那些比如 gbk 编码的文档将会发生乱码, 在上一篇中的实验中也已经证实了这一点: 在开发实践中应该遵循"审慎"原则, 当你做某项调整时, 要清楚知道它的后果. 你不能光顾着解决自己当下碰到的问题, 还要留心是否会引发更多的问题, 也即是人们常说的 “按下葫芦起了瓢”. 很不幸的, 碰到乱码问题时, 我们经常是"病急乱投医", 而这点又往往是因为我们对编码问题没有一个整体认识所导致的. 配置动态响应的响应头编码信息如果说静态的响应头配置在 tomcat8 中显得还不是很灵活, 那么在动态的响应中, 你则可以自由地根据需要调整. 比如这样: import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.ServletOutputStream; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @WebServlet("/encoding/page/rightHeader/utf8") public class EncodingRightHeaderUTF8 extends HttpServlet { protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setHeader("Content-type", "text/html;charset=utf-8"); ServletOutputStream os = response.getOutputStream(); os.write("编码与 header 一致,UTF-8.".getBytes("utf-8")); } }或者这样: @WebServlet("/encoding/page/rightHeader/gbk") public class EncodingRightHeaderGBK extends HttpServlet { protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setHeader("Content-type", "text/html;charset=gbk"); ServletOutputStream os = response.getOutputStream(); os.write("编码与 header 一致,GBK.".getBytes("gbk")); } }文档中也可以省略 meta charset 声明了. 当然, 现在你要注意的依旧是要保证宣称编码与实际所用的一致: 正如上面两段代码呈现的那样, 宣称是 utf-8, 实际也得是 utf-8;宣称是 gbk, 实际也得是 gbk. 不管是文档内的还是文档外的, 保持一致性才能避免乱码的发生. 以上两种情况下, 文档都能浏览器中正确显示: 假如不一致: @WebServlet("/encoding/page/wrongHeader") public class EncodingWrongHeader extends HttpServlet { protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setHeader("Content-type", "text/html;charset=gbk"); //宣称 gbk ServletOutputStream os = response.getOutputStream(); os.write("编码与 header 不一致,乱码!".getBytes("utf-8")); //实际 utf-8 } }结局依然是乱码: 以上, 为每个请求都单独设置了响应头信息, 能够做到很灵活的设置. 但有时我们可能也不想处处都要这样弄一下, 对于动态响应, 同样可以对全局(或者更小一点的局部)做一个统一配置, 这点可以通过 filter 来做到. 不过这些不是这里谈论的重点, 这里也是稍微提下. 优先级测试通过动态响应, 很容易构建一个冲突的编码声明的例子: @WebServlet("/encoding/page/rightHeaderWrongMeta") public class EncodingRightHeaderWrongMeta extends HttpServlet { protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setHeader("Content-type", "text/html; charset=utf-8"); // utf-8 与 getbytes 一致 ServletOutputStream os = response.getOutputStream(); String meta = "";// gbk 与 getBytes 冲突 os.write((meta + "编码与 header 一致,与 meta 冲突,header 优先级高,结果正常。").getBytes("utf-8")); // 实际 utf-8 } }如上, Content-Type 中是 utf-8, 文档内声明 meta charset 是 gbk, 实际是 utf-8. 最终, 显示是正常的: 虽然 meta charset 给出了一个错误的建议, 但 Content-Type 的优先级更高, 浏览器先采纳了它, 忽略了文档内的声明. 反之, 如果优先级更高的 Content-Type 给出了错误的建议, 页面就会乱码了, 具体代码及演示略, 读者可以自行构建类似的测试. 另: 我这里一直用的是 Java 平台来演示, 但这些 meta 呀, headers 呀, Content-Type 呀, response 呀之类的都是跟平台无关的, 属于 html 规范或 http 协议范畴, 无论你是 php, asp 还是 nodejs 等等, 与这些相关的东西都是类似的, 原理也是相通的. 如果你不是 Java 平台, 应该也不难构建类似的测试. 关于通过响应头下的 Content-Type 条目中的 charset 信息来确定文档的编码就介绍到这里, 下一篇将讨论 BOM 存在时的页面编码的确定问题. |
CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3 |