编码问题 UTF-8 & GBK

简介: 记一次奇怪的编码问题。 * Meta Element vs Response Header * Meta 的作用? * 一个细节 * 细节对编码的影响 ## Meta Element vs Response Header 一个 GBK 编码页面,使用 meta 指定页面编码和使用 response header 指定页面编码。哪个优先级比较高? ###

记一次奇怪的编码问题。

  • Meta Element vs Response Header
  • Meta 的作用?
  • 一个细节
  • 细节对编码的影响

Meta Element vs Response Header

一个 GBK 编码页面,使用 meta 指定页面编码和使用 response header 指定页面编码。哪个优先级比较高?

Case 1

Header 为 utf-8
Meta 为 gbk

$  curl -i  http://127.0.0.1:3000/utf-1
HTTP/1.1 200 OK
X-Powered-By: Express
Content-Type: text/html;charset=utf-8
Accept-Ranges: bytes
Cache-Control: public, max-age=0
Last-Modified: Sat, 08 Sep 2018 03:53:26 GMT
ETag: W/"129-165b7502163"
Content-Length: 297
Date: Sat, 08 Sep 2018 03:54:25 GMT
Connection: keep-alive

<html>
<meta charset="gbk">
<p>
中文(使用 encodeURIComponent ):
<script>
  document.write(encodeURIComponent('中文'));
</script>
</p>
<p>页面跳转(使用 a 标签):
<a href='/?p=中文'>
<script>
  document.write(document.querySelector('a').href);
</script>
</a>
</p>
</html>

Case 2

Header 为 gbk
Meta 为 utf-8

$ curl -i  http://127.0.0.1:3000/utf-2
HTTP/1.1 200 OK
X-Powered-By: Express
Content-Type: text/html;charset=gbk
Accept-Ranges: bytes
Cache-Control: public, max-age=0
Last-Modified: Sat, 08 Sep 2018 03:53:44 GMT
ETag: W/"12b-165b750666a"
Content-Length: 299
Date: Sat, 08 Sep 2018 03:54:31 GMT
Connection: keep-alive

<html>
<meta charset="utf-8">
<p>
中文(使用 encodeURIComponent ):
<script>
  document.write(encodeURIComponent('中文'));
</script>
</p>
<p>页面跳转(使用 a 标签):
<a href='/?p=中文'>
<script>
  document.write(document.querySelector('a').href);
</script>
</a>
</p>
</html>

结论

Response Header 编码优先级高于 meta 信息

Meta 的作用?

既然 response header 优先级比较高,那要使 meta 生效,需要先设置 content-type 为空,然后再用 meta 指定编码,验证一下

不设置 header,指定 meta 编码为 utf-8

注意 response header 中没有 content-type 了
由于 shell 编码是采用 utf-8 的,所有在终端中会显示乱码
能看清 meta 信息即可

$ curl -i http://127.0.0.1:3000/gbk-1
HTTP/1.1 200 OK
X-Powered-By: Express
Content-Type: null
Accept-Ranges: bytes
Cache-Control: public, max-age=0
Last-Modified: Sat, 08 Sep 2018 04:05:23 GMT
ETag: W/"115-165b75b105c"
Content-Length: 277
Date: Sat, 08 Sep 2018 04:06:51 GMT
Connection: keep-alive

<html>
<meta charset="utf-8">
<p>
</p>
</html>

浏览器效果:

不设置 header,指定 meta 编码为 gbk

$ curl -i http://127.0.0.1:3000/gbk-2
HTTP/1.1 200 OK
X-Powered-By: Express
Content-Type: null
Accept-Ranges: bytes
Cache-Control: public, max-age=0
Last-Modified: Sat, 08 Sep 2018 04:05:27 GMT
ETag: W/"113-165b75b22f9"
Content-Length: 275
Date: Sat, 08 Sep 2018 04:07:03 GMT
Connection: keep-alive

<html>
<meta charset="gbk">
<p>
</p>
</html>

效果

结论

在未指定 content-type header 的情况下,可以使用 meta 标签指定页面编码

一个细节

我们把能正常编码的页面放一起看

utf-1 页面

gbk-2 页面

现象

我们发现,不管是 GBK 页面还是 UTF 页面,使用 encodeURIComponent('中文') 后的编码都为 %E4%B8%AD%E6%96%87 。也就是说无论页面编码,encodeURIComponent 都使用 UTF 进行编码。

但使用 a 标签,将中文字符放在 HTML 代码中,或在 js 中直接使用 location.href = '/?p=中文' 进行跳转,该编码格式会与页面编码有关。如上图中,UTF 页面会将中文编码成 %E4%B8%AD%E6%96%87, GBK 页面会将中文编码成 %D6%D0%CE%C4,这里面坑就比较大了。

