5章 response内置对象

 

  Response从字面意思上就可以看出是进行服务器回应时的各种操作,目前主要有用于设置http头的输出信息和请求资源输出二类。

 

 

5.1 重定向客户端

在网页中可以利用超级链接引导客户至另一个页面,但是必须要在客户端上操作,可是有时也希望在服务器端就能自动引导(重定向)客户端至另一个页面,例如进行网上考试时,如考试时间已到则应自动引导客户端至结束界面。response对象中的sendRedirect方法就可以起到这个作用。

 

public void sendRedirect(String url) throws IOException

重定向客户端至指定的urlurl可以是相对地址也可以是绝对地址。本方法必须在开启缓存的情况下才能使用。本方法后面必须紧跟return语句,否则会报错。

参数:url需要重定向的地址,可以是相对地址也可以是绝对地址。

异常:如果输出重定向信息中遇到问题则此方法会抛出IOException异常。

 

  看一个列子redirect.dqm,源程序如下:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

<%@page buffer="true" import="java.util.Random"%>

<%

String yahoo = "http://www.yahoo.com";

String sun = "http://www.sun.com";

 

Random rdom = new Random();

int rs = rdom.nextInt(10);

if (rs >=5) {

  response.sendRedirect(yahoo);return;

}

else {

  response.sendRedirect(sun);return;

}

%>

 

  执行的结果是有可能打开yahoo的主页也有可能打开sun的主页,因为使用了伪随机函数随机重定向到这二个网站,当伪随机数大于等于5则重定向到yahoo主页(源代码810行),否则重定向到sun主页(源代码1113行)。

 

因为sendRedirect方法后面必须紧跟return语句,所以此方法有可能必须在判断语句内,因为有可能会提示return语句后无法达到。如以下代码就会报错,因为第2行永远没有机会执行到。

1

2

response.sendRedirect(yahoo);return;

out.print("hello");

 

请改为如下代码:

1

2

3

4

f (true) {

response.sendRedirect(yahoo);return;

}

out.print("hello");

 

编译器会检查动态文件中response.sendRedirect方法后面有没有return,但是无法检测bean或类方法调用response.sendRedirect方法后有无return,所以在这种情况下也请始终牢记要加上return,如下面的例子:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

<%@page buffer="true" import="java.util.Random"%>

<%

String yahoo = "http://www.yahoo.com";

String sun = "http://www.sun.com";

 

Random rdom = new Random();

int rs = rdom.nextInt(10);

if (rs >=5) {

  myRedirect(response, yahoo);return;

}

else {

  myRedirect(response, sun);return;

}%><%!

private void myRedirect(DResponseItface response, String url) throws IOException {

  response.sendRedirect(url);

}

%>

 

因为sendRedirect方法写到了myRedirect的类方法中(源程序1317行),所以即使710行的return部分不写编译器也不会报错,但是请始终牢记调用了sendRedirect方法后要加上return

 

 

5.2 设置http

http协议1.01.1都规定了http传输过程中包括http头和http身体二部分,http头是对此次传输的一些定义,而http身体则是传输的数据。用户可以通过增加http头来设置本次传输的一些参数从而起到一些特殊效果。

 

1. public void setHeader(String tag, String valueString charsetName) throws IOException

用于对http头按指定的字符集进行设置,http头通常由功能标签和此标签的值构成。本方法必须在开启缓存的情况下才能使用。

参数:tag表示所要添加http头的功能标签。

   value表示添加http头功能标签的对应值。

   charsetName指定http头的字符集,比如BIG-5UTF-8GBK等。

异常:如果输出中遇到问题则此方法会抛出IOException异常。

 

2. public void setHeader(String tag, String value) throws IOException

用于对http头按默认的字符集进行设置,http头通常由功能标签和此标签的值构成。默认的字符集是指webconfig.xmlsystemSetdataEnc所制定的值(参见2.1节)。本方法必须在开启缓存的情况下才能使用。

参数:tag表示所要添加http头的功能标签。

   value表示添加http头功能标签对应的值。

异常:如果输出中遇到问题则此方法会抛出IOException异常。

 

  那么此方法具体有什么作用呢?我们举个例子,比如当用户访问一个网页时我们不想让用户直接在浏览器中查看结果,而是想让用户下载,于是我们可以通过设置http头完成。

httphead.dqm的源代码如下:

1

2

3

4

5

6

<%@page buffer="true"%>

<%response.setHeader("Content-Disposition", "attachment;filename=http压缩.txt");%>

 

什么是HTTP压缩?

 

