HttpServletResponse对象
介绍
Web 服务器收到客户端的 http 请求,会针对每一次请求,分别创建一个用于代表请求的 request 对象和代表响应的 response 对象。
request 和 response 对象代表请求和响应:获取客户端数据,需要通过 request 对象;向客户端 输出数据,需要通过 response 对象。
HttpServletResponse 的主要功能用于服务器对客户端的请求进行响应,将 Web 服务器处理后的结果返回给客户端。service()方法中形参接收的是 HttpServletResponse 接口的实例化对象,这个对象 中封装了向客户端发送数据、发送响应头,发送响应状态码的方法。
常用方法
常用方法 | 描述 |
---|---|
addHeader(String name, String value) | 添加指定的键值到响应头信息中 |
containsHeader(String name) | 判断响应的头部是否被设置 |
encodeURL(String url) | 编码指定的 URL |
sendError(int sc) | 使用指定状态码发送一个错误到服务端 |
setStatus(int sc) | 用来设定回应的状态码 |
sendRedirect() | 设置重定向页面 |
getWriter() | 取得PrintWriter对象,输出字符流 |
getOutputStream() | 获取输出的字节流 |
setContentType(String ContentType) | 设置响应的MIME类型 |
刷新和页面自动跳转
所有头信息都是随着请求和回应自动发送到服务器端(客户端),在 response 中一个比较常用的头信息就是刷新的指令,可以完成定时刷新的功能。
//定时刷新
resp.setHeader("refresh","10");
对于刷新的头信息,除了定时的功能外,还具备了定时跳转的功能,可以让一个页面定时跳转到一个指定的页面。(已经注册成功,两秒后跳转到登陆页面)
response.setHeader("refresh","3;URL=ok.html");
但是这种跳转不是万能的,有时候根本就无法进行跳转操作,返回后刷新不会跳转;对于这种定时跳转的头信息,也可以采用 HTML 的方式进行设置,HTML 本身也可以设置头信息。(客户端跳转)
<meta http-equiv="refresh" content="3;http://www.baidu.com" />
数据响应
接收到客户端请求后,可以通过 HttpServletResponse 对象直接进行响应,响应时需要获取输出流,有两种形式 getWriter() 获取字符流 (只能响应回字符);getOutputStream()获取字节流(能响应
切数据)。响应回的数据到客户端被浏览器解析。 注意:两者不能同时使用。
PrintWriter out = resp.getWriter();
out.write("<h1>Hello World</h1>");
ServletOutputStream out = resp.getOutputStream();
out.write("<h1>Hello World</h1>").getBytes());
乱码解决
我们会发现在上述的响应中,如果我们响应的内容中含有中文,则有可能出现乱码。这是因为服务 器响应的数据也会经过网络传输,服务器端有一种编码方式,在客户端也存在一种编码方式,当两端使用的编码方式不同时则出现乱码。
getWriter() 的字符乱码
对于 getWriter() 获取到的字符流,响应中文必定出乱码,由于服务器端在进行编码时默认会使用 ISO-8859-1 格式的编码,该编码方式并不支持中文。
所以要解决该种乱码只能在服务器端告知服务器 使用一种能够支持中文的编码格式,比如我们通常用的“UTF-8” resp.setCharacterEncoding("UTF- 8");,此时还只完成了一半的工作,要保证数据正确显示,还需要指定客户端的解码方式
resp.setHeader(“content-type”, “text/html;charset=UTF-8”);,和服务器一致。两端指定编码 后,乱码就解决了。一句话:保证发送端和接收端的编码一致
resp.setCharacterEncoding("UTF-8");
resp.setHeader("content-type", "text/html; charset=UTF-8");
PrintWriter out = resp.getWriter();
out.write("<h1>你好啊!!!!</h1>");
以上两端编码的指定也可以使用一句替代,同时指定服务器和客户端
resp.setContentType("text/html;charset=utf-8");
对于 getOutputStream() 方式获取到的字节流,响应中文时,由于本身就是传输的字节,所以此时 可能出现乱码,也可能正确显示,这就看人品了_。当服务器端给的字节恰好和客户端使用的编码方 式一致时则文本正确显示,否则出现乱码。无论如何我们都应该准确掌握服务器和客户端使用的是那种 编码格式,以确保数据正确显示。指定客户端和服务器使用的编码方式一致即可。
resp.setHeader(“content-type”, “text/html;charset=UTF-8”);。
resp.setHeader("content-type", "text/html; charset=UTF-8");
ServletOutputStream out = resp.getOutputStream();
out.write("<h1>你好啊!!!</h1>".getBytes("UTF-8"));
同样也可以使用一句替代
resp.setContentType("text/html;charset=utf-8");
总结:要想解决响应的乱码,只需要保证使用支持中文的编码格式。并且保证服务器端 和客户端使
用相同的编码方式即可。
响应图片
在学习 HTML 的时候我们知道使用 的方式可以显示图片。但有的时候我们 并不知道(或不能确定)图片的路径,需要通过请求服务器资源动态地响应图片给客户端。这种方式和文 件拷贝的理念是一致的,客户端请求服务器的资源,在服务端获取到真实的图片资源,通过输入流读取 到内存,然后通过输出流写出到客户端即可。
值得注意的是,在客户端解析资源时默认是以文本(text/html)的形式,当响应图片时需要指定响 应头信息,告知客户端响应内容为图片形式,使用一种叫做 MIME 类型的东西来指定。MIME 类型见 Tomcat 的 web.xml 文件。
<mime-mapping>
<extension>jpeg</extension>
<mime-type>image/jpeg</mime-type>
</mime-mapping>
<mime-mapping>
<extension>jpg</extension>
<mime-type>image/jpeg</mime-type>
</mime-mapping>
<mime-mapping>
<extension>jpgm</extension>
<mime-type>video/jpm</mime-type>
</mime-mapping>
<mime-mapping>
<extension>jpgv</extension>
<mime-type>video/jpeg</mime-type>
</mime-mapping>
<mime-mapping>
<extension>jpm</extension>
<mime-type>video/jpm</mime-type>
</mime-mapping>
定义某一个扩展名和某一个 MIME Type 做对应,包含两个子元素:
<extension></extension> 扩展名的名称
MIME 格式
// 得到图片名称
String name = "tree.jpg";
// 得到图片存放在服务器上的真实路径 String filePath = req.getServletContext().getRealPath("/WEB-INF/image/" + name); System.out.println(filePath);
// 通过路径得到file对象
File file = new File(filePath);
// 判断file对象是否存在,并且是一个标准文件
if(file.exists() && file.isFile()){
resp.setContentType("image/jpeg;charset=utf-8");
// 判断图片的格式
// 得到图片的后缀
String pic = name.split("\\.")[0];
// 判断是否为空
if(pic == null || "".equals(pic)){
return;
}
// 根据不同的图片类型设置MIME响应类型
if("png".equals(pic)){
resp.setContentType("image/png;charset=utf-8");
}else if("gif".equals(pic)){
resp.setContentType("image/gif;charset=utf-8");
}else if("jpg".equals(pic) || "jpeg".equals(pic)){ resp.setContentType("image/jpeg;charset=utf-8"); }
// 得到文件的输入流
FileInputStream in = new FileInputStream(file);
// 得到输出流
ServletOutputStream out = resp.getOutputStream();
byte[] car = new byte[1024];
int len = 0;
while((len = in.read(car)) != -1){
// 输出
out.write(car, 0, len);
}
// 关闭流
in.close();
out.close();
} else {
PrintWriter out = resp.getWriter(); out.write("<h1>文件路径不正确!</h1>");
}
重定向跳转
重定向是一种服务器指导客户端的行为。客户端发出第一个请求,被服务器接收,经过处理服务器 进行响应,与此同时,服务器给客户端一个地址(下次请求的地址 resp.sendRedirect(“url”);),当客 户端接收到响应后,立刻、马上、自动根据服务器给的地址进行第二个请求的发送,服务器接收请求并 作出响应,重定向完成。从描述中可以看出重定向当中有两个请求存在,并且属于客户端行为。实现方式如下:
resp.sendRedirect("index.html")
通过观察浏览器我们发现第一次请求获得的响应码为 302,并且含有一个 location 头信息。并且地 址栏最终看到的地址是和第一次请求地址不同的,地址栏已经发生了变化。
请求转发和重定向比较:
请求转发(req.getRequestDispatcher().forward()) | 重定向(resp.sendRedirect()) |
---|---|
一次请求,数据在 request 域中共享 | 两次请求,request 域中数据不共享 |
服务器端行为 | 客户端行为 |
地址栏不发生变化 | 地址栏发生变化 |
绝对地址定位到站点后 | 绝对地址可写到 http:// |
在请求资源时,必须给出正确的路径,否则是找不到资源的。路径分为相对路径和绝对路径,绝对路径可简单理解为完整路径,在 web 项目中绝对路径分两种,一种是以 http:// 开头的,该种绝对路径 已经跨域,即任何地方的资源都能访问,另一种则是从当前域名|IP|主机后的端口号开始的,不能跨域,也属于一种绝对路径。相对路径则就是相对当前资源所在路径。
我们学的所有的请求可以分为客户端和服务器端请求两类(不考虑ajax);
相对路径
书写路径时,无论是哪类请求相对路径都是相对当前资源的路径
书写格式:直接从当前路径开始写,目录前不加任何符号;a.html html/b.html
相对路径在请求转发时可能会失效,因此开发中不推荐使用相对路径
绝对路径
使用绝对路径时则有两种方式,以 http:// 开头,或者以 / 开头,但是注意:只有客户端跳转才能 使用 http:// 这种方式,此时需要写出资源的完整路径;另一种以 / 开头的绝对路径,则是绝对到端口 后,例如本机则是:http://localhost:8080 此时则是 / 代表以上一串字符。
/helloworld/a.html → http://localhost:8080/helloworld/a.html
浏览器中:“/”代表的是 http://主机|IP:端口
服务器中:“/”代表的是 http://主机|IP:端口/站点名
现在对于我们来说,只有请求转发属于服务器跳转,其他都是客户端跳转。通过观察地址栏状态也 可判定跳转类型(请求类型),地址栏不变 → 服务器端跳转;地址栏改变 → 客户端跳转。
Cookies
Cookie 是浏览器提供的一种技术,通过服务器的程序能将一些只须保存在客户端,或者在客户端 进行处理的数据,放在本地的计算机上,无论何时用户链接到服务器,Web 站点都可以访问 Cookie 信息不需要通过网络传输,因而提高网页处理的效率,并且能够减少服务器的负载,但是由于 Cookie 是服务器端保存在客户端的信息,所以其安全性也是很差的。例如常见的记住密码则可以通过 Cookie 来实现。
有一个专门操作Cookie的类 javax.servlet.http.Cookie。随着服务器端的响应发送给客户端,保存在浏览器。当下次再访问服务器时把 Cookie 再带回服务器。
Cookie 的格式:键值对用 “ = ” 链接,多个键值对间通过 “ ; ” 隔开
Cookie 剖析
Cookie 通常设置在 HTTP 头信息中(虽然 JavaScript 也可以直接在浏览器上设置一个 Cookie)。设置 Cookie 的 Servlet 会发送如下的头信息:
HTTP/1.1 200 OK
Date: Fri, 04 Feb 2000 21:03:38 GMT
Server: Apache/1.3.9 (UNIX) PHP/4.0b3
Set-Cookie: name=xyz; expires=Friday, 04-Feb-07 22:03:38 GMT;
path=/; domain=runoob.com
Connection: close
Content-Type: text/html
Set-Cookie 头包含了一个名称值对、一个 GMT 日期、一个路径和一个域。名称和值会被 URL 编码。expires 字段是一个指令,告诉浏览器在给定的时间和日期之后"忘记"该 Cookie。
如果浏览器被配置为存储 Cookie,它将会保留此信息直到到期日期。如果用户的浏览器指向任何匹配该 Cookie 的路径和域的页面,它会重新发送 Cookie 到服务器。浏览器的头信息可能如下所示:
GET / HTTP/1.0
Connection: Keep-Alive
User-Agent: Mozilla/4.6 (X11; I; Linux 2.2.6-15apmac ppc)
Host: zink.demon.co.uk:1126
Accept: image/gif, */*
Accept-Encoding: gzip
Accept-Language: en
Accept-Charset: iso-8859-1,*,utf-8
Cookie: name=xyz
Servlet 就能够通过请求方法 request.getCookies() 访问 Cookie,该方法将返回一个 Cookie 对象的数组。
Servlet Cookie 方法
序号 | 方法 & 描述 |
---|---|
1 | public void setDomain(String pattern) 该方法设置 cookie 适用的域,例如 runoob.com。 |
2 | public String getDomain() 该方法获取 cookie 适用的域,例如 runoob.com。 |
3 | public void setMaxAge(int expiry) 该方法设置 cookie 过期的时间(以秒为单位)。如果不这样设置,cookie 只会在当前 session 会话中持续有效。 |
4 | public int getMaxAge() 该方法返回 cookie 的最大生存周期(以秒为单位),默认情况下,-1 表示 cookie 将持续下去,直到浏览器关闭。 |
5 | public String getName() 该方法返回 cookie 的名称。名称在创建后不能改变。 |
6 | public void setValue(String newValue) 该方法设置与 cookie 关联的值。 |
7 | public String getValue() 该方法获取与 cookie 关联的值。 |
8 | public void setPath(String uri) 该方法设置 cookie 适用的路径。如果您不指定路径,与当前页面相同目录下的(包括子目录下的)所有 URL 都会返回 cookie。 |
9 | public String getPath() 该方法获取 cookie 适用的路径。 |
10 | public void setSecure(boolean flag) 该方法设置布尔值,表示 cookie 是否应该只在加密的(即 SSL)连接上发送。 |
11 | public void setComment(String purpose) 设置cookie的注释。该注释在浏览器向用户呈现 cookie 时非常有用。 |
12 | public String getComment() 获取 cookie 的注释,如果 cookie 没有注释则返回 null。 |
Cookie的创建和发送
通过 new Cookie(“key”,”value”); 来创建一个 Cookie 对象,要想将 Cookie 随响应发送到客户端,需要先添加到 response 对象中, resp.addCookie(cookie); 此时该 cookie 对象则随着响应发送至了客户端。在浏览器上可以看见。
// 创建Cookie对象
Cookie cookie = new Cookie("A", "aa");
// 响应给客户端
resp.addCookie(cookie);
Cookie 的获取
在服务器端只提供了一个 getCookies() 的方法用来获取客户端回传的所有 cookie 组成的一个数组,如果需要获取单个 cookie 则需要通过遍历,getName() 获取 Cookie 的名称,getValue()获取 Cookie 的值。
// 获取客户端的Cookie数组
Cookie[] cookies = req.getCookies();
// 判断是否为空
if(cookies != null && cookies.length > 0){
// 遍历
for (Cookie cookie : cookies) {
// 键名称
String name = cookie.getName();
// 值
String value = cookie.getValue();
System.out.println("键:" + name + ",值:" + value);
}
}
Cookie 到期时间的设定
从图中除了看到 Cookie 的名称和内容外,我们还需要关心一个信息,到期时间,到期时间用来指定该 cookie 何时失效。默认为当前浏览器关闭即失效。我们可以手动设定 cookie 的有效时间(通过到期时间计算),通过 setMaxAge(int expiry); 方法设定 cookie 的最大有效时间,以秒为单位。
大于 0 的整数,表示存储的秒数;若为负数,则表示不存储该 cookie;若为 0,则删除该cookie。
负整数:cookie 的 maxAge 属性的默认值就是 -1,表示只在浏览器内存中存活,一旦关闭浏览器窗口,那么 cookie 就会消失。
正整数:表示 cookie 对象可存活指定的秒数。当生命大于 0 时,浏览器会把 Cookie 保存到硬盘上,就算关闭浏览器,就算重启客户端电脑,cookie 也会存活相应的时间。
零:cookie 生命等于 0 是一个特殊的值,它表示 cookie 被作废!也就是说,如果原来浏览器已经保存了这个 Cookie,那么可以通过 Cookie 的 setMaxAge(0) 来删除这个 Cookie。 无论是在浏览器内存中,还是在客户端硬盘上都会删除这个 Cookie。
Cookie的注意
在一般的站点中常常有记住用户名这样一个操作,该操作只是将信息保存在本机上,换电脑以后这些信息就无效了。而且 cookie 还不能跨浏览器。
Cookie 中不能出现中文,如果有中文则通过 URLEncoder.encode() 来进行编码,获取时通过URLDecoder.decode() 来进行解码。
String name = "姓名";
String value = "张三";
name = URLEncoder.encode(name, "utf-8");
value = URLEncoder.encode(value, "utf-8");
Cookie cookie = new Cookie(name, value); resp.addCookie(cookie);
Cookie[] cookies = req.getCookies();
if(cookies != null){
for(Cookie coo:cookies){
String name = URLDecoder.decode(coo.getName(), "utf-8");
String value = URLDecoder.decode(coo.getValue(), "utf-8");
System.out.println(name + ":" + value);
}
}
不同的浏览器对 Cookie 也有限定,Cookie 的存储是有上限的。Cookie 是存储在客户端(浏览器)的,而且一般是由服务器端创建和设定。后期结合 Session 来实现会话跟踪。