解决Smarty4J的中文乱码问题

Smarty是一个好东西,但这个好东西只能用在PHP上。
不过好在出现大牛,将Smarty移植到了Java平台上,并且命名为 Smarty4J 但是Smarty4J有个严重的问题,就是对中文编码的支持有问题。

最近在做期末大作业的项目,用到了Smarty4J与Struts2整合。 项目用到了框架,而框架的内容是通过请求URL地址动态加载的。 请求地址交给Struts处理,Struts调用Smarty模板,然后把视图返回给调用者。调用者再将返回值插入到框架之中。

程序流程如下。

首先,为Struts添加映射:

<package name="getpage" namespace="/GetPages" extends="BASE">  
    <action name="GetIndexPageAction" class="me.friskit.hw.furp.action.getpage.GetIndexPageAction">
        <result type="smarty">/tpl/pages/index.tpl</result>
    </action>
</package>  

映射中使用了内容为“smarty”的result type,具体怎么实现,大家可以看这篇文章

上面的代码声明了两个action,其中一个叫做GetIndexPageAction。功能是调用这个Action,将会返回/tpl/pages/index.tpl(当然是经过Smarty4J处理完成之后的内容)。

index.tpl内容很简单:

<!DOCTYPE html>  
<html>  
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    {include file="../inc/scriptHeader.tpl"}
  </head>

  <body>
    index 测试
  </body>
</html>  

上面的代码中包含中文,所以文件格式以UTF-8保存。

GetIndexPageAction类以后可以在其中加入身份验证等等功能,而现在的功能就是返回SUCCESS。
然后我们执行代码,发现代码中的中文都变成了问号,很明显这是编码问题。

这个问题困扰了我一下午,然后机缘巧合之中,我发现,如果用一个JSP页面直接Forward到action,文字显示就正常。而这个JSP页面中,有Myeclipse自动添加的charset="UTF-8"。

这个时候让我们回想一下JSP运行的流程。

首先,JSP通过编译生成Servlet,在编译前,我们可以通过添加这样的标签实现指定编译字符集

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>  

但是由于在Smarty4J中,Action处理完毕之后,直接就将tpl编译,没有设置编码的机会。则编码格式就会设置成为默认的 ISO-8859-1。

以这种格式进行编码,中文信息直接就丢失了。也就是说,无论你用何种格式来保存tpl文件,将tpl文件编译成servlet之后,write出来的数据其实都是ISO-8859-1格式的。

总结一下我们的猜想: 由于JSP能通过page标签指定编译时的编码,所以编译出来的Servlet可以是UTF-8格式的,显示中文没问题。 由于Smarty4J没法指定一些编译选项(至少我不知道咋办,作者也没细说),所以编译出来的Servlet是默认的ISO-8859-1,不能显示中文。

基于上面的猜想,我们可以做一个实验来验证一下我们的猜想是否正确。

我们先回忆一下Struts的运行机理,和JSP差不多,Struts也是将自己编译成为一个Servlet然后执行。编译的时候可以通过Servlet API调整自己的编译选项。 所以我们可以在execute中加入这样一行,看看编译出来Servlet的字符集到底是什么格式的:

System.out.println("Encoding:"+ServletActionContext.getResponse().getCharacterEncoding());  

然后我们创建一个JSP文件,里面的内容1,声明charset="UTF-8"。2,无条件Forward到我们的测试Action。 这样我们就有两种方法访问到Action。一种是通过JSP,一种是通过/GetPages/GetIndexPageAction来访问。

然后我们分别执行,看看输出结果,果然不出所料,两次输出的结果不同,一个是ISO-8859-3,另一个是UTF-8。

所以说我们的猜想是正确的。

那么该如何修改呢,其实非常简单。既然上面能获取到编码,我们就能在编译成Servlet之前设置编码。

ServletActionContext.getResponse().setCharacterEncoding("UTF-8");  

然后就行了。

但是这样每个Action都需要添加这样一行。。。是不是可以通过修改Tomcat的默认编码来达到同样的效果呢?大家自己去尝试吧……

Friskit

继续阅读此作者的更多文章