HttpServletResponse的应用

 准备实验环境

  1. 为支持Servlet 2.4和JSP 2.0 ,使用Tomcat 5.5.12作为运行环境。
  2. 在Tomcat 5.5.12的<tomcat的安装目录>\webapps目录中创建一个名为it315的子目录。
  3. 在it315目录中创建一个名为test.html的网页文件,在该文件中写上“<s>中文测试页面</s>”这几个字符。
  4. 在<tomcat的安装目录>\webapps\it315目录中创建一个名为WEB-INF的子目录。
  5. 在保存Servlet源文件的目录中编写compile.bat编译工具

set PATH=C:\jdk1.5.0_01\bin;%path%

set CLASSPATH=C:\tomcat-5.5.12\common\lib\servlet-api.jar;%CLASSPATH%    

javac -d C:\tomcat-5.5.12\webapps\it315\WEB-INF\classes %1

pause

HttpServletResponse的简介

WEB服务器回送给WEB客户端的HTTP响应消息分为三个部分:

ü  状态行

ü  响应消息头

ü  消息正文(也叫实体内容)

Servlet API中定义的ServletResponse接口类用于创建响应消息。

HttpServletResponse是专用于HTTP协议的ServletResponse子接口,它用于封装HTTP响应消息。

HttpServletResponse定义了一系列用于描述各种HTTP状态码的常量。

在service()方法内部调用HttpServletResponse对象的各种方法来创建响应消息。

产生响应状态行

HTTP响应消息的响应状态行包括HTTP版本、状态代码和一条相关的提示信息:

       HTTP/1.1 200 OK

HttpServletResponse中定义了若干与状态码数值对应的常量,每个常量的名称以前缀SC(Status Code的简写)开头,然后是状态码在HTTP 1.1规范中所表示的状态信息的英文单词的组合,每个单词之间用下划线连接,且所有字母都大写。

       状态码404 对应的常量为HttpServletResponse.SC_NOT_FOUND 

setStatus方法用于设置HTTP响应消息的状态码,并生成响应状态行。

sendError方法用于发送表示错误信息的状态码(一般是404,找不到客户机所请求的资源)到客户端,并清除缓冲区中的内容。

(用常量便于记忆,说实在话,英文单词也记不住,但是,用开发工具的提示功能,我们可以挑选出来,这就像指证罪犯时,自己回忆不出罪犯的像貌,但可以从一堆嫌疑人中指出来。)

构建响应消息头

addHeader与setHeader方法

addIntHeader与setIntHeader方法

addDateHeader与setDateHeader方法

setContentLength方法

setContentType方法  (*)

setCharacterEncoding方法   (*)

setLocale方法

<locale-encoding-mapping-list>

       <locale-encoding-mapping>

              <locale>zh_CN</locale>

              <encoding>GB2312</encoding>

       </locale-encoding-mapping>

</locale-encoding-mapping-list>

containsHeader方法

响应消息头的实用案例

Servlet的中文输出问题

让浏览器定时刷新网页

禁止浏览器缓存当前文档内容

使用<meta>标签模拟响应消息头

(printWriter用于把unicode字符串转换成某种编码的字节流,jdk1.4中的PrintWriter的设计缺陷,嘿嘿!故意不加charsert=GB2312,显示的结果立马为?

setContentType方法有两个作用:一个是把字符以什么码输出,iso里就没有汉字,所以,不能以iso输出汉字;一个告诉浏览器,输出的内容是什么码。)

Servlet的中文输出问题à原理

浏览器接收到的中文字符并不是中文符号本身,而是它的某种字符集编码的数据。

浏览器必须使用正确的字符集编码进行查看,才能将它所接收到的数据显示为正确的中文字符。

当Servlet程序仅仅需要输出纯文本格式的响应正文时,通常应调用ServletResponse对象的getWriter方法返回一个PrintWriter对象,然后使用这个PrintWriter对象将文本内容写入到客户端。

Java程序中的字符文本在内存中是以unicode编码的形式存在的,PrintWriter对象在输出字符文本时,需要先将它们转换成其他某种字符集编码的字节数组后输出。

