热门标签 | HotTags
当前位置:  开发笔记 > 前端 > 正文

HttpServletResponse乱码问题_动力节点Java学院整理

这篇文章主要介绍了HttpServletResponse乱码问题,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧

一个完整的http响应包括响应行,若干响应头和响应数据主体三部分构成。如果我们能用响应对象来进行这三部分的处理,就能向客户发送特定的响应数据包。

先从HttpServletResponse对象的方法中可以看到有如下方法(部分):

这只是一部分,但是我们却可以看出,通过响应对象的方法,我们就能设置响应客户端数据的一些信息。比如setStatus(int sc)方法,我们从HttpServletResponse的API中的字段定义可找到已经设置好的响应码(部分):

我们通过setHeader或者addHeader就能对一些数据进行跟客户端的告知,比如我想让某个页面的数据在客户端保存一天,也就是如果客户端再向我请求的话,则它应该去缓存中获取,直到一天之后才能重新向我请求,那么我就必须使用到了“Expires”响应头,将这个响应头的值设为一天后的时间告诉给客户端:

在MyEclipse中的【myservlet】web工程下,创建名为ServletDemo1的Servlet,代码如下:

 

public class ServletDemo extends HttpServlet {
   public void doGet(HttpServletRequest request, HttpServletResponse response)
       throws ServletException, IOException {
 
     long expiresTime = System.currentTimeMillis()+1*24*60*60*1000; //将缓存截止时间设置为一天后
     response.setDateHeader("expires", expiresTime);
     
   }
 } 

这时候用浏览器来访问这个Servlet(访问之前最好现将浏览器中的缓冲清除干净),访问之后我们再来看看这个缓存的页面文件:

右键查看其属性:

可以看到我们是在9月28日访问的这个文件,而服务器已经将这个文件的缓冲时间设置为了29日。

注意:对于”Expires”响应头的设置必须使用setDateHeader方法,使用setHeader方法无效。

使用响应对象可以向客户端写入数据,我们采用获取响应对象的输出流,将数据用write方法写,这些数据是写入到响应对象中,而服务器再将这些响应对象回传给客户端进行解析。而这些数据在响应对象中正是处于HTTP协议的响应数据实体中。

通过HttpServletResponse对象的父类ServletResponse对象的getOutputStream方法和getWriter方法可以获取向响应对象的数据实体中写入数据。这里如果传送的是英文数据一般都没有什么问题,而中文编码则是会令人头疼的。

首先我们来看响应对象的getOutputStream方法,我们在一个Servlet程序中代码如下:

   String data = "银魂";
   OutputStream out = response.getOutputStream();
   out.write(data.getBytes());

而在浏览器中访问这个Servlet,看到的是:

是的,没有出现乱码的问题,这主要有两点原因:

一个是浏览器本身的解码方式是根据平台语言环境来设置的,我的操作系统是Windows的中文版本,因此浏览器的解码采用的编码表为“GB2312”:

另一个原因是因为,在将字符串转为字节数组时,采用了getBytes()方法,这个方法在String类的API文档中明确说明了采用平台默认的字符集,根据我的系统这个方法也采用了“GB2312”编码表。

因此服务器编码和客户端解码都采用同一编码表这就不会出现中文乱码问题。

如果我在getBytes方法中采用UTF-8编码,那么结果自然会出错:

   String data = "银魂";
   OutputStream out = response.getOutputStream();
   out.write(data.getBytes("UTF-8"));

除非你也在浏览器中更改编码方式,改成UTF-8就可以重新看到正确的中文数据了:

当然这肯定不适合给用户这么操作,毕竟不是谁都懂浏览器的编码。

如果我们一定要将中文数据采用“UTF-8”的方式(UTF-8有利于国际化),有这么两种解决采用UTF-8编码方式的中文乱码问题:

第一种解决方式:使用HttpServletResponse响应对象的setHeader的方法,将“Content-type”这个响应头中设置编码方式。同时,sun公司也提供了更便捷的代码语句setContentType给编程人员使用。

