在之前学习了Servlet中的主体结构,包括Servlet的生命周期方法,和非生命周期方法能获取的一些非常重要的对象如ServletConfig、ServletContext对象等,而从这篇开始我们将开始学习请求对象HttpServletResponse对象和响应对象HttpServletRequest对象。

  在本篇中先从响应对象HttpServletResponse对象开始说起,主要讲诉请求对象HttpServletResponse对象获取IO流向客户端传输数据会碰到的中文乱码问题。

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

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

  

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

  

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

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

 public class ServletDemo1 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方法无效。

  

  使用响应对象可以向客户端写入数据,正如《Servlet的学习(一)》中的例子,我们采用获取响应对象的输出流,将数据用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”这个响应头中设置编码方式,关于这个这个响应头请看之前的博客《HTTP协议的请求和响应学习》。同时,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的<meta>标签,该标签的作用就是模拟一个响应头,这样在回传的响应对象中,某些响应头就不会被设置,但是还是有这个响应头的功能,例如我们在HTML页面中经常能见到的<meta http-equiv="content-type" content="text/html;charset=utf-8">这个标签,是不是和第一种方式很像。相关代码为:

     String data = "银魂";
OutputStream out = response.getOutputStream();
out.write("<meta http-equiv='content-type' content='text/html;charset=utf-8'>".getBytes());
out.write(data.getBytes("UTF-8"));

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

  

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

  

从上面可以看出,服务器发回的响应中没有“Content-type”这个响应头,但是在响应数据实体中有<meta>标签,浏览器能解析这个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 ServletResponse2 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”响应头,或者如之前所说的写入<meta>标签来模拟“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中根据获取的字节流或者字符流向客户端输出数据时会碰到的中文乱码问题做出了分析和解决。关于响应对象和请求对象能实现的更多方法和功能请在之后的篇章中说明。

参考博客:

  javaweb学习总结(七)——HttpServletResponse对象(一)

  PrintWriter与ServletOutputStream的区别

  

Servlet的学习之Response响应对象(1)的更多相关文章

  1. Servlet的学习之Response响应对象(2)

    本篇接上一篇<Servlet的学习之Response响应对象(1)>,继续从HttpServletResponse响应对象来介绍其方法和功能. 使用setHeader方法结合HTTP协议的 ...

  2. Servlet的学习之Response响应对象(3)

    本篇来说明响应对象HttpServletResponse对象的最后一点内容. 首先来看响应对象控制浏览器定时刷新,在我的web应用[myservlet]中创建Servlet,在该Servlet中设置响 ...

  3. Servlet的学习之Request请求对象(3)

    本篇接上一篇,将Servlet中的HttpServletRequest对象获取RequestDispatcher对象后能进行的[转发]forward功能和[包含]include功能介绍完. 首先来看R ...

  4. Servlet的学习之Request请求对象(2)

    在上一篇<Servlet的学习(十)>中介绍了HttpServletRequest请求对象的一些常用方法,而从这篇起开始介绍和学习HttpServletRequest的常用功能. 使用Ht ...

  5. Django视图函数之request请求与response响应对象

    官方文档: https://docs.djangoproject.com/en/1.11/ref/request-response/ 视图中的request请求对象: 当请求页面时,Django创建一 ...

  6. Servlet的学习之Request请求对象(1)

    在本篇中开始对Servlet中的HttpServletRequest请求对象进行学习,请求对象同响应对象一样,我们可以根据该对象中的方法获取例如请求行,请求头和请求实体数据的方法. 在本篇中先对Htt ...

  7. flask基础之Response响应对象(九)

    前言 Response对象负责对客户端的响应,每一个请求都会有一个Response对象,那么它在一个请求的声明周期内是怎么发挥作用的呢? Response对象 响应发生的位置 先回顾一下http请求的 ...

  8. Response响应对象

    1.HttpServletResponse HttpServletResponse是一个定义在Servlet API中的接口,继承自ServletReponse接口,用于封装HTTP响应消息.HTTP ...

  9. django 获取request请求对象及response响应对象中的各种属性值

    django request对象和HttpResponse对象 HttpRequest对象(除非特殊说明,所有属性都是只读,session属性是个例外) HttpRequest.scheme 请求方案 ...

随机推荐

  1. BZOJ 1004: [HNOI2008]Cards( 置换群 + burnside引理 + 背包dp + 乘法逆元 )

    题意保证了是一个置换群. 根据burnside引理, 答案为Σc(f) / (M+1). c(f)表示置换f的不动点数, 而题目限制了颜色的数量, 所以还得满足题目, 用背包dp来计算.dp(x,i, ...

  2. Delphi资源大全

    A curated list of awesome Delphi frameworks, libraries, resources, and shiny things. Inspired by awe ...

  3. include与jsp:include区别

    jsp 中include有两种形式,分别是 <%@ include file=” ”%> <jsp:include page=” ” flush=”true”/> 前者是指令元 ...

  4. MVC整个样例的源代码

    以下就是整MVC样例的源代码: #windows应用程序 #开发者:蔡军生(QQ:9073204) 深圳 2014-9-24 #使用类来描写叙述 from ctypes import * from c ...

  5. ASP.NET过滤器的应用

    在J2EE Web开发中有过滤器filter,该filter可以对指定的URL访问进行拦截,并执行过滤器的方法,根据实际应用情况,在过滤器中修改请求的代码.判断会话信息,也可以做权限控制,总之这个过滤 ...

  6. 原生app与web app的比较

    http://www.2ee9.com/news/news_show_36_237.html http://zhidao.baidu.com/link?url=7lWq2tgqiMiDmsRd54hO ...

  7. ArrayList集合-[长度问题]--C#

    list.Count//获取集合中实际元素的个数.list.Capacity//获取集合中可包含的元素数. /** *   每次集合中实际包含元素的个数(Count)超过了可以包含的元素的个数(Cap ...

  8. Winform TabControl控件使用

    运行效果: 代码: /// <summary> /// 添加选项卡 /// </summary> /// <param name="sender"&g ...

  9. Jsp分页实例---真分页

    网页的分页功能的实现比较简单,实现方法也多种多样. 今天总结一个简单的Jsp真分页实例. 首先,提到分页就要先明确一个概念,何为真分页何谓假分页. 假分页:一次性从数据库读出表的所有数据一次性的返回给 ...

  10. Java--格式化输出

    Java的格式化输出等同于String.Format,与C有很大的相似,比如 System.out.printf("%8.2f", x); 在printf中,可以使用多个参数,例如 ...