我们在写项目的时候经常会传递一些中文参数,但是j2e默认使用ISO-8859-1来编码和解码,所以很容易出现中文乱码问题。这里我做一个统一的整理,其实这里的中文乱码问题和上一篇的路径问题都是j2e经常遇见的很普遍的问题,不管你使用不使用框架都是很容易发生的,所以好好的整理一下还是很有必要的。

  • 具体有可能发生乱码的地方有:

1. 从数据库到Java程序 byte——〉char

2. 从Java程序到数据库 char——〉byte

3. 从文件到Java程序 byte——〉char

4. 从Java程序到文件 char——〉byte

5. 从流到Java程序byte——〉char

6. 从Java程序到流char——〉byte

7. 从Java程序到页面显示 char——〉byte

8. 从页面form提交数据到Java程序byte——〉char



其他的暂时先不管,现在我们先来处理Servlet中的中文乱码,也就是上面最后2点。

  • 首先必须要明白的,Tomcat的参数问题无论是GET或是POST方式都是用8859_1编码的。

1,GET方式要看tomcat下源码,

protected static Locale defaultLocale = Locale.getDefault();貌似这里的编码使用的本地的编码,但是我们仔细看下

org.apache.tomcat.service.http. HttpRequestAdapter类

---- line=new String(buf, 0, count, Constants.CharacterEncoding.Default);

----  Constants.CharacterEncoding.Default=8859_1  

这段代码不好跟踪,千万不要被一些假象迷惑住,HttpRequestAdapter是从RequestImpl中派生的。但是,实际上用8080端口的Server并没有直接用到RequestImpl,而是用了HttpRequestAdapter来获得queryString。

2,POST方式我们看下javax.servlet.http.HttpUtils的parsePostData方法,下面贴出源码。

public static Hashtable parsePostData(int len, ServletInputStream in)
{
if (len <= 0)
{
return new Hashtable();
}
if (in == null)
{
throw new IllegalArgumentException();
} byte[] postedBytes = new byte[len];
try
{
int offset = 0;
do
{
int inputLen = in.read(postedBytes, offset, len - offset);
if (inputLen <= 0)
{
String msg = lStrings.getString("err.io.short_read");
throw new IllegalArgumentException(msg);
}
offset += inputLen;
}
while (len - offset > 0);
}
catch (IOException e)
{
throw new IllegalArgumentException(e.getMessage());
} try
{
String postedBody = new String(postedBytes, 0, len, "8859_1");
return parseQueryString(postedBody);
}
catch (UnsupportedEncodingException e)
{
}
throw new IllegalArgumentException(e.getMessage());
}

其实研究这个tomcat的编码源码没啥意义,也就不用管了,记住就好,不管是GET还是POST,tomcat都是用8859_1来编码的。

  • OK,现在开始整理中文乱码的处理,解决Servlet中的乱码问题。

1,表单提交

POST方式,在代码第一行设置request的编码格式就OK。jsp页面中一般都是设置过编码格式的,一般都是UTF-8,所以我们在这里request也设置用UTF-8解码就OK。

req.setCharacterEncoding("UTF-8");

GET方式,上面的操作不生效,因为get方式提交参数丫的不是在header里面是在url后面跟着的,只能自己用String来转换了。

String userName = new String(req.getParameter("userName").getBytes("ISO-8859-1"),"UTF-8");

2,超链接和重定向

比如:

<a href="/linkin/LinkinServlet?userName=林肯公园">GET方式传参</a>

这2种情况和上面的用GET方式提交表单一样,处理方式也一样,这里不做赘述了。



3,上面的情况都是属于编码级别的,一般的我们的项目上了生产上不管是GET方式还是POST方式,编码格式这些都设置好了,最多是ajxa异步请求的时候增加过滤器编码设置。这里针对tomcat本地开发说下:

如果是GET方式,在修改tomcat配置的地方添加一个属性:URIEncoding,将该属性值设置为UTF-8,即可让Tomcat(默认ISO-8859-1编码)以UTF-8的编码处理get请求。

<Connector port="8080"   protocol="HTTP/1.1"      connectionTimeout="20000"    redirectPort="8443" URIEncoding="UTF-8" />

如果是POST方式,就只能增加编码过滤器。没使用框架的话用自己写一个过滤器,建议将过滤的编码写成配置的,比如写在<init-param>标签中。如果使用了spring,则直接配置就OK。