ServletResponse对象的getWriter方法返回的PrintWriter对象默认使用ISO8859-1字符集编码进行Unicode字符串到字节数组的转换,由于ISO8859-1字符集中根本就没有中文字符,Unicode编码的中文字符将被转换成无效的字符编码后输出给客户端。 

Servlet的中文输出问题à解决办法

ServletResponse接口中定义了setCharacterEncoding、setContentType和setLocale等方法来指定ServletResponse.getWriter方法返回的PrintWriter对象所使用的字符集编码。

调用ServletResponse接口中定义的setContentType方法,在HTTP响应消息的Content-Type头字段中指定响应正文的字符集编码。  

getOutputStream与getWriter方法

getOutputStream方法用于返回Servlet引擎创建的字节输出流对象,Servlet程序可以按字节形式输出响应正文。

getWriter方法用于返回Servlet引擎创建的字符输出流对象,Servlet程序可以按字符形式输出响应正文。

getOutputStream和getWriter这两个方法互相排斥,调用了其中的任何一个方法后,就不能再调用另一方法。 

getOutputStream方法返回的字节输出流对象的类型为ServletOutputStream,它可以直接输出字节数组中的二进制数据。

getWriter方法将Servlet引擎的数据缓冲区包装成PrintWriter类型的字符输出流对象后返回,PrintWriter对象可以直接输出字符文本内容。

Servlet程序向ServletOutputStream或PrintWriter对象中写入的数据将被Servlet引擎获取,Servlet引擎将这些数据当作响应消息的正文,然后再与响应状态行和各响应头组合后输出到客户端。

Serlvet的service方法结束后,Servlet引擎将检查getWriter或getOutputStream方法返回的输出流对象是否已经调用过close方法,如果没有,Servlet引擎将调用close方法关闭该输出流对象。   

选择getOutputStream和getWriter方法的要点

PrintWriter对象输出字符文本内容时,它内部还是将字符串转换成了某种字符集编码的字节数组后再进行输出,使用PrintWriter对象的好处就是不用编程人员自己来完成字符串到字节数组的转换。

使用ServletOutputStream对象也能输出内容全为文本字符的网页文档,但是,如果网页文档内容是在Servlet程序内部使用文本字符串动态拼凑和创建出来的,则需要先将字符文本转换成字节数组后输出。

如果一个网页文档内容全部为字符文本,但是这些内容可以直接从一个字节输入流中读取出来,然后再原封不动地输出到客户端,那么就应该使用ServletOutputStream对象直接进行输出,而不要使用PrintWriter对象进行输出。    

输出缓冲区

Servlet程序输出的HTTP消息的响应正文首先被写入到Servlet引擎提供的一个输出缓冲区中,直到输出缓冲区被填满或者Servlet程序已经写入了所有的响应内容,缓冲区中的内容才会被Servlet引擎发送到客户端。

使用输出缓冲区后,Servlet引擎就可以将响应状态行、各响应头和响应正文严格按照HTTP消息的位置顺序进行调整后再输出到客户端。

如果在提交响应到客户端时,输出缓冲区中已经装入了所有的响应内容,Servlet引擎将计算响应正文部分的大小并自动设置Content-Length头字段。

如果在提交响应到客户端时,输出缓冲区中装入的内容只是全部响应内容的一部分, Servlet引擎将使用HTTP 1.1的chunked编码方式(通过设置Transfer-Encoding头字段来指定)传输响应内容。

输出缓冲区à有关方法

setBufferSize方法

getBufferSize方法

flushBuffer方法

reset方法

isCommitted方法

 

如何实现动态文件内容的下载

需要通过HttpServletResponse.setContentType方法设置Content-Type头字段的值为浏览器无法使用某种方式或激活某个程序来处理的MIME类型,例如,“application/octet-stream”或“application/x-msdownload”等。

需要通过HttpServletResponse.setHeader方法设置Content-Disposition头的值为“attachment; filename =文件名”。

应该调用HttpServletResponse.getOutputStream方法返回的ServletOutputStream对象来向客户端写入附件文件内容,而不应使用HttpServletResponse.getWriter方法返回的PrintWriter对象。

