05Cookie&Session
二、会话技术
(一)、会话技术
浏览器开始访问网站到访问网站结束期间产生的多次请求响应组合在一起叫做一次会话
会话的过程中会产生会话相关的数据,我们需要将这些数据保存起来。
1、什么是会话?
会话可简单理解为:用户开一个浏览器,点击多个超链接,访问服务器多个web资源,然后关闭浏览器,整个过程称之为一个会话。
2、会话过程中要解决的一些问题?
每个用户在使用浏览器与服务器进行会话的过程中,不可避免各自会产生一些数据,程序要想办法为每个用户保存这些数据。
例如:用户点击超链接通过一个servlet购买了一个商品,程序应该想办法保存用户购买的商品,以便于用户点结帐servlet时,结帐servlet可以得到用户购买的商品为用户结帐。
思考:用户购买的商品保存在request(域太小)或servletContext(域太大)中行不行?不行。
3、保存会话数据的两种技术:Cookie技术和Session技术
Cookie:客户端技术 Session:服务器端技术
(二)、Cookie技术:客户端技术
Cookie是客户端技术,程序把每个用户的数据以cookie的形式写给用户各自的浏览器。当用户使用浏览器再去访问服务器中的web资源时,就会带着各自的数据去。这样,web资源处理的就是用户各自的数据了。
1、Cookie是基于set-Cookie响应头和Cookie请求头工作的,服务器可以发送set-Cookie请求头命令浏览器保存一个cookie信息,浏览器会在访问服务器时以Cookie请求头的方式带回之前保存的信息。
HTTP的一个响应头“Set-Cookie:SS=Q0=5Lb_nQ; path=/search”通知浏览器保存cookie信息,浏览器怎么带回来的?HTTP的一个请求头:“Cookie”打给服务器。