在Servlet中的代码:

   response.setHeader("content-type", "text/html;charset=UTF-8");
   //response.setContentType("text/html;charset=UTF-8");  //这句功能同上一句
   String data = "银魂";
   OutputStream out = response.getOutputStream();
   out.write(data.getBytes("UTF-8"));

这样在浏览器中可以看到正确的中文数据,并且浏览器自动将编码方式采用UTF-8:

附带从HttpWatch中观察到的数据包:

注意:如果response.setHeader("content-type", "text/html;charset=UTF-8");中将"text/html;charset=UTF-8"中的分号“;”写成了逗号“,”就会变成下载该Servlet文件。

所以书写要注意。

第二种解决方式:我们不直接在响应对象中设置“Content-type”这个响应头,而是通过HTML的标签,该标签的作用就是模拟一个响应头,这样在回传的响应对象中,某些响应头就不会被设置,但是还是有这个响应头的功能,例如我们在HTML页面中经常能见到的这个标签,是不是和第一种方式很像。相关代码为:

   String data = "银魂";
   OutputStream out = response.getOutputStream();
   out.write("".getBytes());
   out.write(data.getBytes("UTF-8"));

这时候在浏览器中同样能观察到正确的中文数据,同时可以看到浏览器已经自动采用“UTF-8”编码方式:

同时,在浏览器浏览源代码和观察HttpWatch窗口:

从上面可以看出,服务器发回的响应中没有“Content-type”这个响应头,但是在响应数据实体中有标签,浏览器能解析这个HTML语言,得到这个标签中设置的“Content-type”模拟响应头,因此能根据这个模拟响应头中的编码方式来设置浏览器应该采用的码表。

如果我们用输出流直接输出数字的话,会是输出这个数字在编码表中代表的字符,如代码为:

 public void doGet(HttpServletRequest request, HttpServletResponse response)
       throws ServletException, IOException {
     
    response.getOutputStream().write(97);
   }  

在浏览器中得到:a。

说完了从响应对象中得到OutputSteam对象,接着我们来讨论从响应对象中得到Writer对象,众所周知,字符流是由字节流加编码表组成,那么在响应对象中的字符流采用什么编码表方式呢?我们来看看HttpServletResponse对象的getWriter()方法的API手册说明:

从这里面看出如果没有为这个getWriter()方法设置编码表,那么则默认采用 “ISO-8859-1”编码表。或者采用响应对象的getCharacterEncoding()方法查看也可以。

那么在服务器端如果要改变对封装数据的编码格式可以有两种方式:

第一种:使用响应对象的setCharacterEncoding()方法来设置服务器采用的编码表,接着使用setContendType或者setHeader告知客户端服务器采用的编码表,后者在上面已经说过。

示例代码:

 public class ServletResponse extends HttpServlet {
 
   public void doGet(HttpServletRequest request, HttpServletResponse response)
       throws ServletException, IOException {
     
     response.setCharacterEncoding("UTF-8");
     response.setContentType("text/html;charset=UTF-8");
     PrintWriter writer = response.getWriter();
     String data = "银魂";
     writer.write(data);  
   }
 }

注意,光只有setCharacterEncoding()方法只能改变服务器端采用的编码表,而没能通知客户端,所以需要setContentType设置“Content-type”响应头,或者如之前所说的写入标签来模拟“Content-type”响应头。

第二种:直接使用setContentType方法,通过这种方法,可以在服务器和客户端同时设置编码表,也就是第一种方式中两个方法的结合,因此上述示例的代码如下:

 public void doGet(HttpServletRequest request, HttpServletResponse response)
       throws ServletException, IOException {
     response.setContentType("text/html;charset=UTF-8");
     
     PrintWriter writer = response.getWriter();
     String data = "银魂";
     writer.write(data);  
 }
 }

以上分别从响应对象HttpServletResponse中根据获取的字节流或者字符流向客户端输出数据时会碰到的中文乱码问题做出了分析和解决。


