Cookie&Session
1.重定向和请求转发
- 重定向
之前的写法:
//设置状态码,302表示临时重定向,常用于页面跳转
resp.setStatus(302);
//Location为固定写法,后面为要跳转的页面
resp.setHeader("Location","login_success.html");
重定向写法:
resp.sendRedirect("login_success.html");
地址栏上显示的是最后的那个资源的路径地址;
请求次数最少有两次, 服务器在第一次请求后,会返回302 以及一个地址,浏览器在根据这个地址,执行第二次访问;
可以跳转到任意路径,不是自己的工程也可以跳;
效率稍低,因为执行两次请求;
后续的请求,不能使用上一次的request存储的数据,或者说不能使用上一次的request,因为是两次不同的请求。
- 请求转发
req.getRequestDispatcher("login_success.html").forward(req,resp);
地址上显示的是请求servlet的地址,返回200 ok;
请求次数只有一次,因为是服务器内部帮客户端执行后续的工作;
只能跳转自己项目的资源路径;
效率稍高,因为只执行一次请求;
可以使用上一次的request对象。
2.Cookie
饼干,其实是一份小数据,是服务器给客户端,并且存储在客户端上的一份小数据。
应用场景:自动登录、浏览记录、购物车。
Cookie分类:
-
会话Cookie
默认情况下关闭浏览器Cookie就会消失。
-
持久Cookie
在一定时间内都有效,并会保存到客户端上。
cookie.setMaxAge(60);//设置Cookie有效期60秒。
2.1为什么要有Cookie
http的请求是无状态。 客户端与服务器在通讯的时候,是无状态的,其实就是客户端在第二次来访的时候,服务器根本就不知道这个客户端以前有没有来访问过。 为了更好的用户体验,更好的交互(自动登录),其实从公司层面讲,就是为了更好的收集用户习惯(大数据)。
2.2Cookie简单使用
- 添加Cookie给客户端
- 在响应的时候添加Cookie
Cookie cookie=new Cookie("aa","bb");
resp.addCookie(cookie);
- 客户端收到的信息里面,响应头中多了一个字段 Set-Cookie
- 获取客户端带过来的Cookie
Cookie[] cookies = req.getCookies();
for(Cookie c:cookies){
System.out.println(c.getName()+c.getValue());
}
- 常用方法
//设置cookie有效期为7天,有效期以秒计算
cookie.setMaxAge(60*60*24*7);
//给cookie赋新的值
cookie.setValue("lisi");
/*
设置只有请求了指定域名时才带上cookie,即当访问.itheima.com或其子域名如map.itheima.com或再下一级域名的时候都会使用该cookie。
注意:
浏览器不会接收domain为com.cn的cookie,娜样互联网就乱套了;
同一个域不能有同key的Cookie;
只要是为cookie显式的声明domain,前面带不带"."没有区别。
*/
cookie.setDomain(".itheima.com");
//设置当访问该域名下的cookieDemo这个路径时才会携带cookie
cookie.setPath("/CookieDemo");
2.3例1:显示最近登录的时间
@WebServlet("/CookieDemoServlet")
//相当于web.xml里注册Servlet的那两段
public class CookieDemoServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//避免响应中文时的乱码问题
response.setContentType("text/html;charset=UTF-8");
//1.判断账号密码是否正确
String username = request.getParameter("username");
String password = request.getParameter("password");
Cookie loginTimeCookie=null;
PrintWriter pw=response.getWriter();
if(username.equals("abc")&&password.equals("123")){
//2. 如果正确,则获取登录时间的cookie
Cookie[] cookies = request.getCookies();
if (cookies!=null){
for (Cookie c:cookies){
if(c.getName().equals("loginTime")){
System.out.println("1");
loginTimeCookie=c;
//3.如果找到的对象不为空, 表明不是第一次登录
pw.print("最近登录时间为:"+ URLDecoder.decode(loginTimeCookie.getValue(),"UTF-8"));
//更新最近登录时间
String loginTime=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());
loginTime=URLEncoder.encode(loginTime,"UTF-8");
loginTimeCookie.setValue(loginTime);
//记得要重新添加回去
response.addCookie(loginTimeCookie);
}
}
}else if(cookies==null||loginTimeCookie==null){
//4.如果cookies为空或者loginTimeCookie为空,表明是第一次登录,那么要添加cookie
System.out.println("2");
String loginTime=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());
loginTime=URLEncoder.encode(loginTime,"UTF-8");
response.addCookie(new Cookie("loginTime",loginTime));
}
}else{
pw.write("登录名或密码有误");
}
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request,response);
}
}
注意:
- 设置loginTimeCookie的value时
在设置loginTimeCookie的value时,如果直接:
String loginTime=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());
loginTimeCookie.setValue(loginTime);
会出现如下错误:
java.lang.IllegalArgumentException: An invalid character [32] was present in the Cookie value
原因是时间格式"yyyy-MM-dd HH:mm:ss"中有空格,所以会报错。可以直接删除或替换空格,或者也可以:
String loginTime=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());
loginTime=URLEncoder.encode(loginTime,"UTF-8");
loginTimeCookie.setValue(loginTime);
- 输出上次登录时间时
输入上次登录时间时,如果直接:
pw.print("最近登录时间为:"+ loginTimeCookie.getValue());
会出现以下错误:
浏览器上显示的为:最近登录时间为:2020-10-20+10%3A26%3A49
解决方法:
pw.print("最近登录时间为:"+ URLDecoder.decode(loginTimeCookie.getValue(),"UTF-8"));
2.3例2:显示商品浏览记录
2.3.1JSP
jsp,即Java Server Pager,可以再jsp里面使用Java代码,最终会翻译成一个类,就是一个Servlet。
- 定义局部变量
<%! int a=99;%>
- 定义全局变量
<% int b = 999; %>
//Java代码写在<%%>中
- 在jsp页面显示a和b的值
<% =a %>
<% =b %>
2.3.2ProductInfoServlet
@WebServlet("/ProductInfoServlet")
public class ProductInfoServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String id = request.getParameter("id");
Cookie[] cookies = request.getCookies();
Cookie historyCookie = CookieUtils.findCookie(cookies, "history");
if(historyCookie==null){
//证明是第一次来
historyCookie=new Cookie("history",id);
}else{
//不是第一次来
//不是第一次来,拼接以前的cookie
String ago = historyCookie.getValue();
historyCookie.setValue(id+"#"+ago);
}
historyCookie.setMaxAge(60*60*7*24);
response.addCookie(historyCookie);
System.out.println(historyCookie.getValue());
response.sendRedirect("product_info.htm");
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request,response);
}
}
2.3.3Product_list.jsp展示浏览记录
<ul style="list-style: none;">
<%
Cookie[] cookies=request.getCookies();
Cookie historyCookie = CookieUtils.findCookie(cookies, "history");
if(historyCookie==null){
%>
<h2>您暂未浏览任何商品</h2>
<%
}else{
String[] ids = historyCookie.getValue().split("#");
for (String id:ids){
%>
<img src="products/1/cs1000<%=id%>.jpg" width="130px" height="130px" />
<%
}
}
%>
</ul>
2.3.4清除浏览记录
其实就是清除Cookie,删除cookie是没有什么delete方法的,只有设置maxAge为0。
- ClearCookieServlet
@WebServlet("/ClearCookieServlet")
public class ClearCookieServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request,response);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//新建一个同名但为空的Cookie覆盖之前的Cookie
Cookie cookie = new Cookie("history","");
//删除Cookie其实就是把生命周期设置为0
cookie.setMaxAge(0);
//记得再重新添加回去
response.addCookie(cookie);
response.sendRedirect("product_list.jsp");
}
}
3.Session
由于Cookie会保存在客户端上,所以有安全隐患问题。另外Cookie的大小和个数有限制。Session可有效解决这个问题。
Session,即会话,是基于Cookie的一种会话机制。 Cookie是服务器返回一小份数据给客户端,并且存放在客户端上。Session是,数据存放在服务端。
3.1Session创建与销毁,常见API
-
创建
当在Servlet里调用request.getSession()。
-
销毁
- 关闭服务器;
- Session会话时间过期,默认是30分钟。
-
常见API
HttpSession session = request.getSession(); //得到会话ID String id = session.getId(); //存值 session.setAttribute("name","zhangsan"); //取值 String name = (String) session.getAttribute("name"); //移除值 session.removeAttribute("name"); //销毁Session session.invalidate();
3.2例1:简单购物车
- CartServlet
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//1.获取要添加的商品id
Integer id = Integer.parseInt(request.getParameter("id"));
String[] products={"小米8","魅族16th","华为P30"};
String product=products[id];
//2.存储到购物车
HttpSession session = request.getSession();
Map<String,Integer> cartMap = (Map<String,Integer>)session.getAttribute("cart");
if(cartMap==null){
//即没有名为cart的session
session.setAttribute("cart",new HashMap<String,Integer>());
}
if(cartMap.containsKey(product)){
//如果购物车中已有该商品
cartMap.put(product,cartMap.get(product)+1);
// session.setAttribute("cart",cartMap);
}else {
//如果购物车中没有该商品
cartMap.put(product,1);
//session.setAttribute("cart",cartMap);
}
//遍历输出session
Set<Map.Entry<String, Integer>> entries = cartMap.entrySet();
Iterator<Map.Entry<String, Integer>> iterator = entries.iterator();
while(iterator.hasNext()){
Map.Entry<String, Integer> next = iterator.next();
System.out.println(next.getKey()+"="+next.getValue());
}
}
这里有个疑问,为什么更新完session的值后,如需要重新赋值回去,session也会更新?
如下:
cartMap.put(product,cartMap.get(product)+1);
// session.setAttribute("cart",cartMap);
原因:
当我们创建购物车时,内存当初就会开辟一块空间来存放数据,当我们把购物车放入session中后,有一个理解误区,那就是购物车并不是放入了session,还是把购物车的引用地址放入了session,以方便我们通过session快速的找到购物车的hashmap。当我们操作购物车时,是通过session里的引用地址找到了hashmap对象,然后操作它,但是这个对象没有变,session对它的引用地址也没有变,所以不需要再重新放入session。