2、javax.servlet.http.Cookie类用于创建一个Cookie,response接口也中定义了一个addCookie方法,它用于在其响应头中增加一个相应的Set-Cookie头字段。 同样,request接口中也定义了一个getCookies方法,它用于获取客户端提交的Cookie。Cookie类的方法:
public Cookie(String name,String value)
setValue与getValue方法
getName方法
request.getCookies(); response.addCookie(Cookie c);
new Cookie(String name,String value)//Cookie在构造的时候就需要设定好cookie的名字和值
getName(); getValue(); setValue();
@WebServlet("/CookieDemo1")public void doGet(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {//response.setHeader("Set-Cookie", "name=dianbingxiang");//原来做法//显示用户上次访问的时间//Date date = new Date();//Cookie c = new Cookie("lastTime", date.getTime()+"");//构造时设置好name值//name不可以更改,只能新建一个Cookie//response.addCookie(c);//httpwatch:Stream的请求中:Set-Cookie: lastTime=12638939393//第二次访问时,Stream的响应中:返回的一个响应Cookie:JSESSIONID=C14.....改为://Cookie:lastTime=12638939393; JSESSIONID=C14.....//解决中文乱码response.setCharacterEncoding("UTF-8");response.setContentType("text/html;charset=UTF-8");//返回多个cookie信息Cookie[] cs = request.getCookies();Cookie findC = null;//cs部位空,才可以增强for循环if (cs != null)for(Cookie c : cs){if ("lastTime".equals(c.getName())) {findC = c;}- }
if (findC == null) {response.getWriter().write("这是您第一次访问本网站!");} else {Long lastTime = Long.parseLong(findC.getValue());response.getWriter().write("您上次访问的时间是:" + new Date(lastTime).toLocaleString());}//缓存最新时间Date date = new Date();Cookie cTime = new Cookie("lastTime", date.getTime()+"");response.addCookie(cTime);}

浏览器关闭后,重新打开,不会显示关闭前的缓冲。
3、其他方法:
setMaxAge与getMaxAge方法
setPath与getPath方法
setDomain与getDomain方法 .google.com
( 1)、 !!setMaxAge与getMaxAge方法
==一个Cookie如果没有设置过MaxAge则这个Cookie是一个会话级别的Cookie,这个Cookie信息打给浏览器后浏览器会将它保存在浏览器的内存中,这意味着只要浏览器已关闭随着浏览器内存的销毁Cookie信息也就消失了。
==一个Cookie也可以设置MaxAge,浏览器一旦发现收到的Cookie被设置了MaxAge,则会将这个Cookie信息以文件的形式保存在浏览器的临时文件夹中,保存到指定的时间到来位置。这样一来即使多次开关浏览器,由于这些浏览器都能在临时文件夹中看到cookie文件,所以在cookie失效之前cookie信息都存在.
==想要命令浏览器删除一个Cookie,发送一个同名同path的cookie,MaxAge设置为0,浏览器以名字+path识别cookie,发现同名同path,cookie覆盖后立即超时被删除,从而就删除了cookie。
(2)、!!setPath与getPath方法
==用来通知浏览器在访问服务器中的哪个路径及其子路径时带着当前cookie信息过来
==如果不明确设置,则默认的路径是发送Cookie的Servlet所在的路径
http://localhost/Day05/(servlet/)...
(3)、setDomain与getDomain方法
==用来通知浏览器在访问哪个域名的时候带着当前的cookie信息。但是要注意,现代的浏览器一旦发现cookie设置过domain信息则会拒绝接受这个Cookie。我们平常不要设置这个方法
//缓存最新时间Date date = new Date();Cookie cTime = new Cookie("lastTime", date.getTime()+"");//设置缓存有效时间cTime.setMaxAge(3600*24*30);//以秒为单位//本来输入http://localhost/Day05/时,响应不携带上述Cookies信息//加入下面一句,也可以是上述链接携带cookie信息cTime.setPath(request.getContextPath());//虚拟路径,web应用名称response.addCookie(cTime);
4、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的name+path来标识一个cookie)
4、 !案例:曾经看过的书
/*** JavaBean是指一段特殊的Java类,* 就是有默然构造方法,只有get,set的方法的java类的对象* JavaBean类,最好实现Serializable接口* JavaBean定义了一组规则 ,JavaBean就是遵循此规则的平常的Java对象* 满足这三个条件:* 1.执行java.io.Serializable 接口;* 2.提供无参数的构造器* 3.提供getter 和 setter方法访问它的属性.* @author angel11288**/private String id;private String name;private String price;private String auther;private String publish;private String description;
2、建一个工具类:
package com.lmd.cookie;import java.util.LinkedHashMap;import java.util.Map;import com.lmd.domain.Book;/*** 私有构造器的存在可以让某些类不能被实例化和子类化,* 这些类通常是一些工具类* @author angel11288**/public class BookDao {private static Map<String, Book> bookMap = new LinkedHashMap<String, Book>();//私有构造函数的目的:无法被类以外的函数使用,只能通过调用来实现。private BookDao() {// TODO Auto-generated constructor stub}static{bookMap.put("1", new Book("1", "疯狂Java讲义", "88.0", "lily", "JAVA", "学习JAVA基础的书"));bookMap.put("2", new Book("2", "JAVA程序员面试宝典", "46.0", "jake", "JAVA面试", "JAVA面试必看的书"));bookMap.put("3", new Book("3", "面向对象", "36.0", "lucy", "面向对象Object", "面向对象基础"));bookMap.put("4", new Book("4", "JAVA编程思想", "100.0", "tom", "黑马", "很不错的书,JAVA基础"));}public static Map<String, Book> getBooks() {return bookMap;}public static Book getBook(String id) {return bookMap.get(id);}}
3、
@WebServlet("/BookListServlet")public class BookListServlet extends HttpServlet {public void doGet(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {response.setContentType("text/html;charset=UTF-8");//1、查询数据库中所有的书Map<String, Book> map = BookDao.getBooks();for (Map.Entry<String, Book> entry : map.entrySet()) {Book book = entry.getValue();response.getWriter().write("<a href='"+request.getContextPath()+"/BookInfoServlet?id="+book.getId()+"'>" + book.getName() + "</a><br/>");}response.getWriter().write("<hr>");//2、显示之前看过的书Cookie[] cs = request.getCookies();Cookie findC = null;if(cs != null){for (Cookie c : cs) {if ("last".equals(c.getName())) {findC = c;}}}if (findC == null) {response.getWriter().write("没有看过的任何书!<br>");}else {//String id = findC.getValue();//Book book = BookDao.getBook(id);//response.getWriter().write("您之前看过的书:<br>");//response.getWriter().write(book.getName());response.getWriter().write("您之前看过的书:<br>");String[] ids = findC.getValue().split(",");for (String id : ids) {Book book = BookDao.getBook(id);response.getWriter().write(book.getName()+"<br/>");}}}public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {// TODO Auto-generated method stubdoGet(request, response);}}
4、
@WebServlet("/BookInfoServlet")public class BookInfoServlet extends HttpServlet {public void doGet(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {response.setContentType("text/html;charset=UTF-8");//1、获取要看书的id,查询数据库找出书,输出书的信息String id = request.getParameter("id");Book book = BookDao.getBook(id);if (book == null) {response.getWriter().write("找不到这本书!");return;}else {response.getWriter().write("<h1>书名:"+book.getName()+"</h1><br/>");response.getWriter().write("<h3>售价:"+book.getPrice()+"</h3><br/>");response.getWriter().write("<h3>作者:"+book.getAuther()+"</h3><br/>");response.getWriter().write("<h3>出版社:"+book.getPublish()+"</h3><br/>");response.getWriter().write("<h3>描述信息:"+book.getDescription()+"</h3><br/>");}//2、发送cookie保存最后看过的书:3本//--> 1 -->一样不加1//1-->2,1-->不一样加上2,1//2,1--3,2,1-->3,2,1//3,2,1--4,3,2-->4,3,2String ids = null;Cookie[] cs = request.getCookies();Cookie findC = null;if (cs != null) {for (Cookie c : cs) {if ("last".equals(c.getName())) {findC = c;}}}if (findC == null) {//说明之前没有看过书的记录ids += book.getId();}else {//说明之前有历史看过的书记录,需要根据历史计算一个新的记录出来String[] oIds = findC.getValue().split(",");StringBuffer sb = new StringBuffer();sb.append(book.getId()+",");for (int i = 0; i < oIds.length && sb.toString().split(",").length<3; i++) {String old = oIds[i];if (!old.equals(book.getId())) {sb.append(old + ",");}}ids = sb.substring(0, sb.length() - 1);}Cookie lastC = new Cookie("last", ids);lastC.setMaxAge(3600*24*2);lastC.setPath(request.getContextPath());response.addCookie(lastC);}public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {doGet(request, response);}}
(二)、Session技术:服务器端技术
Session是服务器端技术,利用这个技术,服务器在运行时可以为每一个用户的浏览器创建一个其独享的session对象,由于session为用户浏览器独享,所以用户在访问服务器的web资源时,可以把各自的数据放在各自的session中,当用户再去访问服务器中的其它web资源时,其它web资源再从用户各自的session中取出数据为用户服务。
2、Session:服务器端技术
(1)、在WEB开发中,服务器可以为每个用户浏览器创建一个会话对象(session对象),注意:一个浏览器独占一个session对象(默认情况下)。因此,在需要保存用户数据时,服务器程序可以把用户数据写到用户浏览器独占的session中,当用户使用浏览器访问其它程序时,其它程序可以从用户的session中取出该用户的数据,为用户服务。 (2)、session是一个域对象,作用范围为整个会话。
(3)、Session和Cookie的主要区别在于:
Cookie是把用户的数据写给用户的浏览器。
Session技术把用户的数据写到用户独占的session中。
Session 是一个域
!!作用范围:当前会话范围
!!生命周期:当程序第一次调用到request.getSession()方法时说明客户端明确的需要用到session此时创建出对应客户端的Session对象。(Session死亡方式)
1、当session超过30分钟(这个时间是可以在web.xml文件中进行修改的),没有人使用则认为session超时销毁这个session。
<session-config><session-timeout>30</session-timeout></session-config>
2、程序中明确的调用session.invalidate()方法可以立即杀死session。
3、当服务器被非正常关闭时,随着虚拟机的死亡而死亡。(点击console中的红色按钮,JDK挂掉)
**如果服务器是正常关闭,还未超时的session会被以文件的形式保存在服务器的work目录下,这个过程叫做session的钝化。下次再正常启动服务器时,钝化着的session会被恢复到内存中,这个过程叫做session的活化.
(点击stop,F:\tomcat8\work\Catalina\localhost\Day05下出现一个SESSIONS.ser 文件)
如果一个对象被保存到session域中,这个对象必须实现Serializable接口才可以随着session钝化。
!!作用:在会话范围内共享数据。
使用Session完成简单的购物功能:
@WebServlet("/BuyServlet")public void doGet(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {//只用于解决POST请求的中文乱码,针对实体内容//request.setCharacterEncoding("UTF-8");//放在getParameter前才有效//1、获取请求参数String prod = request.getParameter("prod");//GET解决中文乱码,设置tomcat Connector URIEncoding=“utf-8”prod = new String(prod.getBytes("ISO-8859-1"),"UTF-8");//获取Sessionrequest.getSession().setAttribute("prod", prod);}
@WebServlet("/PayServlet")public void doGet(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {response.setCharacterEncoding("UTF-8");response.setContentType("text/html; charset=UTF-8");//获取SessionHttpSession session = request.getSession();String prod = (String) session.getAttribute("prod");response.getWriter().write("您购买的是" + prod + ",价值99999元!");}
<%@ page language="java" import="java.util.*" contentType="text/html; charset=UTF-8"%><%String path = request.getContextPath();String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";%><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"><html><head><title>ProdList</title></head><body><a href="<%=request.getContextPath() %>/BuyServlet?prod=电视机">电视机</a><br><a href="<%=request.getContextPath() %>/BuyServlet?prod=电冰箱">电冰箱</a><br><a href="<%=request.getContextPath() %>/PayServlet">结账</a><br></body></html>



关闭浏览器后,再打开,没有之前的session,那么怎么实现多个IE浏览器共享同一session?
//创建Session,存数据//request.getSession().setAttribute("prod", prod);//改为:实现多个IE浏览器共享同一sessionHttpSession session = request.getSession();Cookie jc = new Cookie("JESSIONID", session.getId());jc.setPath(request.getContextPath());jc.setMaxAge(1800);//30分钟response.addCookie(jc);session.setAttribute("prod", prod);
(1)、在简单购物案例中,由于一个浏览器对应一个Session,所以当我们重新开一个浏览器时,是无法找到在上一个浏览器中购买的商品的,这在电子商务中是无法接受的,其实我们可以利用session的原理巧妙的使同一台机器内的多个浏览器共用一个session
(2)、由于服务器识别浏览器是通过Jsessionid这个特殊的cookie进行的,我们可以手动设置该cookie的setMaxAge,使这个cookie被保存在硬盘中被多个浏览器公用,从而使同一台机器上的多个浏览器公用一个session。(注意设置cookie值时path也要相同)。
!!session 的原理:
(1)、request.getSession()方法会检查请求中有没有JSESSIONID cookie,如果有拿出他的值找到对应的session为他服务。
(2)、如果没有,则检查请求的URL后有没有以参数的形式带着JSESSIONID过来,如果有,则找到对应的Session为浏览器服务器。
(3)、如果还找不到,则认为这个浏览器没有对应的Session,创建一个Session然后再在响应中添加JSESSIONID cookie,值就是这个Session 的id。
默认情况下,JSESSIONID 的path为当前web应用的名称,并且没有设置过MaxAge,是一个会话级别的cookie。
这意味着一旦关闭浏览器再新开浏览器时,由于JSESSIONID丢失,会找不到之前的Session。
我们可以手动的发送JSESSIONID cookie,名字和path设置的和自动发送时一样,但是设置一下MaxAge,使浏览器除了在内存中保存JSESSIONID信息以外,还在临时文件夹中以文件的形式保存,这样即使重开浏览器仍然可以使用之前的session。
但若将IE中Internet选项--》隐私--》高级中禁用cookie,则上述案例无法付款,于是提出一种解决方案——URL重写
URL重写:
(1)、如果浏览器禁用了Cookie,浏览器就没有办法JSESSIONID cookie,这样就用不了Session了。
(2)、我们可以使用URL重写的机制,在所有的超链接后都以参数的形式拼接JSESSIONID信息,从而在点击超链接时可以使用URL参数的方式待会JSESSIONID,从而使用Session。
将URL进行重写拼接上JSESSIONID的过程就叫做URL重写
request.getSession() --在URL重写之前一定要先创建出Session,才有Session id,才能进行重写;用于对表单action和超链接的url地址进行重写。
response.encodeURL()--- 一般的地址都用这个方法重写;
response.encodeRedirectURL() --- 如果地址是用来进行重定向的则使用这个方法;用于对sendRedirect方法后的url地址进行重写。
**url重写的方法一旦发现浏览器带回了任意cookie信息,则认为客户端没有禁用cookie,就不会再进行重写操作。
<%request.getSession();String url1 = request.getContextPath() + "/BuyServlet?prod=电视机";response.encodeURL(url1);String url2 = request.getContextPath() + "/BuyServlet?prod=电冰箱";response.encodeURL(url2);String url3 = request.getContextPath() + "/PayServlet";response.encodeURL(url3);%><a href="<%=url1%>">电视机</a><br><a href="<%=url2%>">电冰箱</a><br><a href="<%=url3%>">结账</a><br>

下面整理一下getParameter和getAttribute的区别和各自的使用范围。
(1)HttpServletRequest类有setAttribute()方法,而没有setParameter()方法
(2)getParameter()用于用于客户端重定向时,即当两个Web组件之间为链接关系时,被链接的组件通过getParameter()方法来获得请求参数,例如假定welcome.jsp和authenticate.jsp之间为链接关系,welcome.jsp中有以下代码:
authenticate.jsp: 请输入用户姓名: 提交
在authenticate.jsp中通过request.getParameter("username")方法来获得请求参数username:
<% String username=request.getParameter("username"); %>
假定 authenticate.jsp和hello.jsp之间为转发关系。authenticate.jsp希望向hello.jsp传递当前的用户名字, 如何传递这一数据呢?先在authenticate.jsp中调用setAttribute()方法:
<%String username=request.getParameter("username");request.setAttribute("username",username);%>
<% String username=(String)request.getAttribute("username"); %>Hello: <%=username %>
request.setAttribute()和getAttribute()方法传递的数据只会存在于Web容器内部,在具有转发关系的Web组件之间共享。这两个方法能够设置Object类型的共享数据。
request.getParameter()取得是通过容器的实现来取得通过类似post,get等方式传入的数据。
request.setAttribute()和getAttribute()只是在web容器内部流转,仅仅是请求处理阶段。
getAttribute是返回对象,getParameter返回字符串
总的来说:request.getAttribute()方法返回request,sessiont范围内存在的对象,而request.getParameter()方法是获取http提交过来的数据。
cookie是客户端技术
优点:数据保存在客户端,这个信息可以保存很长时间。
缺点:数据随时有可能被清空,所以cookie保存的数据是不太靠谱的;
数据被保存在了客户端,随时有可能被人看走,如果将一些敏感信息比如用户名密码等信息存在cookie中,可能有安全问题。
session是服务器端技术
优点:数据保存在服务区端,相对来说比较稳定和安全。
缺点:占用服务器内存,所以一般存活的时间不会太长,超过超时时间就会被销毁。所以 我们要根据服务器的压力和session 的使用情况合理设置session的超时时间,既能保证session的存活时间够用,同时不用的session可以及时销毁减少对服务器内存的占用。
Session案例:
!!用户登录注销
防止表单重复提交
实现一次性验证码
(一)用户登录注销
1、开始界面
<%@ page language="java" import="java.util.*" contentType="text/html;charset=UTF-8"%><%String path = request.getContextPath();String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";%><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"><html><head><title>开始界面</title></head><body><h1>我的网站</h1><%//获取Session中的登录状态String user = (String) session.getAttribute("user");%><%if(user == null || "".equals(user)){//用户已登录%>欢迎光临!游客!<a href="${ pageContext.request.contextPath}/loginout/login.jsp">登录</a><a href="#">注册</a><%} else {//用户未登录%>欢迎回来!<%=user %>!<a href="${ pageContext.request.contextPath}/LogoutServlet">注销</a><%}%></body></html>
2、用户名数据库:
package com.lmd.loginout;import java.util.HashMap;import java.util.Map;public class UserDao {//私有构造函数的目的:无法被类以外的函数使用,只能通过调用来实现。private UserDao(){}private static Map<String, String> map = new HashMap<String, String>();static{map.put("lily", "111");map.put("lucy", "222");map.put("jake", "333");map.put("tom", "444");}public static boolean valiNamePsw(String username, String password) {return map.containsKey(username)&& map.get(username).equals(password);}}
3、登录界面
package com.lmd.loginout;import java.io.IOException;import javax.servlet.ServletException;import javax.servlet.annotation.WebServlet;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;/*** Servlet implementation class LoginServlet*/@WebServlet("/LoginServlet")public class LoginServlet extends HttpServlet {public void doGet(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {//post提交,解决中文乱码request.setCharacterEncoding("UTF-8");response.setContentType("text/html;charset=UTF-8");//1、获取用户名和密码String username = request.getParameter("user");String password = request.getParameter("password");//2、查询数据库,检查用户名和密码if (UserDao.valiNamePsw(username, password)) {//3、如果正确重定向到登录界面request.getSession().setAttribute("user", username);response.sendRedirect(request.getContextPath()+"/loginout/index.jsp");return; //重定向后防止后续代码的执行} else {//4、如果错误,提示response.getWriter().write("用户名或密码不正确!");}}protected void doPost(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {doGet(request, response);}}
4、注销界面
package com.lmd.loginout;/*** Servlet implementation class LogoutServlet*/@WebServlet("/LogoutServlet")public class LogoutServlet extends HttpServlet {public void doGet(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {//1、杀死Session//false to return null if there's no current sessionif (request.getSession(false) != null|| request.getSession().getAttribute("user") != null) {request.getSession().invalidate();}//2、重定向到主页response.sendRedirect(request.getContextPath()+"/loginout/index.jsp");}protected void doPost(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {doGet(request, response);}}

单击登录:


单击注销,回到开始界面。
(二)防止表单重复提交
<%@ page language="java" import="java.util.*" contentType="text/html;charset=UTF-8"%><%String path = request.getContextPath();String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";%><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"><html><head><title>防止表单重复提交</title></head><body><form action="${ pageContext.request.contextPath}/ResubmitServlet" method="post">用户名:<input type="text" name="username" /><br><input type="submit" value="注册" /></form></body>
package com.lmd.resubmit;/*** Servlet防止表单重复提交*/@WebServlet("/ResubmitServlet")public class ResubmitServlet extends HttpServlet {public void doGet(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {request.setCharacterEncoding("UTF-8");//模拟网络延迟try {Thread.sleep(4*1000);} catch (InterruptedException e) {e.printStackTrace();}String username = request.getParameter("username");System.out.println("向数据库中注册一次:" + username);//注册时,发生网络延迟;点击多次的时候,后台会输出多句}- }

注册时,发生网络延迟;点击多次的时候,后台会输出多句——怎么解决?
加入onsubmit="return canSub()" 防止表单重复提交
<html><head><title>防止表单重复提交</title><script type="text/javascript">var isNotSub = true;function canSub(){if(isNotSub){isNotSub = false;return true;} else {alert("请不要重复提交!!!");return false;//阻止提交}}</script></head><body><form action="${ pageContext.request.contextPath}/ResubmitServlet" method="post" onsubmit="return canSub()">用户名:<input type="text" name="username" /><br><input type="submit" value="注册" /></form></body></html>
多次点击注册,仅仅提交一次。但是JavaScript不太靠谱(前台校验不靠谱,客户端执行),如果用户禁用JavaScript,就不可行了;若本网页保存为文件,文件易被改变,造成不好的结果。
前台校验只是给用户一个友好的体验,那么需要后台校验。
(三)实现一次性验证码
后台校验,防止后台提交
@WebServlet("/ResubmitServlet")public void doGet(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {request.setCharacterEncoding("UTF-8");response.setContentType("text/html;charset=UTF-8");//模拟网络延迟try {Thread.sleep(4*1000);} catch (InterruptedException e) {e.printStackTrace();}String username = request.getParameter("username");//用户提交的String valiNum = request.getParameter("valiNum");//session当中发过去的String valiNum2 = (String) request.getSession().getAttribute("valiNum");if (valiNum2 != null && !"".equals(valiNum2) && valiNum.equals(valiNum2)) {request.getSession().removeAttribute("valiNum");System.out.println("向数据库中注册一次:" + username);} else {response.getWriter().write("form web:不要重复提交!");}}
<body><%Random r = new Random();int valiNum = r.nextInt();session.setAttribute("valiNum", valiNum);%><form action="${ pageContext.request.contextPath}/ResubmitServlet" method="post">用户名:<input type="text" name="username" /><br><input type="hidden" name="valiNum" value="<%=valiNum %>" /><input type="submit" value="注册" /></form></body></html>
16.考点:HttpSession
Person 类:
public class Person {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Servlet1:
request.getSession().setAttribute("name","zhangsan");
Person p = new Person();
p.setName("lisi");
request.getSession().setAttribute("person",p);
Servlet2:
String name1 = (String)request.getSession().getAttribute("name");
Person p = (Person)request.getSession().getAttribute("person");
response.getWriter().write(name1);
if(p!=null)response.getWriter().write(p.getName());
浏览器先访问Servlet1,则浏览器再去访问Servlet2,应该看到的输出是:(A)(难度A)
A.zhangsanlisi
B.zhangsan
C.lisi
D.服务器重启时报错




浙公网安备 33010602011771号