Loading

JavaWeb中文乱码问题

概述

在 JavaWeb 中通过 请求(request)响应(response) 来进行 数据传递 的过程中,有一个不可避免的问题,即传输的数据中有可能包含中文。

当传输的数据中包含中文时,往往会出现一个常见问题,即 中文乱码 问题,在这里详细解释一下出现中文乱码问题的原因以及解决的方法,服务器以 Tomcat为准

出现中文乱码问题的原因

所谓的 请求响应,其实是数据的 流向 问题

  • 浏览器 端发送数据到 服务器 端,称之为 请求(request)
  • 服务器 端发送数据到 浏览器 端,称之为 响应(response)

在浏览器和服务器之间传递数据时,存在一个 编码/解码 的过程

  • 浏览器 -> 服务器:数据以浏览器规定的 字符集待发送的数据 进行 编码,然后发送到服务器(请求);服务器接收到来自浏览器的数据,以自己(服务器)规定的 字符集 对该数据进行 解码
  • 服务器 -> 浏览器:数据以服务器规定的 字符集待发送的数据 进行 编码,然后发送到浏览器(响应);浏览器接收到来自服务器的数据,以自己(浏览器)规定的 字符集 对该数据进行 解码

常用字符集:ISO-8859-1UTF-8GBK...

导致中文乱码原因:

  • 编码 时使用的 字符集解码 时使用的 字符集 不一致,而不同 字符集中文 的编解码方式是 不一致
  • 字符集 本身不包含 中文;例如 ISO-8859-1,它本身不包含中文,如果用它对中文进行 编码 或者 解码,则会直接出现中文乱码

注意:

  • 数据传输时,若数据中包含中文,且 编码解码 所使用的 字符集 不一致,则出现中文乱码问题;这是由于不同的 字符集中文 的编解码方式是 不一致的;例如:用 UTF-8中文 进行编码,再以 GBK 对其解码,会出现中文乱码问题
  • 数据传输时,若数据中不包含中文(只有英文和数字),那么即使 编码解码 所使用的 字符集 不一致,也不会出现乱码问题;这是由于不同的 字符集英文和数字 的编解码方式是 一致的;例如:用 UTF-8英文和数字 进行编码,再以 GBK 对其解码,不会出现乱码问题

解决中文乱码问题的方案

  • 使得 编码解码 时所使用的 字符集 保持一致
  • 使用支持中文的字符集对中文进行 编码解码

请求时中文乱码问题

请求分为 GET 请求与 POST 请求两种方式,它们提交数据的方式是不同的;GET 请求在 请求行(URI) 中提交数据,POST 请求在 请求体 中提交数据

请求编码

所谓的请求编码,即浏览器发送数据给服务器之前,浏览器对数据进行的 编码

  • 直接在浏览器地址栏中给定 URL,这种请求方式一般会将 提交数据UTF-8 进行编码
  • 通过页面上的 表单 或者 超链接 发送请求,则由当前页面的编码(<meta charset="字符集">)来决定 提交数据 的编码方式,例如:当前页面 <meta charset="GBK">,则 提交数据GBK 进行编码

GET 请求中文乱码问题

乱码原因:

  • Tomcat8 之前的版本中,URI(请求行) 的 默认字符集ISO-8859-1,即 GET 请求中提交的数据会以 ISO-8859-1 进行 解码,该字符集是不包含中文的,因此会出现中文乱码
  • Tomcat8 之后,URI默认字符集 被修改为 UTF-8,即 GET 请求中提交的数据会以 UTF-8 进行 解码,此时只要浏览器提交的数据是以 UTF-8 进行 编码 的(<meta charset="UTF-8">),就不会出现中文乱码问题

Tomcat8 之前乱码解决方案:

  • 前提:当前浏览器向服务器发送的数据是以 UTF-8 进行编码的(<meta charset="UTF-8">)
  • CATALINA_HOME/conf/server.xml<Connector> 标签中,设置 URIEncoding="UTF-8",即将 URI默认字符集 修改为 UTF-8,该字符集是包含中文的
<Connector port="8080" protocol="HTTP/1.1"
           connectionTimeout="20000"
           redirectPort="8443" 
           URIEncoding="UTF-8"/>
  • 只要调用了 request.getParameter() 方法获取参数,则获取到的参数一定是使用了 ISO-8859-1 进行 解码错误数据,可以使用 getBytes("ISO-8859-1") 将当前参数以 ISO-8859-1 转回 byte[] 字节数组,再通过 new String(byte[], "字符集") 使用正确的 字符集(UTF-8) 来进行 解码 即可
