Servlet—Cookie(显示用户上次访问时间、显示商品浏览历史)

1 . 什么是会话?

会话可简单理解为:用户开一个浏览器,点击多个超链接,访问服务器多个web资源,然后关闭浏览器,整个过程称之为一个会话。

1.1 会话过程中要解决的一些问题?

每个用户在使用浏览器与服务器进行会话的过程中,不可避免各自会产生一些数据,程序要想办法为每个用户保存这些数据。

例如:用户点击超链接通过一个servlet购买了一个商品,程序应该想办法保存用户购买的商品,以便于用户点结帐servlet时,结帐servlet可以得到用户购买的商品为用户结帐。

思考:用户购买的商品保存在request或servletContext中行不行?

假如用使用request保存购买商品的servlet中的信息,当用户访问结账servlet时,又重新创建了一个新的request,所以使用request不行。

使用ServletContext也不行,因为ServletContext是针对整个web应用的。

1.2 保存会话数据的技术

保存会话有两种技术:Cookie和Session。

2. Cookie

Cookie是客户端技术,程序把每个用户的数据以cookie的形式写给用户各自的浏览器。当用户使用浏览器再去访问服务器中的web资源时,就会带着各自的数据去。这样,web资源处理的就是用户各自的数据了。

2.1 Cookie的常用方法

public Cookie(String name,String value):Constructs a cookie with a specified name and value.

void setValue(java.lang.String newValue)  :Assigns a new value to a cookie after the cookie is created.

String getValue() : Returns the value of the cookie.

void setMaxAge(int expiry):Sets the maximum age of the cookie in seconds.

int getMaxAge():Returns the maximum age of the cookie, specified in seconds, By default,-1 indicating the cookie will persist until browser shutdown.

void setPath(java.lang.String uri): Specifies a path for the cookie to which the client should return the cookie.

String getPath() :Returns the path on the server to which the browser returns this cookie.

void setDomain(java.lang.String pattern):Specifies the domain within which this cookie should be presented.

String getDomain():Returns the domain name set for this cookie. 

String getName(): Returns the name of the cookie.

2.2  显示用户上次登录的时间

package com.oner.cookie;

import java.io.IOException;
import java.io.PrintWriter;
import java.util.Date;

import javax.servlet.ServletException;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

//假设CookieDemo这个Servlet代表网站首页
public class CookieDemo extends HttpServlet {

	private static final long serialVersionUID = 1L;

	protected void doGet(HttpServletRequest request,
			HttpServletResponse response) throws ServletException, IOException {

		response.setCharacterEncoding("UTF-8");
		response.setContentType("text/html;charset=UTF-8");
		
		response.setHeader("Expires", "-1");  
        response.setHeader("Cache-Control", "no-cache");  
        response.setHeader("Pragma", "no-cache"); 

		PrintWriter out = response.getWriter();
		out.print("您上次访问的时间是");

		// 获得用户的时间cookie
		Cookie[] cookies = request.getCookies();
		// 用户第一次访问时并没有带Cookie,所以需要先检查下
		for (int i = 0; cookies != null && i < cookies.length; i++) {
			if (cookies[i].getName().equals("lastAccessTime")) {
				long cookieValue = Long.parseLong(cookies[i].getValue());// 得到了上次访问的时间
				Date date = new Date(cookieValue);
				out.print(date.toString());
			}
		}

		// 给用户发送最新的访问时间
		Cookie cookie = new Cookie("lastAccessTime", System.currentTimeMillis()
				+ "");
		cookie.setMaxAge(1 * 30 * 24 * 3600);// 设置Cookie有效期为一个月
		cookie.setPath("/day07");
		response.addCookie(cookie);
		
	}

	protected void doPost(HttpServletRequest request,
			HttpServletResponse response) throws ServletException, IOException {
		doGet(request, response);
	}

}


在浏览器地址中输入:http://localhost:8080/day07/CookieDemo,回车:


刷新后:


2.3 Cookie的一些细节

1. 一个Cookie只能标识一种信息,它至少含有一个标识该信息的名称(NAME)和设置值(VALUE)。

2. 一个WEB站点可以给一个WEB浏览器发送多个Cookie,一个WEB浏览器也可以存储多个WEB站点提供的Cookie。

3. 浏览器一般只允许存放300个Cookie,每个站点最多存放20个Cookie,每个Cookie的大小限制为4KB。