编码细节的影响

Nodejs

在 nodejs 中,使用 express 框架,分别请求 http://127.0.0.1:3000/?p=%D6%D0%CE%C4 和 http://127.0.0.1:3000/?p=%E4%B8%AD%E6%96%87 ,使用 req.query.xxx 获取 url 参数时,会分别返回 %D6%D0%CE%C4 和 中文,这点也比较好,默认会使用 utf-8 解码,不行的返回原编码。

使用 GBK to UTF 的编码解决即可。

Java Servlet

不像 Nodejs,Java 编码不正确设置,会乱码

@WebServlet("/GBK")
public class GBK extends HttpServlet {

    /**
     * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
     */
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // 设置为 GBK 编码
        request.setCharacterEncoding("GBK");

        String name =request.getParameter("name");

        response.setContentType("text/html; charset=utf-8");
        response.getWriter().append("Hello " + name);
    }
}
~ curl -i http://localhost:8080/servlet/GBK\?name\=%D6%D0%CE%C4
HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
Content-Type: text/html;charset=utf-8
Content-Length: 12
Date: Sat, 08 Sep 2018 06:47:52 GMT

Hello 中文%

️ 其中需要注意的是,URL 中由于是 GBK 编码,实际处理过程中:

// 从 connector 中拿到配置项
boolean useBodyEncodingForURI = connector.getUseBodyEncodingForURI();
if (enc != null) {
    parameters.setEncoding(enc);
    // 配置了 bodyencodingforuri 才对 uri 进行制定编码 decode
    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);
    }
}
// 处理 query 参数
parameters.handleQueryParameters();
  • 而 useBodyEncodingForURI 需要容器侧设置,如:tomcat 中的 server.xml

    <Connector useBodyEncodingForURI="true"/>

References

相关文章
|
存储 移动开发 Linux
Linux系统之部署h5ai目录列表程序
【5月更文挑战第3天】Linux系统之部署h5ai目录列表程序
832 2
|
机器学习/深度学习 算法 openCL
高效、轻量的深度学习框架MNN
MNN是一个高效、轻量的深度学习框架。
高效、轻量的深度学习框架MNN
|
10月前
|
存储 缓存 Cloud Native
EMR StarRocks Stella 内核正式发布,登顶 TPC 榜单全球第一
EMR Serverless StarRocks 重磅发布全新企业级版本内核 Stella (StarRocks Efficient and Lightening-fast Lakehouse),完全兼容开源 StarRocks,为用户提供企业级的产品功能、卓越的性能及稳定性保障。
|
11月前
|
Web App开发 Ubuntu 安全
Ubuntu 26.04 LTS (Resolute Raccoon) - 现代化的企业与开源 Linux
Ubuntu 26.04 LTS (Resolute Raccoon) - 现代化的企业与开源 Linux
3647 1
Ubuntu 26.04 LTS (Resolute Raccoon) - 现代化的企业与开源 Linux
|
IDE 安全 Android开发
深入探索Android与iOS操作系统的架构差异
本文旨在对比分析Android和iOS两大主流移动操作系统在架构设计上的根本差异。通过详细解读两者的系统架构、开发环境、以及安全性等方面,揭示它们各自的特点及优势,为开发者选择合适的平台提供参考。
|
缓存 NoSQL Java
Java高并发实战:利用线程池和Redis实现高效数据入库
Java高并发实战:利用线程池和Redis实现高效数据入库
1275 0
|
人工智能 算法 机器人
人工智能浪潮下的人类社会:重构秩序,还是共舞未来?
人工智能正以前所未有的速度改变社会结构,从工具变为行为参与者。它不仅提升效率,更具备学习、生成与决策能力,重塑就业、法律、财富分配乃至国家治理模式。面对AI风暴,唯有制度创新与全民共享,方能避免技术垄断与社会失衡。
566 0
|
SQL 数据库 流计算
实时计算 Flink版操作报错之采集fink指标写入 InfluxDB 时遇到报错,如何处理
在使用实时计算Flink版过程中,可能会遇到各种错误,了解这些错误的原因及解决方法对于高效排错至关重要。针对具体问题,查看Flink的日志是关键,它们通常会提供更详细的错误信息和堆栈跟踪,有助于定位问题。此外,Flink社区文档和官方论坛也是寻求帮助的好去处。以下是一些常见的操作报错及其可能的原因与解决策略。
|
SQL 关系型数据库 MySQL
SQL日期函数
SQL日期函数
441 0
|
存储 并行计算 异构计算
显存优化综述
显存优化综述
1154 1
显存优化综述

热门文章

最新文章