String username = request.getParameter("username"); // 使用 ISO-8859-1 错误的解码了
byte[] bytes = username.getBytes("ISO-8859-1"); // 退回错误的解码,让字符串通过 ISO-8859-1 返回到字节数组,即还原字节数据
username = new String(bytes, "UTF-8"); // 重新使用正确的字符集(UTF-8)对字节数据进行解码

注意:

  • ISO-8859-1 中转 UTF-8 数据是 安全的,即 ISO-8859-1 错误解码,利用 byte[] bytes = getBytes("ISO-8859-1") 还原字节数组,再以 new String(bytes, "UTF-8") 重新解码数据是可行的
  • GBK 中转 UTF-8 数据是 不安全的,即 GBK 错误解码,利用 byte[] bytes = getBytes("GBK") 还原字节数组,再以 new String(bytes, "UTF-8") 重新解码数据是 不可行的

POST 请求中文乱码问题

乱码原因:

  • Tomcat10 之前,对 请求体 的字符集没有特殊指定,则 请求体 是按照 Servlet规范 中指定的字符集进行 解码 的,而 Servlet规范 的默认字符集是 ISO-8859-1,因此会出现中文乱码
  • Tomcat10 之后,对 请求体 的字符集有了特殊指定,即指定为 UTF-8,则 请求体 不再是按照 Servlet规范 中指定字符集进行 解码,而是以 UTF-8 进行 解码,此时只要保证浏览器发送给服务器的数据是以 UTF-8 进行 编码 的(<meta charset="UTF-8">),就不会出现中文乱码问题

Tomcat10 之前乱码解决方案:

  • 前提:当前浏览器向服务器发送的数据是以 UTF-8 进行编码的(<meta charset="UTF-8">)
  • 在获取请求参数之前,手动设置 请求体 的字符集为 UTF-8,即指定以 UTF-8 对请求体的内容进行 解码,该字符集包含中文,用它对中文进行编码,且保证 编解码 的字符集一致,就不会出现中文乱码问题
request.setCharacterEncoding("UTF-8"); // 这行代码一定要在 获取请求参数之前,否则无效
String username = request.getParameter("username");

响应时中文乱码问题

响应编码

所谓的响应编码,即服务器发送数据给浏览器之前,服务器对数据进行的 编码

  • 服务器通过 response.getWriter() 方法向浏览器发送数据

响应乱码原因

  • 如果在 response.getWriter() 方法调用之前,不手动设置服务器的编码方式,则默认将 响应内容ISO-8859-1 进行编码,若响应内容中包含中文,则必然发生中文乱码(使用了不支持中文的字符集对中文进行编码)
PrintWriter out = response.getWriter();
out.print("你好"); // 必定乱码
  • 在调用 response.getWriter() 方法之前,调用 response.setCharacterEncoding("UTF-8") 方法来手动设置 响应内容UTF-8 进行编码,若响应内容中包含中文,在服务器中 编码 时不会乱码,但在发送给浏览器之后,浏览器是不知道服务器的编码字符集的,它可能默认会以 GBK解码,则页面中从服务器获取来的 中文内容 依旧会乱码(编解码不一致)
response.setCharacterEncoding("UTF-8");
PrintWriter out = response.getWriter();
out.print("你好"); // 服务器端不会乱码,客户端有可能乱码

响应乱码解决方案

  • 在调用 response.getWriter() 方法之前,调用 response.setHeader("Content-Type", "text/html;charset=UTF-8") 方法设置响应头,通知浏览器当前服务器编码的字符集;该方法执行之后,会 自动执行 response.setCharacterEncoding("UTF-8"),即服务器以 UTF-8 对响应内容进行 编码,而浏览器 也会用 UTF-8 进行 解码
response.setHeader("Content-Type", "text/html;charset=UTF-8"); // 这行代码一定要在 response.getWriter() 之前,否则无效
PrintWriter out = response.getWriter();
out.print("你好"); // 服务器端不会乱码,客户端也不会乱码
  • response.setContentType("text/html;charset=utf-8")response.setHeader("Content-Type", "text/html;charset=UTF-8") 效果一致,因此一般使用 response.setContentType("text/html;charset=utf-8") 就可以了
response.setContentType("text/html;charset=utf-8"); // 这行代码一定要在 response.getWriter() 之前,否则无效
PrintWriter out = response.getWriter();
out.print("你好"); // 服务器端不会乱码,客户端也不会乱码
posted @ 2023-03-06 23:19  赵妹儿  阅读(308)  评论(0编辑  收藏  举报