<filter>
<filter-name>encodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<!-- 编码格式 -->
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
<!-- 控制是否强制设置编码,如果是true,不管request中有没有指定编码,这个过滤器设置编码都会被触发,如果是false,只是在request中没有设置编码的时候被触发设置上这里的编码 -->
<init-param>
<param-name>forceEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<!-- 这里是jsp中的编码格式 都被设置为统一的了 -->
<filter-mapping>
<filter-name>encodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>

下面贴出这个类的源码:核心就是doFilterInternal()方法。

package org.springframework.web.filter;

import java.io.IOException;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; public class CharacterEncodingFilter extends OncePerRequestFilter
{ /**
* @param request
* @param response
* @param filterChain
* @throws ServletException
* @throws IOException
* 个人不喜欢forceEncoding这种控制和别的控制一起写的写法,单独拎出来写成旗标多好
* 一般情况下,为了不冲掉自己的request中原有的编码,建议forceEncoding配置成false
*/
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException
{
//1,如果配置的encoding为空,不设置编码
//2,如果配置的encoding不为空,forceEncoding为true,不管request中有没有自己的编码都会设置编码
//3,如果配置的encoding不为空,forceEncoding为false,只有在request没有自己的编码的时候才会设置编码
if (this.encoding != null && (this.forceEncoding || request.getCharacterEncoding() == null))
{
request.setCharacterEncoding(this.encoding);
if (this.forceEncoding)
{
response.setCharacterEncoding(this.encoding);
}
}
filterChain.doFilter(request, response);
} }

另外这里也稍微花点时间来说明一下,OncePerRequestFilter这个抽象过滤器很好的实现了对每个request只执行一次过滤操作,如果有类似的需求可以继承该类并实现doFilterInternal方法来完成,上面的CharacterEncodingFilter就是继承这个抽象类的,下面贴出这个抽象类的源码,后面整理框架的时候我会做详细的整理的。

package org.springframework.web.filter;

import java.io.IOException;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; public abstract class OncePerRequestFilter extends GenericFilterBean { public static final String ALREADY_FILTERED_SUFFIX = ".FILTERED"; //开始过滤,有点小技巧,实现了只过滤一次的功能
public final void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain)
throws ServletException, IOException { if (!(request instanceof HttpServletRequest) || !(response instanceof HttpServletResponse)) {
throw new ServletException("OncePerRequestFilter just supports HTTP requests");
}
HttpServletRequest httpRequest = (HttpServletRequest) request;
HttpServletResponse httpResponse = (HttpServletResponse) response; String alreadyFilteredAttributeName = getAlreadyFilteredAttributeName();
if (request.getAttribute(alreadyFilteredAttributeName) != null || shouldNotFilter(httpRequest)) {//第2次进来
// Proceed without invoking this filter...
filterChain.doFilter(request, response);
}
else {//第一次进来
// Do invoke this filter...
request.setAttribute(alreadyFilteredAttributeName, Boolean.TRUE);
try {
doFilterInternal(httpRequest, httpResponse, filterChain);
}
finally {//最后清空alreadyFilteredAttributeName属性
// Remove the "already filtered" request attribute for this request.
request.removeAttribute(alreadyFilteredAttributeName);
}
}
} protected String getAlreadyFilteredAttributeName() {
String name = getFilterName();
if (name == null) {
name = getClass().getName();
}
return name + ALREADY_FILTERED_SUFFIX;
} protected boolean shouldNotFilter(HttpServletRequest request) throws ServletException {
return false;
} //推迟到子类实现,真正的过滤的方法
protected abstract void doFilterInternal(
HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException; }



4,向页面传参

后台处理完逻辑后,跳转到页面上,如果页面上中文出现乱码,比如

resp.getWriter().write(req.getParameter("userName"));

在返回响应之前添加

response.setCharacterEncoding("UTF-8");

其实用上面那个设置编码格式的方式不怎么好,最好用下面这种的。

response.setContentType("text/html;charset=UTF-8");
response.setContentType("application/json;charset=gbk");

5,最后2点补充。

第一点,常用中文字符用utf-8编码占用3个字节,用GBK、GB2312编码的汉字占2个字节,严格地用iso8859-1无法表示汉字,只能转为问号。所以当我们传递的中文如果是基数的时候,即使我们正常编码和转码了也会出现乱码,在IE6版本一下就会出现这种情况。解决的办法就是在前台页面就编码下中文;

