1.Cookie
Cookie 是一种会话技术。
所谓会话技术,十分简单,就是用来解决这样一个问题:
保存用户在一次浏览器会话中,所产生的的数据。
何为会话?会话可简单理解为:用户开一个浏览器,点击多个超链接,访问同一个服务器多个web资源,然后关闭浏览器,整个过程称之为一个会话。
所谓cookie技术:
1. Cookie是客户端技术,服务器程序把每个用户的数据以cookie的形式写给用户各自的浏览器
2. 当用户使用浏览器再去访问服务器中的web资源时,就会带着各自的数据去(用户之前产生的会话数据)
1.1 Cookie示意图
1.2 Cookie类中的api(新建一个Cookie类,)
Cookie(String name,value) ——构造方法
.get/setValue() ——获得/设置cookie对象的值
.getName() ——获取cookie对象中所存储的键名
非Cookie类中的API:
getCookies() ——返回一个cookie数组,获得一次请求中的所有cookie对象
addCookie(Cookie cookie)——向响应报文中 添加 cookie
所谓的Cookie,对应到代码中,一个cookie信息(就是一个键值对)就对应一个Cookie对象,所以我们要学习Cookie类中的api,从而能够像Cookie中存放在服务器端产生的数据(这个数据最终是保存到浏览器端的)
下面的api 不是Cookie类中的方法,但是它能帮助我们获取浏览器所提交的请求中的cookie信息
下面的api 也不是cookie类中的方法,但是它能帮助我们,将用户在服务器端的数据,以cookie的形式,保存到浏览器端(将cookie写入响应报文中)
1.3 Cookie的简单应用
使用cookie,记录用户上一次登录的时间,并在这一次登录时显示:
@WebServlet(name = "CookieServlet01", urlPatterns = "/servlet/cookie01") public class CookieServlet01 extends HttpServlet { protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("text/html;charset=utf-8"); //利用cookie完成,显示用户上次访问的时间 //1. 如果用户是首次访问,不显示用户上次访问的时间 //2. 如果用户不是首次访问,获取浏览器中的cookie,通过cookie知道用户上次访问的时间,并显示 // lastAccessTime=long类型的时间值 // 设置浏览器的cookie信息,让其保存,本次访问的时间,作为下一次访问servlet的时候, 所显示的上次访问的时间 //1. 获取浏览器,发起请求时,所携带的cookie信息 Cookie[] cookies = request.getCookies(); //遍历cookie数组,看下cookie信息中,有没有我们之前存储的,用户上次访问的时间信息 //如果有,就从cookie中拿出该时间显示, //如果没有,说明,是首次访问,就暂时不显示用户上次访问的时间 //This method returns null if no cookies were sent. if(cookies == null) { //用户的请求数据中,没有携带cookie信息 response.getWriter().write("欢迎您首次访问"); } else { //用户的请求中携带了cookie信息 Cookie co = null; for (Cookie cookie : cookies) { //根据cookie的名称来判断,之前有没有通过cookie保存用户上次访问的时间 if("lastAccessTime".equals(cookie.getName())) { co = cookie; } } if(null == co) { //说明用户首次访问 response.getWriter().write("欢迎您首次访问"); } else { //从cookie中拿出时间来显示 long longTime = Long.parseLong(co.getValue());//此处要记一下API //如果单纯输出这个longTime会的到一串不懂的数字,谁懂?Date懂 //得到这个时间的日期表达形式 Date date = new Date(longTime); response.getWriter().write("您上次访问本网站的时间是:" + date); } } //修改浏览器端,保存的上次访问的时间,因为下次访问servlet的时候,所显示的上次访问的时间, //就是本次访问servlet的时间 Cookie cookie = new Cookie("lastAccessTime", System.currentTimeMillis() + ""); response.addCookie(cookie); } }
1.4 Cookie出现在请求报文中的条件
我们已经知道,一旦javaweb程序,将用户的会话数据,保存到浏览器端,那么浏览器在发起请求的时候,就会自动携带相应的cookie信息。
如果我先在访问天猫,天猫在我的浏览器中保存了一堆cookie, 此时我又去访问京东,
访问京东的时候,在请求报文中,我有必要带着天猫的cookie吗? 没有,而且即使带了,京东很可能也看不懂?
浏览器,在构建http请求报文的时候,究竟带不带某个特定的信息,取决于cookie的domain和path的值
(默认情况下)
domain:localhost 产生这个cookie对象的,servlet所在的服务器的域名
path: /cookie/servlet 就是产生这个cookie对象的servlet,在服务器上的相对路径的父路径,简言之就是servlet的URI,路径中最后一个"/"之前的部分
浏览器究竟如何根据,cookie的domain和path的值决定是否在请求中,携带特定的cookie信息呢?
匹配过程
1. 首先获取请求资源的url:localhost/cookie/servlet/cookie01
2. 把请求的url中的域名和cookie的domain进行匹配,如果发现相同,执行3,如果发现不同,在本次请求中就不携带该cookie
3. 再将请求URL的URI部分,拿出来和cookie的path属性的值,进行匹配,url的uri部分是否包含path,如果包含,本次请求浏览器就携带该cookie信息,如果不包含,就不携带该cookie(包含的意思是)
同时,我们还可以自己设置和获取,cookie的domain和path的值,通过
1.5 Cookie细节
1. 一个Cookie只能标识一种信息,它至少含有一个标识该信息的名称(NAME)和设置值(VALUE)value也必须是字符串类型。
2. 一个WEB站点可以给一个WEB浏览器发送多个Cookie,一个WEB浏览器也可以存储多个WEB站点提供的Cookie。
3. 浏览器一般只允许存放300个Cookie,每个站点最多存放20-50个Cookie,每个Cookie的大小限制为4KB。
4. 如果创建了一个cookie,并将他发送到浏览器,默认情况下它是一个会话级别的cookie(即存储在浏览器的内存中),用户退出浏览器之后即被删除。若希望浏览器将该cookie存储在磁盘上,则需要使用maxAge,并给出一个以秒为单位的时间。将最大时效设为0则是命令浏览器删除该cookie。
5. 注意,删除cookie时,path必须一致,否则不会删除
2.Session
1 Session定义
1. Session是服务器端技术
2. 利用这个技术服务器在运行时可以为每一个用户的浏览器创建一个其独享的HttpSession对象(用来在服务器端,存储用户会话数据)
3. 由于session为用户浏览器独享,所以用户在访问服务器的web资源时,可以把各自的数据放在各自的session中,当用户再去访问服务器中的其它web资源时,其它web资源再从用户各自的session中取出数据为用户服务
在WEB开发中,服务器可以为每个用户浏览器创建一个会话对象(session对象)
注意:数据保存在服务器的Session对象中的内存中
把用户数据写到用户浏览器独占的session中,当前用户使一个浏览器独占一个session对象(默认情况下)。因此,在需要保存用户数据时,服务器程序可以用session保存。同一浏览器可以从用户的session中取出该用户的数据,为用户服务。
浏览器如何在服务器端的多个session对象中找到,自己的session对象呢?
通过JsessionId(session.getValue()可以得到查看)
1.1 Session的api
获取session对象
1.public HttpSession getSession() 《=》 getSession(true)
a. Returns the current session associated with this request(jsessionId),
b. or if the request does not have a session, creates one
2.public HttpSession getSession(boolean create)
1. Returns the current HttpSession associated with this request or,
if if there is no current session and create is true, returns a new session.
2. If create is false and the request has no valid HttpSession, this method returns null
eg. HttpSession session = request.getSession()
—— 这并不意味着session是由客户端产生的,只是这是获取session对象的方式。
1.2 Session的示意图
1.3 session实现原理
从图上我们能够清楚的看到:虽然说Session是服务器端的技术,但是,要用session来保存用户一次会话中的数据,我们仍然需要用到cookie——>向浏览器写入浏览器独享的session对象的JsessionId
2 利用session实现自己的商城
见最后。
3 Session的生命周期
3.1 有关序列化session的细节
序列化session对象,其实不仅仅是将session对象的内容序列化到磁盘上,更是将session对象中所存储的对象网,也存储到磁盘上,所以存储到session中的对象,最好都实现Serializable接口
同时还要注意,如果要让tomcat帮你自动序列化session对象,必须在idea中设置
如果还要访问到,tomcat的默认引用的话,还得
同时如果想要进入tomcat服务器的后台管理界面,还必须配置一个用户名和密码,配置方式为,在你的tomcat服务器中(不是tomcat实例)
在这个文件中添加如下内容,其中tomcat是你配置的用户名,可以改
123456是你配置的密码可以改
其他都不动
配置好之后,点击默认应用首页的Manager App,tomcat会让你登录,此时输入你刚刚配置好的用户名和密码就可以登录了
4 浏览器禁用cookie的解决方案
也就是请求参数不会放到请求报文中了,而是通过URL传输过来:
localhost/servletSession/session?name="zs"&value="1"
eg.
String index = response.encodeURL(request.getContextPath + "/index") (其中主页就是../index)
response.getWriter().println("<a href='"+index+"'>回到主页</a>")
其中有个点,如何拼接标签和内容: <a href='"++"'>,用 ' ' 来分割其中的内容,其中的内容用字符串表示。
购物车的简单功能实现:
1.商品类Product,实现其中的无参构造(bean类创建对象需要)/所有参的构造方法/get和部分set方法(有些不希望被修改)/toString()方法
//要想让session在应用重启,服务器重启等情况下,序列化ssession中的数据,session的对象必须实现 //Serializable接口 public class Product implements Serializable{ //每一个手机商品的编号 private String id; //手机的名称 private String name; //商品的价格 private double price; //运存 以GB为单位 private int ram; //存储 以GB为单位 private int rom; //机身的大小 private double screen; //商品概述 private String description; public Product(String id, String name, double price, int ram, int rom, double screen, String description) { this.id = id; this.name = name; this.price = price; this.ram = ram; this.rom = rom; this.screen = screen; 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 double getPrice() { return price; } public void setPrice(double price) { this.price = price; } public int getRam() { return ram; } public void setRam(int ram) { this.ram = ram; } public int getRom() { return rom; } public void setRom(int rom) { this.rom = rom; } public double getScreen() { return screen; } public void setScreen(double screen) { this.screen = screen; } public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } @Override public String toString() { return "Product{" + "id='" + id + '\'' + ", name='" + name + '\'' + ", price=" + price + ", ram=" + ram + ", rom=" + rom + ", screen=" + screen + ", description='" + description + '\'' + '}'; } }
2.ProductDB
此类用于初始化商品,并且提供对商品的查询功能
public class ProductDB { //商品列表 private static List<Product> products = new ArrayList<>(); //因为只需要在调用一次,不必每次都加载,所以使用静态代码块 static { products.add(new Product("1", "smrtisan 坚果pro3", 1500, 6, 128, 5.7, "文艺青年,装x利器")); products.add(new Product("2", "小米 mix2s", 3500, 4, 128, 5.5, "为发烧而生")); products.add(new Product("3", "HUAWEI", 5800, 6, 256, 5.8, "高端大气上档次")); products.add(new Product("4", "oppo r30", 3000, 6, 256, 5.5, "渠道为王")); products.add(new Product("5", "iphoneX", 8888, 6, 256, 5.7, "装x利器")); } //获取数据库中所有商品信息 public static List<Product> getAllProducts() { return products; } //根据商品id获取数据库中的单个商品信息 public static Product getProductById(String id) { //注意到,商品的id和商品在list中所处的位序,刚好有关系 int index = Integer.parseInt(id) - 1; return products.get(index); } }3.Servletindex 的servlet,此servlet主要是进行:
界面显示 / 动态获取商品 / 为每个商品产生一个详情页的超链接
@WebServlet(name = "ServletIndex", urlPatterns = "/index") public class ServletIndex extends HttpServlet { protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("text/html;charset=utf-8"); PrintWriter writer = response.getWriter(); //返回,请求的应用的url入口 String contextPath = request.getContextPath(); System.out.println("contextpath = " + contextPath); writer.println("<p align=center>" + " <font size=7>京西商城</font>" + "</p>"); String showCart = response.encodeURL(contextPath + "/showCart"); writer.println("<p>" + " <a href='"+ showCart +"'><font size=3>查看购物车</font></a><br>" + "</p>"); String exit = response.encodeURL(contextPath + "/exit"); writer.println("<p>" + " <a href='"+ exit +"'><font size=3>注销</font></a><br>" + "</p>"); writer.println("<hr>"); writer.println("<p>" + " <font size=5>本店商品如下:</font><br>" + "</p>"); //动态产生,商品数据的显示 //获取所有商品信息 List<Product> products = ProductDB.getAllProducts(); //跳转到商品详情的超链接 String productDetail = contextPath + "/detail?id="; //System.out.println(products); //遍历商品列表数据,为每一个商品,动态产生超链接 for (Product product : products) { String url = response.encodeURL(productDetail + product.getId()) ; writer.println("<a href='" +url+ "'>" + product.getName() + "</a><br>"); } } }其中的最后几行中 contextPath+"/detail?id=" + product.getId() 的用意就是,因为是doGet()方法,所以请求参数会通过浏览器的url来传输,通过response.encodeURL(url) 可以解析这个请求参数,得到其中的 id 值。
所以只需要一个urlpatten = “/detail”的servlet来处理就可以了,因为实际他们跳转到的都是/detail这个servlet中
4.商品详情页
此servlet的功能:显示商品详情 / 加入购物车
@WebServlet(name = "ProductDetailServlet", urlPatterns = "/detail") public class ProductDetailServlet extends HttpServlet { protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("text/html;charset=utf-8"); String id = request.getParameter("id");//通过getParamter("id")得到传进来的id Product product = ProductDB.getProductById(id); System.out.println(product); //显示用户所点击的商品的详细信息: PrintWriter writer = response.getWriter(); writer.println("<p>" + " <font size=5>商品的详细信息如下:</font><br>" + "</p>"); writer.println("<hr>"); writer.println(product); String addCart = response.encodeURL(request.getContextPath() + "/addCart?id=" + id); writer.println("<a href= '"+ addCart +"'>加入购物车</a>"); } }
首先要理解getParameter():是用来获取get或post方法提交上来的表单参数值
用法:是从提交的表单数据(有name属性的才能提交上来)中的值,比如:
<form><input type="text" name="user"></form>
此时用request.getParamter("user")会得到这个文本输入框的value值 (如果没有设置的话就是文本框输入的值)
然后就是加入购物车的操作:
记住这种模式: 链接到其他servlet的方式
String addCart = response.encodeURL(request.getContextPath() + "/addCart?id=" + id);
response.getWriter().wirte("<a href='"+addCart+"'>加入购物车</a>");
5.处理加入购物车
此servlet主要是处理加入购物车的操作,并用session对象将其保存起来。
@WebServlet(name = "AddCartServlet", urlPatterns = "/addCart") public class AddCartServlet extends HttpServlet { protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("text/html;charset=utf-8"); String id = request.getParameter("id"); //待加入购物车的商品 Product product = ProductDB.getProductById(id); System.out.println("AddCartServlet :" + product); //将用户准备加入购物车的商品,添加到session中 //1.获取用户之前的session对象(如果有),将购物数据,追加到之前在session存储的,用户的购物列表中国 //2.假设用户,第一次购物,还没有为他创建一个session // a. 为用户创建session // b. 在session中为用户创建购物列表 // c. 将带加入购物车的商品,追加到购物列表中 //获取用户之前创建的session HttpSession session = request.getSession(false); if(null == session) { //该用户,要向购物车中,添加商品,但是,还没有为该用户创建session对象 //为该用户创建ssession session = request.getSession(); //为用户创建购物列表,并且将该购物列表,放入session中 ArrayList<Product> products = new ArrayList<>(); session.setAttribute("products", products); //将用户购买的商品,追加到用户的商品列表中 products.add(product); } else { //用户之前,在服务器上已经有了一个session对象和他对应 List<Product> products = (List<Product>) session.getAttribute("products"); if(products == null) { //说明,用户有session但是还没有,购物 products = new ArrayList<>(); session.setAttribute("products", products); } //将商品放入用户的购物列表中 products.add(product); } //给用户一个响应,告诉用户,商品在购物车中添加成功 PrintWriter writer = response.getWriter(); writer.println("恭喜您,商品添加成功"); String showCart = response.encodeURL(request.getContextPath() + "/showCart"); writer.println("<a href='"+ showCart +"'>查看购物车</a>"); } }
6.购物车
此 servlet用于显示用户放入其中的商品
@WebServlet(name = "ShowCartServlet", urlPatterns = "/showCart") public class ShowCartServlet extends HttpServlet { protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("text/html;charset=utf-8"); //1.获取用户本次会话的session对象 //2.从session中拿出用户的购物列表,遍历显示 HttpSession session = request.getSession(false); PrintWriter writer = response.getWriter(); writer.println("<p>" + " <font size=5>您购买的商品如下:</font><br>" + "</p>"); writer.write("<hr>"); if(session == null) { writer.println("您还有没购物信息,赶紧开始购物吧!"); } else { //用户有一个session对象,来保存数据 List<Product> products = (List<Product>) session.getAttribute("products"); if(products == null) { writer.println("您还有没购物信息,赶紧开始购物吧!"); } else { //用户的商品列表不为空,遍历用户的商品列表,并显示在客户端 for (Product product : products) { writer.write("Product: " + product.getName() + "<br>"); } } } String index = response.encodeURL(request.getContextPath() + "/index"); writer.println("<a href='" + index + "'>回到主页继续购物</a>"); } }
7.注销
可以让用户在退出商城之前销毁自己的session数据。
@WebServlet(name = "ExitServlet", urlPatterns = "/exit") public class ExitServlet extends HttpServlet { protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("text/html;charset=utf-8"); //1.获取用户的session对象 //2. session.invalidate() HttpSession session = request.getSession(false); if(session != null) { //销毁session中的信息,使session无效 session.invalidate(); } response.getWriter().write("注销成功,3s后自动跳转到主页"); response.setHeader("Refresh", "3;/session/index"); } }注意两点:
1.session.invalidate()方法:专用用来清理session,而不用session.removeAttribute()方法
2.response.setHeader("Refresh","3;/...") :可以设置响应报文的响应头来 设置 刷新跳转功能(3秒后跳转到哪)