(下载文件的名称为中文时,会出现乱码,要将文件名进行utf-8编码再进行URL编码。

在rfc2183中搜索encode,要求查rfc2184

查看urlencoder的帮助,建议用UTF-8编码,和一个连接到w3c的超连接,该超连接指向内容如下:

B.2 Special characters in URI attribute values

B.2.1 Non-ASCII characters in URI attribute values

Although URIs do not contain non-ASCII values (see [URI], section 2.1) authors sometimes specify them in attribute values expecting URIs (i.e., defined with %URI; in the DTD). For instance, the following href value is illegal:

<A href="http://foo.org/Håkon">...</A> We recommend that user agents adopt the following convention for handling non-ASCII characters in such cases:

Represent each character in UTF-8 (see [RFC2279]) as one or more bytes.

Escape these bytes with the URI escaping mechanism (i.e., by converting each byte to %HH, where HH is the hexadecimal notation of the byte value).

This procedure results in a syntactically legal URI (as defined in [RFC1738], section 2.2 or [RFC2141], section 2) that is independent of the character encoding to which the HTML document carrying the URI may have been transcoded.

Note. Some older user agents trivially process URIs in HTML using the bytes of the character encoding in which the document was received. Some older HTML documents rely on this practice and break when transcoded. User agents that want to handle these older documents should, on receiving a URI containing characters outside the legal set, first use the conversion based on UTF-8. Only if the resulting URI does not resolve should they try constructing a URI based on the bytes of the character encoding in which the document was received.

Note. The same conversion based on UTF-8 should be applied to values of the name attribute for the A element.

MIME邮件中的附件名的中文,会出乱码吗?用outlook做个实验,结果是这样的:

Content-Disposition: attachment;

  filename="=?gb2312?B?0MK9qCDOxLG+zsS1tS50eHQ=?=")

图像访问计数器à介绍

网页每次被访问时,页面的访问次数都要发生改变,所以这个功能必须通过服务器端的程序来实现。

一些WEB站点只能输出静态页面内容,没有开放运行服务器端程序的功能,无法直接在这些只支持静态内容的WEB站点上编写服务器端程序来实现页面访问次数的统计和显示功能。 

一些具有执行服务器端程序功能的WEB站点推出了免费的页面访问计数器,只要在位于任何站点的一个静态HTML页面中增加一条该站点提供的HTML语句,该语句就能显示出该静态页面的访问次数。

一个站点要想能统计另外一个站点上的某个HTML页面的访问次数,必须让任何一个浏览器在每次访问那个HTML页面都通知这个一下站点,这可以通过在静态HTML页面中增加两种特殊的标签来实现:<img>标签和设置src属性的<script>标签。   

<img>标签的三个重要特性

一个包含有图像的网页文件中并没有包含真正的图像数据内容,而只是使用<img>标签指明了图像的URL地址。

              举例:本网页已被浏览了<img src= "count.gif ">次

<img>标签的src属性也可以指向当前页面所在WEB服务器之外的其他WEB服务器上的图像文件。

浏览器并不关心<img>标签所需的图像数据在服务器端是如何产生,它只知道去访问src属性指定的URL资源,并把服务器返回的数据当作一个图像的内容来显示。服务器返回的图像数据可以直接从一个静态图像文件中读取,也可以通过Servlet程序在内存中动态创建。

用sendRedirect方法实现请求重定向

sendRedirect方法用于生成302响应码和Location响应头,从而通知客户端去重新访问Location响应头中指定的URL,其完整的定义语法如下:

       public void sendRedirect(java.lang.String location)

              throws java.io.IOException

使用下面两条语句也能完成response.sendRedirect(url)语句所完成的功能:

       response.setStatus(response.SC_MOVED_TEMPORARILY );

       response.setHeader ("Location", url);

sendRedirect 方法不仅可以重定向到当前应用程序中的其他资源,它还可以重定向到同一个站点上的其他应用程序中的资源,甚至是使用绝对URL重定向到其他站点的资源。