HTTP压缩(或叫HTTP内容编码)作为一种网站和网页相关的标准,存在已久了,只是最近几年才引起大家的注意。HTTP压缩的基本概念就是采用标准的gzip压缩或者deflate编码方法,来处理HTTP响应,在网页内容发送到网络上之前对源数据进行压缩。有趣的是,在版本4IENetScape中就早已支持这个技术,但是很少有网站真正使用它。Port80软件公司做的一项调查显示,财富1000强中少于5%的企业网站在服务器端采用了HTTP压缩技术。不过,在具有领导地位的网站,如GoogleAmazon、和Yahoo!等,HTTP内容编码技术却是普遍被使用的。考虑到这种技术会给大型的网站们带来带宽上的极大节省,用于突破传统的系统管理员都会积极探索并家以使用HTTP压缩技术。

 

  当访问此动态文件时浏览器会提示下载“http压缩.txt”这个文件,而不是直接在浏览器中显示内容,可以试着将第2setHeader方法去掉再访问,就会看到内容是显示在浏览器上了。可以看出为了实现此功能用到了http头标签“Content-Disposition”,其值为“attachment;filename=http压缩.txt”,filename后面所制定的就是下载时的文件名。

  如果http头这样设置response.setHeader("Content-Disposition", "inline;filename=http压缩.txt"),则不会提示用户“打开”或“保存”,而是浏览器尝试直接打开。

F2.3节中将更详细介绍如何国际化输出http头。

 

 

5.3 设置ContentType

在前面3.2节中介绍过page指令中contenttype可以指定MIME类型,response对象中也有方法可以修改此值。

 

public void setContentType(String contentType)

设置响应结果的MIME类型。当多次出现此方法则以最后一次设定的值为准。本方法必须在开启缓存的情况下才能使用。

参数:contentType表示指定的MIME类型。

 

  注意:此方法与page指令的contenttype项最大不同在于本方法可以使用String变量(即可以条件性指定),而在指令中是不能使用变量的。同时本方法需要开启缓存才能使用,在未开启的情况下则只能使用指令设置。

 

 

5.4 设置encodeURL方法

因为session机制(session请参见第8章)是由cookie机制实现的,所以如果浏览器关闭了cookiesession也就失效了,而encodeURL方法就是为了让session机制即使在浏览器关闭cookie的情况下也能正常使用。实现的 原理是如果浏览器不支持cookieDQM容器可以重写客户请求的URL,把session信息添加到URL信息中去。

 

public String encodeURL(String url)

如果客户端关闭了cookie则将指定的URL重写,使其包含session信息后返回。

参数:url需要重写的地址。

返回:如果浏览器启用cookie则原封不动返回指定的url,否则返回包含session信息的url

 

  该方法首先会判断当前动态文件是否启用了session(如何关闭或启用session参见3.2节),如果没有启用那么直接返回指定的url

  如果开启了session则需要判断浏览器是否支持cookie,如果支持则直接返回url,如果不支持cookie则在url中加入sessionID信息,然后返回修改的url

  如果要在浏览器不支持cookie情况下也可以正常使用session,则所有链接都必须经过encodeURL,如:

  修改前:

    <a herf="yourfile.dqm">

  修改后:

    <a herf="<%=response.encodeURL("yourfile.dqm")%>">

  当浏览器不支持cookie时,url会自动修改为“yourfile.dqm? DQMSESSIONID=XXX”,XXX代表随机的sessionID

 

注意:因为当cookie不可用时,DQM容器会自动为链接url加上sessionID,而sessionID的参数名叫“DQMSESSIONID”,这个名字是系统保留的,所以请不要在动态文件程序中使用此名来进行url数据传送。

 

 

5.5 使用cookie在客户端保存信息

  cookie是可以在客户端长期保存信息的一种技术,服务器端可以将一些信息写入客户端浏览器,等客户端下次再访问时服务器端可以再次读取这些数据。利用cookie可以做一些特殊的功能,比如当用户第一次访问时可以询问访问者姓名,然后将姓名保存在用户的浏览器上,等下次此用户再次访问就可以从浏览器中读取访问者的名字,用于问好。

 

1.       public void addCookie(DCookie cookie)

向客户端添加一个新的cookie

参数:需要写入至客户端的cookie

 

  具体的使用方法我们将在第7章介绍。

 

 

 

5.6 静态插入文件

  在前面3.2节中介绍过include指令,这里我们还要介绍includeFile的方法,不过这二种插入文件的方法还是有较大区别的。采用include指令插入的文件是作为动态文件一部分,所以如果含有java程序片则会执行,此方法始终会检查插入文件是否变化,如变化则重新编译。而includeFile方法不会因为插入文件的变化而重新编译,此方法只是将插入文件中的内容原封不动的输出至客户端。

 

1.       public void includeFile(String fileName) throws java.io.IOException

插入文件,此文件内容会输出至客户端。