4. 如果创建了一个cookie,并将他发送到浏览器,默认情况下它是一个会话级别的cookie(即存储在浏览器的内存中),用户退出浏览器之后即被删除。若希望浏览器将该cookie存储在磁盘上,则需要使用maxAge,并给出一个以秒为单位的时间。将最大时效设为0则是命令浏览器删除该cookie。需要注意,删除cookie时,path必须一致,否则不会删除。

这儿演示下:

先使用火狐浏览器搜索Cookie选项,搜索“lastAccessTime”,发现没有lastAccessTime文件:


在地址栏中输入:http://localhost:8080/day07/CookieDemo,回车:


再次搜索名为“lastAccessTime”的cookie文件,发现确实存在:


说明浏览器确实得到了该cookie,再次刷新地址栏:


说明第二次访问时服务器从浏览器发送到请求中得到了“lastAccessTime”cookie信息。点击“点击上次访问时间”:


地址跳转到了http://localhost:8080/day07/DeleteCookie,说明执行了DeleteCookie这个Servlet,在Cookie选项中搜索“lastAccessTime”这条Cookie,发现没有:



说明删除成功。

2.4 显示商品浏览历史记录

假如一个网上书店需要在用户每次登陆主页后显示用户曾经浏览过的一些书籍,该怎么实现呢?

思考下,这儿至少需要两个Servlet,一个是ServletA,一个是ServletB。ServletA代表网上书店的主页,主要是用于显示书店的所有书籍以及曾经浏览的一些书籍(按照浏览时间的递减顺序显示,也就是最后浏览的书籍显示在最上面):


ServletB主要用于显示书籍的详细信息:


显示书店的所有书籍以及显示书籍的详细信息很容易实现,可以从数据库中得到,这儿用一个Book类和DB类来模拟数据库:

class Book {

	private String id;
	private String name;
	private String author;
	private String description;

	public Book() {
		super();
	}

	public Book(String id, String name, String author, String description) {
		super();
		this.id = id;
		this.name = name;
		this.author = author;
		this.description = description;
	}

	public String getId() {
		return id;
	}

	public void setId(String id) {
		this.id = id;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public String getAuthor() {
		return author;
	}

	public void setAuthor(String author) {
		this.author = author;
	}

	public String getDescription() {
		return description;
	}

	public void setDescription(String description) {
		this.description = description;
	}

}


显示所有书籍名称:

要在ServletA中显示所有的书籍名称,只需要输出书名就可以了,但是想要在点击之后显示该书籍的详细信息,必须要将该书籍的id在超链接中带给ServletB,这样ServletB才能知道显示哪个书籍的详细信息。

		PrintWriter out = response.getWriter();

		out.write("本网站有如下书籍:<br/>");
		Map<String, Book> map = DB.getAll();
		Set<Map.Entry<String, Book>> set = map.entrySet();
		for (Map.Entry<String, Book> entry : set) {
			Book book = entry.getValue();
			// 这儿的超链接打开方式设为在新的窗口打开
			// 并且需要将每次所浏览书的id给ServletB
			out.print("<a href='/day07/ServletB?id=" + book.getId()
					+ "' target='_blank'>" + book.getName() + "</a>");
			out.write("<br/>");
		}


显示书籍详细内容:

在ServletB中,从用户点点击的超链接中得到想要查看书籍的id,然后由该id输出该书籍的详细信息:

		PrintWriter out = response.getWriter();

		String id = request.getParameter("id");
		Book book = DB.getAll().get(id);

		out.write("您要查看的书的详细信息如下:<br/><br/>");
		out.print("书名:" + book.getName() + "<br/>");
		out.print("作者:" + book.getAuthor() + "<br/>");
		out.print("描述:" + book.getDescription() + "<br/>");


以上实现了显示所有书籍名称以及显示书籍详细内容,如果想要实现用户浏览过的书籍该怎么实现呢?

可以创建一个名为“bookHistory”的Cookie,它的值为浏览过书籍的id。这样用户在浏览器中每次(第一次访问除外)访问一本书籍时,服务器这端都可以从这次的request中得到“bookHistory”的值,也就是之前浏览书籍的id,然后就能拿到用户之前访问的书籍有哪些。

这里设定bookHistory的存放的书籍id个数最多为3(也就是说在浏览器页面只能显示最近的3次浏览历史),并且id之间以“,”分割。具体怎么实现呢?

在ServletB中就是拿到用户最新访问书籍的id,并根据之前的bookHistory值来构造新的bookHistory,然后写给浏览器。

在ServletA中就是从用户的本次request中得到bookHistory,也就是拿到了之前浏览过的书籍的id,然后由id将书名输出即可。

ServletB:

// 根据当前浏览书籍的id和本次request中包含的bookHistory来构建一个新的bookHistory
		String cookieValue = buildCookie(id, request);
		Cookie cookie = new Cookie("bookHistory", cookieValue);
		