如果传递给sendRedirect 方法的相对URL以“/”开头,则是相对于整个WEB站点的根目录,而不是相对于当前WEB应用程序的根目录。

请求重定向与请求转发的比较

RequestDispatcher.forward方法只能将请求转发给同一个WEB应用中的组件;而HttpServletResponse.sendRedirect 方法还可以重定向到同一个站点上的其他应用程序中的资源,甚至是使用绝对URL重定向到其他站点的资源。

如果传递给HttpServletResponse.sendRedirect 方法的相对URL以“/”开头,它是相对于整个WEB站点的根目录;如果创建RequestDispatcher对象时指定的相对URL以“/”开头,它是相对于当前WEB应用程序的根目录。

调用HttpServletResponse.sendRedirect方法重定向的访问过程结束后,浏览器地址栏中显示的URL会发生改变,由初始的URL地址变成重定向的目标URL;调用RequestDispatcher.forward 方法的请求转发过程结束后,浏览器地址栏保持初始的URL地址不变。

HttpServletResponse.sendRedirect方法对浏览器的请求直接作出响应,响应的结果就是告诉浏览器去重新发出对另外一个URL的访问请求;RequestDispatcher.forward方法在服务器端内部将请求转发给另外一个资源,浏览器只知道发出了请求并得到了响应结果,并不知道在服务器程序内部发生了转发行为。

RequestDispatcher.forward方法的调用者与被调用者之间共享相同的request对象和response对象,它们属于同一个访问请求和响应过程;而HttpServletResponse.sendRedirect方法调用者与被调用者使用各自的request对象和response对象,它们属于两个独立的访问请求和响应过程。

无论是RequestDispatcher.forward方法,还是HttpServletResponse.sendRedirect方法,在调用它们之前,都不能有内容已经被实际输出到了客户端。如果缓冲区中已经有了一些内容,这些内容将被从缓冲区中清除。

缺省Servlet的缓存问题

场景:

       在实际项目中,有些HTML文件是在程序运行时调用一个模版文档来动态生成的,例如,一些站点的统计报表就是在用户访问某个Servlet时,由这个Servlet临时调用报表生成模块(例如,ireport)生成一个HTML文件,然后再将请求转发这个HTML文件。

问题:

       如果A部门的一个员工查看了A部门的统计信息后的一个较短时间范围内,B部门的员工来查看B部门的统计信息,结果后者看到的是A部门的统计信息。

缺省Servlet缓存问题的模拟程序

public class ReportServlet extends HttpServlet

{

       private int count = 0;

       public void service(HttpServletRequest request,

              HttpServletResponse response) throws ServletException, IOException

       {

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

             

              //禁止浏览器缓存

              response.setDateHeader("Expires",0);

              response.setHeader("Cache-Control","no-cache");

              response.setHeader("Pragma","no-cache");         

             

              String department = request.getParameter("dept");

              synchronized(this)

              {

                     System.out.println(++count + ":" + department);

                     String path = getServletContext().getRealPath("/WEB-INF/report.html");

                     ReportTool rt = new ReportTool();

                     rt.setCount(count);

                     rt.setDepartment(department);

                     rt.createReportHTML(path);

                     RequestDispatcher rd = getServletContext().getRequestDispatcher(

                                          "/WEB-INF/report.html");

                     rd.forward(request,response);

              }

       }

}

缺省Servlet缓存问题à解决方案

为每一种统计方案都生成一个名称不同的HTML报表文件,例如,不同部门使用不同名称的报表文件。如果统计方案比较多,生成的HTML报表文件的个数也就会很多,所以,这个方案比较适合只有少数几种固定的统计方案的情况,特别不适合要根据各种组合条件来生成报表的情况。

修改Tomcat的缺省Servlet程序,让它不要进行缓存,这种方案可能会严重影响系统的性能,一般不要采用。

在ReportServlet程序中生成HTML报表文件后,按字节流的形式读取该文件中的内容,然后以字节流的形式直接输出到客户端。这种方案可以生成实时的统计报表,也适合根据各种组合条件来生成报表的情况,所以,推荐采用这种方案。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值