java.net.URLEncoder.encode("林肯公园","UTF-8");

在后台不需要转码,Servlet引擎已经帮我做好了。某些情况下比如搜索引擎的搜索时,如果发送请求的是GET方式,浏览器上面的中文就会变成application/x-www-form-urlencoded MIME字符串,比如“%E6%E6”这种,Servlet引擎来解析这段字符串的时候自动会给我们转会成汉字的。转码的代码如下:

URLDecoder.decode(req.getParameter("userName"), "UTF-8");

第二点,BASE64Encoder,BASE64Decoder编码和解码,这种编码和解码还会涉及算法的,了解下好了。这2个类在API中查不到,因为JDK已经不推荐使用了,不过我个人觉得还是挺好使的。

String name = new sun.misc.BASE64Encoder().encode("林肯公园".getBytes());// name:wda/z7mr1LA=
System.out.println(new String((new sun.misc.BASE64Decoder()).decodeBuffer(name)));//林肯公园

OK,最后贴出自己写的Servlet和jsp:

package linkin;

import java.io.IOException;
import java.net.URLDecoder; import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; /**
* @author LinkinPark
* @author 2015-7-10
* @Descri 解决j2e中文乱码问题
*/
public class LinkinServlet extends HttpServlet
{
private static final long serialVersionUID = 1L; @Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
{
//req.setCharacterEncoding("UTF-8");//解决POST方式
//String userName = new String(req.getParameter("userName").getBytes("ISO-8859-1"),"UTF-8");//解决GET方式
System.out.println(req.getParameter("userName"));
System.out.println(URLDecoder.decode(req.getParameter("userName"), "UTF-8"));
req.setAttribute("userName", "林肯公园");
//resp.setCharacterEncoding("UTF-8");//解决向页面传参乱码问题,建议使用下面这种
resp.setContentType("text/html;charset=UTF-8");//解决向页面传参乱码问题,建议使用这种
resp.getWriter().write(req.getParameter("userName"));
//req.getRequestDispatcher("/jsp/Linkin1.jsp").forward(req, resp);
//resp.sendRedirect("/linkin/jsp/Linkin1.jsp"); //下面使用base64来编码和解码
String name = new sun.misc.BASE64Encoder().encode("林肯公园".getBytes());// name:wda/z7mr1LA=
System.out.println(new String((new sun.misc.BASE64Decoder()).decodeBuffer(name)));//林肯公园 } @Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
{
this.doGet(req, resp);
} public static void main(String[] args) throws Exception
{ } }
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%
String path = request.getContextPath();
String basePath = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + path + "/";
%> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<base href="<%=basePath%>"> <title>Servlet中文乱码</title>
<script type="text/javascript" src="/linkin/jsp/jquery-1.8.0.js"></script>
<script type="text/javascript">
var huhu = function(){
var userName = $("#userName").val();
$.ajax({
url : "/linkin/LinkinServlet",
type : "GET",
async : true,
data:{userName:userName},
dataType : "json",
success : function(a) {
}
});
} </script> </head> <body>
<form action="/linkin/LinkinServlet" method="GET">
姓名:<input type="text" name="userName" id="userName" />
<input type="button" value="提交" name="tijiao" onclick="huhu();"/>
<a href="/linkin/LinkinServlet?userName=<%=java.net.URLEncoder.encode("林肯公园","UTF-8") %>林肯公园">GET方式传参</a>
</form>
</body>
</html>

Servlet--j2e中文乱码解决的更多相关文章

  1. Flex用HTTPService调用servlet返回中文乱码解决

    servlet中使用URLEncoder.encode对输出内容进行编码 Flex中使用decodeURIComponent进行解码

  2. java中文乱码解决之道(九)-----总结

    乱码,我们前台展示的杀手,可能有些朋友和我的经历一样:遇到乱码先按照自己的经验来解决,如果没有解决就google,运气好一搜就可以解决,运气不好可能够你折腾一番了.LZ之所以写这个系列博客就是因为遇到 ...

  3. JSP和Servlet的中文乱码处理

    JSP和Servlet的中文乱码处理 前几天学习了JSP和Servlet中有关中文乱码的一些问题,写成了博客,今天进行更新一下.应该是可以解决日常的乱码问题了.现在作以下总结希望对需要的人有所帮助.我 ...

  4. Javaee中文乱码解决方法