推荐阅读
  • yikesnews第11期:微软Office两个0day和一个提权0day
    点击阅读原文可点击链接根据法国大选被黑客干扰,发送了带漏洞的文档Trumps_Attack_on_Syria_English.docx而此漏洞与ESET&FireEy ... [详细]
  • Windows 7 64位系统下Redis的安装与PHP Redis扩展配置
    本文详细介绍了在Windows 7 64位操作系统中安装Redis以及配置PHP Redis扩展的方法,包括下载、安装和基本使用步骤。适合对Redis和PHP集成感兴趣的开发人员参考。 ... [详细]
  • Java EE CDI:解决依赖关系冲突的实例
    在本教程中,我们将探讨如何在Java EE的CDI(上下文和依赖注入)框架中有效解决依赖关系的冲突问题。通过学习如何使用限定符,您将能够为应用程序的不同客户端提供多种接口实现,并确保每个客户端都能正确调用其所需的实现。 ... [详细]
  • 本文介绍了如何利用 Spring Boot 和 Groovy 构建一个灵活且可扩展的动态计算引擎,以满足钱包应用中类似余额宝功能的推广需求。我们将探讨不同的设计方案,并最终选择最适合的技术栈来实现这一目标。 ... [详细]
  • 本文深入探讨了SQL数据库中常见的面试问题,包括如何获取自增字段的当前值、防止SQL注入的方法、游标的作用与使用、索引的形式及其优缺点,以及事务和存储过程的概念。通过详细的解答和示例,帮助读者更好地理解和应对这些技术问题。 ... [详细]
  • 实用正则表达式有哪些
    小编给大家分享一下实用正则表达式有哪些,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下 ... [详细]
  • 嵌入式开发环境搭建与文件传输指南
    本文详细介绍了如何为嵌入式应用开发搭建必要的软硬件环境,并提供了通过串口和网线两种方式将文件传输到开发板的具体步骤。适合Linux开发初学者参考。 ... [详细]
  • 探索新一代API文档工具,告别Swagger的繁琐
    对于后端开发者而言,编写和维护API文档既繁琐又不可或缺。本文将介绍一款全新的API文档工具,帮助团队更高效地协作,简化API文档生成流程。 ... [详细]
  • 深入解析Serverless架构模式
    本文将详细介绍Serverless架构模式的核心概念、工作原理及其优势。通过对比传统架构,探讨Serverless如何简化应用开发与运维流程,并介绍当前主流的Serverless平台。 ... [详细]
  • 本文探讨了如何解决PHP文件无法写入本地文件的问题,并解释了PHP文件中HTML代码无效的原因,提供了一系列实用的解决方案和最佳实践。 ... [详细]
  • HTML5 表单新增属性详解
    本文深入探讨了HTML5中表单的新增属性,帮助读者全面掌握这些新特性。内容涵盖autocomplete、autofocus、list等常用属性,并详细解释了form、novalidate、enctype和accept-charset等高级属性的功能与应用场景。 ... [详细]
  • 本文探讨了为何相同的HTTP请求在两台不同操作系统(Windows与Ubuntu)的机器上会分别返回200 OK和429 Too Many Requests的状态码。我们将分析代码、环境差异及可能的影响因素。 ... [详细]
  • 本文详细介绍了一种通过MySQL弱口令漏洞在Windows操作系统上获取SYSTEM权限的方法。该方法涉及使用自定义UDF DLL文件来执行任意命令,从而实现对远程服务器的完全控制。 ... [详细]
  • 在尝试使用C# Windows Forms客户端通过SignalR连接到ASP.NET服务器时,遇到了内部服务器错误(500)。本文将详细探讨问题的原因及解决方案。 ... [详细]
  • 本文介绍了如何使用Gradle和gdx-setup.jar工具来创建LibGDX项目,包括详细的步骤和注意事项,适合初学者和有经验的开发者。 ... [详细]
author-avatar
zhanwang
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有