		cookie.setMaxAge(1 * 30 * 24 * 3600);
		cookie.setPath("/day07");

		response.addCookie(cookie);


ServletA:

		out.print("<br/><br/>您曾经浏览过的书籍:<br/>");
		Cookie[] cookies = request.getCookies();
		for (int i = 0; cookies != null && i < cookies.length; i++) {
			if (cookies[i].getName().equals("bookHistory")) {
				String[] ids = cookies[i].getValue().split("\\,");// 得到浏览过所有书籍的id集合{2,3,1}
				for (String id : ids) {
					Book book = DB.getAll().get(id);
					out.print(book.getName() + "<br/>");
				}
			}
		}


完整的代码如下:

ServletA:

package com.oner.cookie;

import java.io.IOException;
import java.io.PrintWriter;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;

import javax.servlet.ServletException;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

//代表书店首页的Servlet
public class ServletA extends HttpServlet {
	private static final long serialVersionUID = 1L;

	protected void doGet(HttpServletRequest request,
			HttpServletResponse response) throws ServletException, IOException {

		response.setCharacterEncoding("UTF-8");
		response.setContentType("text/html;charset=UTF-8");
		PrintWriter out = response.getWriter();

		// 1. 输出网站的所有书籍
		out.write("本网站有如下书籍:<br/>");
		Map<String, Book> map = DB.getAll();
		Set<Map.Entry<String, Book>> set = map.entrySet();
		for (Map.Entry<String, Book> entry : set) {
			Book book = entry.getValue();
			// 这儿的超链接打开方式设为在新的窗口打开
			// 并且需要将每次所浏览书的id给ServletB
			out.print("<a href='/day07/ServletB?id=" + book.getId()
					+ "' target='_blank'>" + book.getName() + "</a>");
			out.write("<br/>");
		}

		// 4. 从浏览器带来的Cookie中,得到用户曾经看过的书籍
		out.print("<br/><br/>您曾经浏览过的书籍:<br/>");
		Cookie[] cookies = request.getCookies();
		for (int i = 0; cookies != null && i < cookies.length; i++) {
			if (cookies[i].getName().equals("bookHistory")) {
				String[] ids = cookies[i].getValue().split("\\,");// 得到浏览过所有书籍的id集合{2,3,1}
				for (String id : ids) {
					Book book = DB.getAll().get(id);
					out.print(book.getName() + "<br/>");
				}
			}
		}

	}

	protected void doPost(HttpServletRequest request,
			HttpServletResponse response) throws ServletException, IOException {
		doGet(request, response);
	}

}

class DB {

	// 使用LinkedHashMap是为了保证存储和取出的顺序一致
	private static Map<String, Book> map = new LinkedHashMap<String, Book>();

	static {

		map.put("1", new Book("1", "javaweb开发", "老张", "一本好书"));
		map.put("2", new Book("2", "spring开发", "老黎", "一本好书"));
		map.put("3", new Book("3", "hibernate开发", "老佟", "一本好书"));
		map.put("4", new Book("4", "struts开发", "老毕", "一本好书"));
		map.put("5", new Book("5", "ajax开发", "老张", "一本好书"));

	}

	public static Map<String, Book> getAll() {
		return map;
	}

}

class Book {

	private String id;
	private String name;
	private String author;
	private String description;

	public Book() {
		super();
	}

	public Book(String id, String name, String author, String description) {
		super();
		this.id = id;
		this.name = name;
		this.author = author;
		this.description = description;
	}

	public String getId() {
		return id;
	}

	public void setId(String id) {
		this.id = id;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public String getAuthor() {
		return author;
	}

	public void setAuthor(String author) {
		this.author = author;
	}

	public String getDescription() {
		return description;
	}

	public void setDescription(String description) {
		this.description = description;
	}

}


ServletB:

package com.oner.cookie;

import java.io.IOException;
import java.io.PrintWriter;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;

import javax.servlet.ServletException;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

//显示商品详细信息的Servlet
public class ServletB extends HttpServlet {
	private static final long serialVersionUID = 1L;

