cookie是小段的文本信息,web服务器将它发送到浏览器,之后,在访问同一网站或域时,浏览器又将它原封不动的返回给服务器,通过服务器读取它之前发送给客户程序的信息,站点可以为访问者提供很多便利,例如按照访问者之前的定制呈现该站点,或让身份可以验证的访问者进入,不需要再次输入密码。
8.1 了解cookie的优点
网站对cookie的利用一般有以下四种方式:
1)在电子商务会话中表示用户
许多在线商店使用购物车来模拟现实世界的购物。由于每个页面发送之后,一般都会关闭(Http协议的特点),持续性连接只适用于时间上极为接近的请求,不能解决这一问题,cookie能够解决这一问题,为此,servlet还提供了专门用于会话的API,使得开发人员不需直接操作cookie就能利用它(见后一章节)
2)记录用户名和密码
3)定制站点(使用cookie记录用户偏好)
4)定向广告
8.2 cookie存在的一些问题
cookie可能对用户的隐私造成一定的威胁,某些用户选择关闭cookie,所以要综合考虑是否可以不用cookie实现相同的功能。另外尽量不要用cookie记录特别隐私的信息。
8.3 cookie的删除
不说了,鼠标操作,会上网的人都会
8.4 cookie的发送和接受
cookie的原理的两个过程:1,服务器发送cookie到客户端 2,客户端将cookie返回给服务器
1,向客户端发送cookie
1)创建Cookie对象
2)设置最大时效
3)将Cookie放入到HTTP响应报头(发送给客户端)
userCookie.setMaxAge( 60 * 60 * 24 * 365 ); // Store cookie for 1 year
response.addCookie(userCookie);
2,读取客户端发送回来的cookie
1)调用request.getCookies得到一个Cookie对象数组
2)对数组循环,调用每个cookie的getName方法得到cookie的名字,getValue得到cookie的值
例子:
Cookie[] cookies = request.getCookies();
if (cookies != null ) {
for ( int i = 0 ; i < cookies.length; i ++ ) {
Cookie cookie = cookies[i];
if (cookieName.equals(cookie.getName())) {
doSomethingWith(cookie.getValue());
}
}
}
8.5 示例:使用cookie检测初访者
用一个boolean值newbie来标记是否是初访者,初始时servlet中的newbie值是true。(还没有访问者,第一个来的就是初访者)。用一个cookie字段repeatVisitor来表示不是访者(开始没有这个cookie,当第一次请求时,给这个cookie设置值为yes,以后再访问这个servlet时,就有了repeatVisitor这个cookie字段)
---注意这段话也表明了上面红线部分提到的cookie的工作原理:
package coreservlets;
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
/*cookie的原理:第一次访问servlet,浏览器端没有cookie,在服务器中创建一个cookie对象并设置值,
* 服务器会把这个cookie返回给浏览器,下一次在访问这个servlet,浏览器会带来这个cookie对象,服务器
* 从request读取浏览器返回来的cookie对象,根据这个cookie对象及其值来决定接下来的操作(返回给浏览器什么)*/
public class RepeatVisitor extends HttpServlet {
public void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
boolean newbie = true;//每次访问这个servlet都要置newbie为true
Cookie[] cookies = request.getCookies();//得到所有的cookies
if (cookies != null) {
for(int i=0; i<cookies.length; i++) {
Cookie c = cookies[i];
if ((c.getName().equals("repeatVisitor")) &&(c.getValue().equals("yes")))//判断有没有特定的这个cookie
{
newbie = false;//如果没有(说明之前没有来过)就要改变newbie的值
break;
}
}
}
String title;
if (newbie) {//如果是初访,要创建一个cookie对象repeatVisitor且设置对应的title
Cookie returnVisitorCookie =
new Cookie("repeatVisitor", "yes");
returnVisitorCookie.setMaxAge(60*60*24*365); // 1 year
response.addCookie(returnVisitorCookie);
title = "Welcome Aboard";
} else {
title = "Welcome Back";//如果不是初访,设置对应的title
}
response.setContentType("text/html");
PrintWriter out = response.getWriter();
String docType =
"<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 " +
"Transitional//EN\">\n";
out.println(docType +
"<HTML>\n" +
"<HEAD><TITLE>" + title + "</TITLE></HEAD>\n" +
"<BODY BGCOLOR=\"#FDF5E6\">\n" +
"<H1 ALIGN=\"CENTER\">" + title + "</H1>\n" +
"</BODY></HTML>");
}
}
首次访问:http://localhost:8080/Servlet/servlet/RepeatVisitor,浏览器显示Welcome Abroad,将浏览器刷新下,显示Welcome back
8.6 使用cookie属性
服务器在将cookie加到输出报头之前,可以使用下面的setXxx方法设置cookie的各项特性。Xxx是属性名称。注意,属性是从服务器发送到浏览器的报头的一部分,但它们不属于由浏览器返回给服务器的报头(单向的),即从服务器发送到浏览器的cookie包含名称,值,以及属性,但反过来从浏览器返回给服务器的cookie只有名称和值,不包含属性。
因而在服务器端不要期望通过request.getCookies得到的cookie中可以使用这些属性。
设置cookie属性的方法:
最常用的public void setMaxAge(int lifetime)
public String getName()
public String getValue()
8.7 区分会话cookie与持续性cookie
通过对比设置最大时效和未设置最大时效的cookie行为,说明cookie属性的使用。
一个浏览回话----浏览器没有重新启动
下列servlet设置了6个cookie,3个没有设置时效(默认只适合当前会话---即在浏览器重启前这3个cookie有效)
还有3个设置了时效1小时,setMaxAge是将他们写在写在磁盘上的,所以一小时内,不管是重启计算机还是重启浏览器,这三个cookie总是有效
代码:
package coreservlets;
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
public class CookieTest extends HttpServlet {
public void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
for(int i=0; i<3; i++) {
//未设置时效的3个cookie
Cookie cookie = new Cookie("Session-Cookie-" + i,"Cookie-Value-S" + i);
response.addCookie(cookie);//别忘了这一步,加入response响应报头才能发送给浏览器
//设置时效的3个cookie
cookie = new Cookie("Persistent-Cookie-" + i,"Cookie-Value-P" + i);
cookie.setMaxAge(3600);//设置cookie属性
response.addCookie(cookie);
}
response.setContentType("text/html");
PrintWriter out = response.getWriter();
String docType =
"<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 " +
"Transitional//EN\">\n";
String title = "Active Cookies";
out.println(docType +
"<HTML>\n" +
"<HEAD><TITLE>" + title + "</TITLE></HEAD>\n" +
"<BODY BGCOLOR=\"#FDF5E6\">\n" +
"<H1 ALIGN=\"CENTER\">" + title + "</H1>\n" +
"<TABLE BORDER=1 ALIGN=\"CENTER\">\n" +
"<TR BGCOLOR=\"#FFAD00\">\n" +
" <TH>Cookie Name\n" +
" <TH>Cookie Value");
Cookie[] cookies = request.getCookies();
if (cookies == null) {
out.println("<TR><TH COLSPAN=2>No cookies");//没有cookie时的输出
} else {
for(Cookie cookie: cookies) {//输出有效cookie的名称和值
out.println
("<TR>\n" +
" <TD>" + cookie.getName() + "\n" +
" <TD>" + cookie.getValue());
}
}
out.println("</TABLE></BODY></HTML>");
}
}
结果分析:
首次访问的结果,因为第一次没有cookie,如果关闭浏览器(当前会话结束),一个小时候后,在访问还是这个结果(持续性的cookie的有效期也过了)
首次访问后一个小时内,在同一个会话中(浏览器木有关闭),再次访问的结果,此时6个cookie对象均有效(没有设置有效时间的cookie的有效期是当前会话期间)
首次访问之后,在不同的会话中(浏览器关闭过),一小时内再次访问的结果(只有设置时效为一小时的cookie对象有效)
8.8 基本的cookie使用程序
创建小的实用程序(将一些最常写到的代码写成一个类的静态方法以后每次可以直接调用这个方法);
1, 把CookieUtilities作为一个工具类,两个静态方法为我们编制程序提供了许多方便:
public static String getCookieValue(HttpServletRequest request,String cookieName,String defaultValue)
根据三个参数request,cookieName,defaultValue返回在请求request中cookieName对应的cookie的值,如果木有就返回默认值defaultValue
public static Cookie getCookie(HttpServletRequest request,String cookieName)
返回cookie对象。
package coreservlets;
import javax.servlet.*;
import javax.servlet.http.*;
//工具类:两个静态方法
public class CookieUtilities {
//返回指定名字的cookie的值
public static String getCookieValue
(HttpServletRequest request,
String cookieName,
String defaultValue) {
Cookie[] cookies = request.getCookies();
if (cookies != null) {
for(Cookie cookie: cookies) {
if (cookieName.equals(cookie.getName())) {
return(cookie.getValue());
}
}
}
return(defaultValue);
}
//返回指定名字的cookie的cookie对象
public static Cookie getCookie(HttpServletRequest request,
String cookieName) {
Cookie[] cookies = request.getCookies();
if (cookies != null) {
for(Cookie cookie: cookies) {
if (cookieName.equals(cookie.getName())) {
return(cookie);
}
}
}
return(null);
}
}
2, 下面的清单给出了一个创建长期存在的cookie的工具
实际上就是写了一个继承Cookie的类,创建了这个类的对象实际上也属于Cookie对象。
package coreservlets;
import javax.servlet.http.*;
//继承自Cookie
public class LongLivedCookie extends Cookie {
public static final int SECONDS_PER_YEAR = 60*60*24*365;
public LongLivedCookie(String name, String value) {//用Cookie名称和值初始化一个对象
super(name, value);
setMaxAge(SECONDS_PER_YEAR);
}
}
8.9 使用上述使用工具程序(简化代码)
有了上述工具后,在RepeatVisitor程序中
String value =CookieUtilities.getCookieValue(request, "repeatVisitor2","no");就可以直接得到cookie的值
LongLivedCookie returnVisitorCookie =new LongLivedCookie("repeatVisitor2", "yes");就可以直接创建cookie对象
8.10 修改cookie的值:记录用户的访问计数
要想替换之前的cookie,需要发送名称相同但值不同的的cookie,别忘了如果你的这个cookie还有许多属性的话,由于浏览器返回的cookie木有cookie属性,所以服务器再次发送的cookie必须还要重设所有属性(很麻烦)。
要从服务器再次发送的cookie中指示删除一个cookie,只需重设属性setMaxAge为0即可。
package coreservlets;
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
public class ClientAccessCounts extends HttpServlet {
public void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
String countString = CookieUtilities.getCookieValue(request,"accessCount", "1");//调用工具类
int count = 1;
try {
count = Integer.parseInt(countString);
} catch(NumberFormatException nfe) { }
LongLivedCookie c =new LongLivedCookie("accessCount",String.valueOf(count+1));//调用工具类
response.addCookie(c);//每次都要发送同名cookie,更新count的值
response.setContentType("text/html");
PrintWriter out = response.getWriter();
String title = "Access Count Servlet";
String docType =
"<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 " +
"Transitional//EN\">\n";
out.println(docType +
"<HTML>\n" +
"<HEAD><TITLE>" + title + "</TITLE></HEAD>\n" +
"<BODY BGCOLOR=\"#FDF5E6\">\n" +
"<CENTER>\n" +
"<H1>" + title + "</H1>\n" +
"<H2>This is visit number " +
count + " by this browser.</H2>\n" +
"</CENTER></BODY></HTML>");
}
}
每个用户都会看到他们的访问技术,不同的浏览器维护一个独立的计数
8.11 使用cookie记录用户偏好
下列清单创建一个输入表单:
package coreservlets;
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
public class RegistrationForm extends HttpServlet {
public void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html");
PrintWriter out = response.getWriter();
String actionURL ="/Servlet/servlet/RegistrationServlet";//表单相应的url的写法
//调用工具类取得表单项的值(如果有值的话,重定向回来就会填充表单,不会再次填写)
String firstName =CookieUtilities.getCookieValue(request, "firstName", "");
String lastName = CookieUtilities.getCookieValue(request, "lastName", "");
String emailAddress =CookieUtilities.getCookieValue(request, "emailAddress","");
String docType =
"<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 " +
"Transitional//EN\">\n";
String title = "Please Register";
out.println
(docType +
"<HTML>\n" +
"<HEAD><TITLE>" + title + "</TITLE></HEAD>\n" +
"<BODY BGCOLOR=\"#FDF5E6\">\n" +
"<CENTER>\n" +
"<H1>" + title + "</H1>\n" +
"<FORM ACTION=\"" + actionURL + "\">\n" +//响应的servlet
"First Name:\n" +
" <INPUT TYPE=\"TEXT\" NAME=\"firstName\" " +
"VALUE=\"" + firstName + "\"><BR>\n" +
"Last Name:\n" +
" <INPUT TYPE=\"TEXT\" NAME=\"lastName\" " +
"VALUE=\"" + lastName + "\"><BR>\n" +
"Email Address: \n" +
" <INPUT TYPE=\"TEXT\" NAME=\"emailAddress\" " +
"VALUE=\"" + emailAddress + "\"><P>\n" +
"<INPUT TYPE=\"SUBMIT\" VALUE=\"Register\">\n" +
"</FORM></CENTER></BODY></HTML>");
}
}
第一个servlet创建的表单填写后向第二个servlet提交响应请求,第二个servlet检查填写的情况,若完整直接显示,若不完整重定向到表单填写页面(即第一个servlet),且之前填写好的表单项不丢失,用户只需填写未填的项。
下列清单提交后的响应:
package coreservlets;
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
public class RegistrationServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html");
boolean isMissingValue = false;
//得到表单项
String firstName = request.getParameter("firstName");
if (isMissing(firstName)) {
firstName = "Missing first name";
isMissingValue = true;
}
String lastName = request.getParameter("lastName");
if (isMissing(lastName)) {
lastName = "Missing last name";
isMissingValue = true;
}
String emailAddress = request.getParameter("emailAddress");
if (isMissing(emailAddress)) {
emailAddress = "Missing email address";
isMissingValue = true;
}
Cookie c1 = new LongLivedCookie("firstName", firstName);
response.addCookie(c1);
Cookie c2 = new LongLivedCookie("lastName", lastName);
response.addCookie(c2);
Cookie c3 = new LongLivedCookie("emailAddress", emailAddress);
response.addCookie(c3);
//注意路径的写法!!!
String formAddress = "/Servlet/servlet/RegistrationForm";
//如果表单缺失,重定向到表单填写的servlet处,注意重定向会将cookie一起发给那个servlet
if (isMissingValue) {
response.sendRedirect(formAddress);
} else {
PrintWriter out = response.getWriter();
String docType = "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 "
+ "Transitional//EN\">\n";
String title = "Thanks for Registering";
out.println(docType + "<HTML>\n" + "<HEAD><TITLE>" + title
+ "</TITLE></HEAD>\n" + "<BODY BGCOLOR=\"#FDF5E6\">\n"
+ "<CENTER>\n" + "<H1 ALIGN>" + title + "</H1>\n"
+ "<UL>\n" + " <LI><B>First Name</B>: " + firstName + "\n"
+ " <LI><B>Last Name</B>: " + lastName + "\n"
+ " <LI><B>Email address</B>: " + emailAddress + "\n"
+ "</UL>\n" + "</CENTER></BODY></HTML>");
}
}
private boolean isMissing(String param) {
return ((param == null) || (param.trim().equals("")));
}
}
结果:
填写完整,就直接显示页面
填写不完整(假设第一项缺失),提交后返回到提交的这个页面,之前填写过的不丢失,没有填写的被missing,,,填充,提示你没有填写
总结:cookie还是很重要的一章,在电子商务网站中经常用到的一项技术。主要是要理解这些技术的实现原理和过程,至于细节的实现,要在具体的项目里逐渐熟悉。