参数:fileName表示所要插入的文件,可以是绝对或相对路径。相对路径如:“../xxx.txt”、“test/xxx.txt”等,绝对路径如:“c:\\xxx.txt”、“/xxx.txt”等。不过注意如果要使用‘\’字符必须使用转义的‘\\’取代。同时如果在windows下以‘/’或‘\\’开头则表示从当前动态文件所在盘的根目录开始,而在linuxunix下则表示从根目录开始。

异常:如果向客户端输出插入文件的内容中遇到问题则抛出异常。

 

看一个例子,动态文件includeFile.dqm演示了如何插入与其同一个目录下的includeFile.txt文件。源程序如下:

1

2

3

4

5

<pre>

以下是includeFile.txt文件中的内容-----------------------------

<%response.includeFile("includeFile.txt");%>

以上是includeFile.txt文件中的内容-----------------------------

</pre>

 

输出的结果如下:

以下是includeFile.txt文件中的内容-----------------------------

八月二日晚上九点钟——世界历史上最可怕的八月。人们也许已经想到,上帝的诅咒使得这个堕落的世界显得沉闷无聊,因为在闷热的空气中,有一种令人可怕的静寂和渺茫期待的感觉。太阳早已落山,但是仍留有一道血红色的斑痕,象裂开的伤口低挂在遥远的西边天际。上空星光烁烁,下面,船只上的光亮在海湾里闪耀。两位著名的德国人伫立在花园人行道的石栏旁边。他们身后是一长排低矮沉闷的人字形房屋。

他们往下眺望着白垩巨崖脚下的那一大片海滩。冯.波克本人曾象一只到处游荡的山鹰,四年前就在这处悬崖上栖息下来。他们紧挨着站在那里在低声密谈。从下面望去,那两个发出红光的烟头就像是恶魔的两只眼睛,在黑暗中窥视,在黑暗中冒着烟。

以上是includeFile.txt文件中的内容-----------------------------

 

  可以看到动态文件的第3行将includeFile.txt中的内容原封不动的输出。

 

注意:从上面例子可以看出直接写相对地址<%response.includeFile("includeFile.txt");%>,所指定的地址是相对于当前动态文件所在的目录。

 

 

5.7 有条件的文件输出

  BBSweb程序中经常会遇到这样一种情况,允许用户自行上传文件,而上传后的文件必须满足某些条件才能够被访问(读取、下载),这里所指的某些条件例如只有登录用户才能够访问,或者购买了BBS主题后才能访问等等。类似情况在其它web应用中也有很多,那该如何实现这种功能呢?当然我们可以利用4.24.5节所介绍的方法自己编写文件输出的程序,可是这样会很麻烦,如果要支持断点续传的需要那么自己还要写上一大段程序,要完全符合HTTP协议进行文件输出那么就更加困难了,因为还要去了解HTTP协议。那么有什么更好的方法吗?当然有,就是我们接下来要介绍的方法。

 

1.       public void outBinFile(File out_bin_file) throws IOException

向客户端输出指定的静态文件。本方法必须在开启缓存的情况下才能使用。本方法后面必须紧跟return语句,否则会报错。

参数:out_bin_file需要向客户端输出的静态文件。

异常:如果输出静态文件中遇到问题则此方法会抛出IOException异常。

 

  首先我们来看一个例子,outputfile.dqm是一个输出文件的页面,当请求时附带type=download参数则可以下载文件,而其它情况就会提示无法下载,其源代码如下:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

<%@ page buffer="true"%>

 

<html>

 

<head>

<meta http-equiv="Content-Language" content="zh-cn">

<meta http-equiv="Content-Type" content="text/html; charset=gb2312">

<title>Download Page</title>

</head>

 

<body>

 

<%

String type = request.getParameter("type");

 

if ("download".equals(type)) { //下载

  File downFile = new File("c:\\kgweb.rar");

  response.outBinFile(downFile);

  return;

}

%>

 

<p align="center"><b><font color="#FF0000">对不起,无法下载!</font></b></p>

 

</body>

 

</html>

 

从源程序第16行可以看到,当请求时type参数值为download时就向客户端输出在17行所指定的文件。这里我们指定了输出本机C盘根目录下的kgweb.rar,读者在测试程序时请修改此处用于指定你所要输出的文件。

 

注意:如果在上面17行指定的文件为相对地址File downFile = new File("kgweb.rar"),则所指定的文件位置是相对于kangaroo-egg安装的根目录(参见1.2节)。同时在正式使用中请先判断所要输出的文件(这里就是downFile)是否存在,否则服务器后台会报错,而前台用户将下载到空文件。

 

当我们通过上述参数访问时,就会看到客户端提示用户下载文件,参见图5-7-1

5-7-1

 

从上图中IE地址栏中可以看到是附带了type=download这个参数的,因为如此所以才会提示用户下载文件,不过提示下载的文件名竟然是我们访问的outputfile.dqm,先不要急稍后我们会介绍如何改变它。此外我们还可以看到提示下载的文件大小是10.9M,因为我们在17行所指定输出的c:\\kgweb.rar文件大小是10.9M