	protected void doGet(HttpServletRequest request,
			HttpServletResponse response) throws ServletException, IOException {

		response.setCharacterEncoding("UTF-8");
		response.setContentType("text/html;charset=UTF-8");
		PrintWriter out = response.getWriter();

		// 2. 根据用户带过来的id,显示商品的详细信息
		String id = request.getParameter("id");
		Book book = DB.getAll().get(id);

		out.write("您要查看的书的详细信息如下:<br/><br/>");
		out.print("书名:" + book.getName() + "<br/>");
		out.print("作者:" + book.getAuthor() + "<br/>");
		out.print("描述:" + book.getDescription() + "<br/>");

		// 3. 构建Cookie,回写给浏览器
		// 根据当前浏览书籍的id和本次request中包含的bookHistory来构建一个新的bookHistory
		String cookieValue = buildCookie(id, request);
		Cookie cookie = new Cookie("bookHistory", cookieValue);
		
		cookie.setMaxAge(1 * 30 * 24 * 3600);
		cookie.setPath("/day07");

		response.addCookie(cookie);

	}

	private String buildCookie(String id, HttpServletRequest request) {

		// bookHistory=null 1 bookHistory=1
		// bookHistory=3_1_5 1 bookHistory=1_3_5
		// bookHistory=3_2_5 1 bookHistory=1_3_2
		// bookHistory=3_2 1 bookHistory=1_3_2

		String bookHistory = null;
		Cookie[] cookies = request.getCookies();

		// 从浏览器带来的cookie中得到bookHistory,并将其赋值给本地变量bookHistory
		for (int i = 0; cookies != null && i < cookies.length; i++) {
			if (cookies[i].getName().equals("bookHistory")) {
				bookHistory = cookies[i].getValue();
			}
		}

		// ①如果bookHistory=null,说明浏览器之前并没有访问过本网站的书籍,所以返回当前浏览的书籍的id
		if (bookHistory == null) {
			return id;
		}

		// bookHistory字符串中包含的是之前浏览书籍的所有id,且以“,”分开
		// 所以可以将其切分成一个数组
		String[] srcArray = bookHistory.split("\\,");
		// 将数组转成List集合
		List<String> srcList = Arrays.asList(srcArray);
		// 查看asList方法的源码,知道该方法返回的其实是一个ArrayList
		// 由于LinkedList集合的增删快,所以可以将ArrayList集合转成LinkedList
		LinkedList<String> list = new LinkedList<String>(srcList);

		// ②如果bookHistory中包含当前浏览书籍的id,那么就将当前浏览书籍的id放在最前面
		if (list.contains(id)) {
			list.remove(id);
			list.addFirst(id);
		} else {
			// ③如果bookHistory中包含的书籍id大于等于3,则需要移去最后的id,并将当前浏览书籍的id放在最前面
			if (list.size() >= 3) {
				list.removeLast();
				list.addFirst(id);

			} else {
				// ④bookHistory包含的书籍id小于3,且不含当前浏览书籍的id,则直接将当前浏览书籍的id加入到最前面
				list.addFirst(id);
			}
		}

		StringBuffer sb = new StringBuffer();
		for (String l : list) {
			sb.append(l).append(",");
		}

		// 去掉左后一个无用的逗号
		return sb.deleteCharAt(sb.length() - 1).toString();
	}

	protected void doPost(HttpServletRequest request,
			HttpServletResponse response) throws ServletException, IOException {
		doGet(request, response);
	}

}


测试:

打开httpwatch,在浏览器地址中输入:http://localhost:8080/day07/ServletA,回车:



点击spring开发这本书籍,浏览器开启了一个新的页面显示这本书的详细信息:


查看httpwatch:


从响应头Set-Cookie中可以看出来,服务器发送了一个名为“bookHistory”,值为2的Cookie。其实这时候,浏览器会将这个cookie缓存到本地磁盘中。再次回到主页,点击javaweb开发这本书,发现浏览器开启了一个新的页面显示这本书的详细信息:


查看httpwatch:


从发送头Cookie可以看到,bookHistory=2,说明浏览器这次是携带着上次缓存到本地磁盘的cookie来访问的;从响应头的Set-Cookie可以看出,bookHistory=“1,2”,由此说明了ServletB更新了bookHistory,将其值赋为本次访问的书籍id1以及之前访问的书籍id2。这时回到主页刷新下:




  • 3
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值