    分类: javaee2015-07-09 16:35 29人阅读 评论(0) 收藏 编辑 删除 post 中文乱码解决方式 接受数据的时候设置 request.setCharacterEncoding ...

  5. java中文乱码解决之道(七)-----JSP页面编码过程

    我们知道JSP页面是需要转换为servlet的,在转换过程中肯定是要进行编码的.在JSP转换为servlet过程中下面一段代码起到至关重要的作用. <%@ page language=" ...

  6. java中文乱码解决之道(七)—–JSP页面编码过程

    我们知道JSP页面是需要转换为servlet的,在转换过程中肯定是要进行编码的.在JSP转换为servlet过程中下面一段代码起到至关重要的作用. <%@ page language=" ...

  7. idea配置tomcat及中文乱码解决

    放在前面:不要使用tomcat10,访问自己的页面会报404错误,目前无解,在这个坑爬了一下午,最终换了tomcat 9才解决.所以我选择了tomcat 9 + idea 2021.2版本 配置步骤: ...

  8. java中文乱码解决之道(二)-----字符编码详解:基础知识 + ASCII + GB**

    在上篇博文(java中文乱码解决之道(一)-----认识字符集)中,LZ简单介绍了主流的字符编码,对各种编码都是点到为止,以下LZ将详细阐述字符集.字符编码等基础知识和ASCII.GB的详情. 一.基 ...

  9. Arch Linux中文乱码解决

    Arch Linux中文乱码解决 1.安装中文字体 pacman -S wqy-zenhei ttf-fireflysung (flash乱码)   ---乱码的原因就是缺少中文字体的支持,下载文泉驿 ...

随机推荐

  1. Qt 地址薄 (二) 添加地址

    在上一篇 Qt 地址薄 (一) 界面设计 中,主要是实现了地址簿的界面,使用布局管理器进行元素的布局,并解释了"子类化" 和"所有权"的概念. 本篇将在上面的基 ...

  2. 加速scp传输速度

    当需要在机器之间传输400GB文件的时候,你就会非常在意传输的速度了.默认情况下(约125MB带宽,网络延迟17ms,Intel E5-2430,本文后续讨论默认是指该环境),scp的速度约为40MB ...

  3. asp.net core 教程(五)-配置

    Asp.Net Core-配置 Asp.Net Core-配置 在这一章,我们将讨论 ASP.NET Core项目的相关的配置.在解决方案资源管理器中,您将看到 Startup.cs 文件.如果你有以 ...

  4. IdentityServer(11)- 使用Hybrid Flow并添加API访问控制

    关于Hybrid Flow 和 implicit flow 我在前一篇文章使用OpenID Connect添加用户认证中提到了implicit flow,那么它们是什么呢,它和Hybrid Flow有 ...

  5. Git知识总览(三) 分支的创建、删除、切换、合并以及冲突解决

    前两篇博客集中的聊了git的一些常用命令,具体请参见<Git知识总览(一) 从 git clone 和 git status 谈起>.<Git知识总览(二) git常用命令概览> ...

  6. Go并发模式:管道与取消

    关键字:Go语言,管道,取消机制,并发,sync.WaitGroup,包引用,通道,defer,select GO并发模式:管道与取消 简介 Go的并发能力可以使构建一个流数据管道变得非常容易,并且可 ...

  7. Linux ssh双向免密认证

    一.实现原理 使用一种被称为"公私钥"认证的方式来进行ssh登录."公私钥"认证方式简单的解释是: 首先在客户端上创建一对公私钥(公钥文件:~/.ssh/id_ ...

  8. 基于Xilinx FPGA的视频图像采集系统

    本篇要分享的是基于Xilinx FPGA的视频图像采集系统,使用摄像头采集图像数据,并没有用到SDRAM/DDR.这个工程使用的是OV7670 30w像素摄像头,用双口RAM做存储,显示窗口为320x ...

  9. CTF---Web入门第二题 上传绕过

    上传绕过分值:10 来源: Justatest 难度:易 参与人数:5847人 Get Flag:2272人 答题人数:2345人 解题通过率:97% bypass the upload 格式:fla ...

  10. 基于Windows环境下cmd/编译器无法输入中文,显示中文乱码解决方案

    基于Windows环境下cmd/编译器无法输入中文,显示中文乱码解决方案 两个月前做C++课设的时候,电脑编译器编译结果出现了中文乱码,寻求了百度和大神们,都没有解决这个问题,百度上一堆解释是对编译器 ...