你可以用上面附带type参数的url进行断点续传测试,结果绝对不会让你失望。同时不光是断点续传的功能,所有HTTP协议规范的传输功能都会完整支持。如客户端请求条件中含有if-modified-since,则就会根据条件符合情况输出文件还是提示HTTP304信息,这对于类似有条件显示用户上传图片的情况十分有利,因为浏览器第一次访问时通常会将图片放入本地缓存,再次访问时会发送if-modified-since,如果图片没有变化则服务器直接返回304信息,浏览器就从本地缓存中读取图片,这样就节约了网络资源。另外使用outBinFile方法进行文件输出效率也很高。

那如果不附带参数进行访问呢?就像源程序23行所写的,输出无法下载的提示,如图5-7-2

5-7-2

 

参照上面的例子我们可以实现根据不同的条件进行文件输出,例如最常用的就是根据session的状态进行文件输出。

 

接下来我们就是要解决前面提到的一个问题,如何指定输出文件时的下载名,outBinFile还有二个重载方法就是为了解决这个问题。

 

2.       public void outBinFile(File out_bin_file, String saveAsNameStr) throws IOException

向客户端输出指定的静态文件,同时按默认的字符集重新指定输出文件名。默认的字符集是指webconfig.xmlsystemSetdataEnc所制定的值(参见2.1节)。本方法必须在开启缓存的情况下才能使用。本方法后面必须紧跟return语句,否则会报错。

参数:out_bin_file需要向客户端输出的静态文件。

      saveAsNameStr表示按默认字符集重新指定输出文件名。

异常:如果输出静态文件中遇到问题则此方法会抛出IOException异常。

 

 

3.       public void outBinFile(File out_bin_file, String saveAsNameStr, String charsetName) throws IOException

向客户端输出指定的静态文件,同时按指定的字符集重新指定输出文件名。本方法必须在开启缓存的情况下才能使用。本方法后面必须紧跟return语句,否则会报错。

参数:out_bin_file需要向客户端输出的静态文件。

      saveAsNameStr表示按指定的字符集重新指定输出文件名。

      charsetName指定的字符集,比如BIG-5UTF-8GBK等。

异常:如果输出静态文件中遇到问题则此方法会抛出IOException异常。

 

上面第2个方法的原理请参见5.2节介绍的第2个方法。上面第3个方法的原理请参见5.2节介绍的第1个方法。

 

  于是我们将outputfile.dqm修改一下,源代码如下:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

<%@ page buffer="true"%>

 

<html>

 

<head>

<meta http-equiv="Content-Language" content="zh-cn">

<meta http-equiv="Content-Type" content="text/html; charset=gb2312">

<title>Download Page</title>

</head>

 

<body>

 

<%

String type = request.getParameter("type");

 

if ("download".equals(type)) { //下载

  File downFile = new File("c:\\kgweb.rar");

  response.outBinFile(downFile, downFile.getName(), "GBK");

  return;

}

%>

 

<p align="center"><b><font color="#FF0000">对不起,无法下载!</font></b></p>

 

</body>

 

</html>

 

再次带参数访问就会看到保存时的文件名改变了,如图5-7-3

5-7-3

 

因为outBinFile方法后面必须紧跟return语句,所以此方法有可能必须在判断语句内,因为有可能会提示return语句后无法达到。如以下代码就会报错,因为第2行永远没有机会执行到。

1

2

response.outBinFile(new File("c:\\kgweb.rar"));return;

out.print("hello");

 

请改为如下代码:

1

2

3

4

f (true) {

response.outBinFile(new File("c:\\kgweb.rar"));return;

}

out.print("hello");

 

编译器会检查动态文件中response.outBinFile方法后面有没有return,但是无法检测bean或类方法调用response.outBinFile方法后有无return,所以在这种情况下也请始终牢记要加上return,如下面的例子:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

<%@ page buffer="true"%>

 

<html>

 

<head>

<meta http-equiv="Content-Language" content="zh-cn">

<meta http-equiv="Content-Type" content="text/html; charset=gb2312">

<title>Download Page</title>

</head>

 

<body>

 

<%

String type = request.getParameter("type");

 

if ("download".equals(type)) { //下载

  File downFile = new File("c:\\kgweb.rar");

  myOutBinFile(response, downFile);return;

}

%>

 

<p align="center"><b><font color="#FF0000">对不起,无法下载!</font></b></p>

 

</body>

 

</html>

<%!

private void myOutBinFile(DResponseItface response, File outFile) throws IOException {

  response.outBinFile(outFile);

}

%>

 

因为outBinFile方法写到了myOutBinFile的类方法中(源程序2731行),所以即使18行的return部分不写编译器也不会报错,但是请始终牢记调用了myOutBinFile方法后要加上return