Spring Boot入门3

回顾
Http协议
请求部分 HttpServletRequest
请求行
请求头
请求体
响应部分 HttpServletResponse
响应行
响应头
响应体
重定向 :
地址栏会发生变化
发送两次请求
跳转去任意路径
不可以通过model携带数据
转发:
地址栏不会发生变化
发送一次请求
只能是内部路径
可以通过model携带数据
重定向和转发的区别

如何将java代码中的数据,传递给html模版
模版技术:thymeleaf


拨通电话之后,开始聊天,挂断电话,可以看做一次会话



## 一、会话技术

### 1. 什么是会话

> 在计算机术语中,会话指的是客户端和服务器交互通讯的过程。简单的理解,大家可以看成是两个普通的人在打电话。一次电话从通话开始到挂断,可以看成是会话。

* 会话的特征
  * 会话能够把同一用户发出的不同请求之间关联起来。不同用户的会话应当是相互独立的
  * 会话一旦建立就一直存在,直到用户空闲时间超过了某一个时间界限,会话才会中断。 好比大家打电话,超过了某一定时间就会自动断开。

### 2. 什么是会话技术

> HTTP是一种无状态协议,每当用户发出请求时,服务器就会做出响应,客户端与服务器之间的联系是离散的、非连续的。当用户在同一网站的多个页面之间转换时,根本无法确定是否是同一个客户。
>
>  举例如下:
>
> 1.  我们在电商网站上选购商品,即便没有登录,假如购物车。下一次我们再打开网站,还是能够看到上一次添加到购物车的商品
> 2.  我们在一些网站登录了之后,过了几天我们在打开这个网站,还能认识我们,还能在右上角显示 `欢迎您  xxx` 
> 3.  当我们再次登录一些论坛时,总会显示上一次我们访问论坛的时间。

### 3. 会话技术的分类

> 会话技术主要常用的有两种, 一种是`Cookie`  , 另一种是 `Session` .

## 二、 Cookie

### 1. 什么是cookie

~~~java
Cookie 可以翻译为“小甜品,小饼干” ,  Cookie 实际上是指小量信息,是由 Web 服务器创建的,将信息存储在用户计算机上的文件。当用户通过浏览器访问服务器的时候,浏览器会自动携带早前存储的cookie ,传递给服务器。
~~~

### 2. Cookie的应用场景

> 所有服务器想要让客户端帮忙存储一些数据,以便下次访问能够识别客户的情形,都可以使用cookie,如:

1. 商品浏览记录

2. 购物车信息

3. 记住账号密码

   ...

### 3. Cookie的基本使用

> 大家要记住一个核心,cookie是服务器生成,然后寄存在客户端的一小份数据。 那么服务器如何把这一小份数据给客户端呢?  只能依靠前面讲过的`response`对象了。

~~~java
Cookie cookie = new Cookie("key" ,"value");
response.addCookie(cookie);
~~~

### 4. Cookie的分类

> cookie有分临时性(也称之为会话级别cookie),也有持久性。 

* 临时性Cookie

> 默认情况下,关闭浏览器后,cookie就销毁掉了。

~~~java
//这种做出来的cookie就是临时性的cookie
Cookie cookie = new Cookie("key" ,"value");
response.addCookie(cookie);
~~~

* 持久性Cookie


> 可以设置保存时长

~~~java
 Cookie cookie = new Cookie("name","aobama");

 //设置过期时间
 cookie.setMaxAge(60 * 60 * 24 * 7);

 response.addCookie(cookie);
~~~

* 删除cookie

> 由于cookie是存放在客户端上的,所以要删除cookie, 由服务器对同样的cookie名称设置过期时长即可

~~~java
/** Sets the maximum age of the cookie in seconds.
* <p>
* A positive value indicates that the cookie will expire after that many
* seconds have passed. Note that the value is the <i>maximum</i> age when
* the cookie will expire, not the cookie's current age.
* <p>
* A negative value means that the cookie is not stored persistently and
* will be deleted when the Web browser exits. A zero value causes the
* cookie to be deleted.
*/
这是setMaxAge方法的注释,大意是: 
1. 设置一个正数( > 0) 那么cookie将会在这段时间过后就过期
2. 设置一个负数( < 0) 那么cookie将不会被存储,浏览器关闭,cookie将会消失。也就是临时性cookie
3. 设置的值是 0 , 那么cookie会立即被删除。

其实cookie的这个有效期,默认值是多少呢?  是  -1  , 可以查看getMaxAge()方法
~~~



### 5.  获取上次访问时间


![这里写图片描述](https://img-blog.csdn.net/20180907163432342?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3d3dzI5NDk5Mzc0MQ==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)

* 页面准备

~~~html
 <form action="user_login" method="post">
        用户名:<input type="text" name="username"/></br>
        密  码:<input type="password" name="password"/></br>
        <input type="submit" value="登录"/>
    </form>
~~~

* controller

~~~java
@RestController
public class UserController {
    private static final String TAG = "UserController";

    @RequestMapping("user_login")
    public String login(HttpServletRequest request , HttpServletResponse response ,  String username , String password){

        System.out.println(username + " : " + password);
        HttpSession session = request.getSession();
      //  session.setAttribute("name","zhangsan");

        String id = session.getId();
        System.out.println("id=" + id);

        //1. 判断账号密码
        if("admin".equals(username) &&  "123".equals(password)){

            String result = "欢迎您 , "+ username;

            //2.要获取cookies
            Cookie[] cookies = request.getCookies();

            //有cookie
            if(cookies != null){

                //就想看cookie里面有没有带上次的时间
                for(Cookie cookie : cookies){
                    //如果能成立,就表示找到了上一次访问的时间。
                    if("last".equals(cookie.getName())){

                        long time = Long.parseLong(cookie.getValue());

                        Date date = new Date();
                        date.setTime(time);

                        result += "\r\n 上一次访问时间是:"+date.toLocaleString();
                    }
                }
            }

            //只要有登录成功,就必须返回最新的登录时间。
            String time = System.currentTimeMillis()+"";
            Cookie cookie = new Cookie("last" , time);
            cookie.setMaxAge(60*60*24*7);
            response.addCookie(cookie);

            return result;
        }

        return "登录失败";
    }
}
~~~

### 6. Cookie的问题

1. cookie 大小有限制,每个服务器在最多不能超过20个。
2. cookie 是存放在客户端上,信息不安全,有被截获的可能 , 一般cookie不会放一些隐私的数据。
3. cookie不能跨浏览器
4. cookie不能保存中文

## 三、 Session

### 1. 什么是session

> Session是基于Cookie的一种会话机制。 Session服务器上的一段内存空间 , 可以用来存储数据。
>
> Cookie是服务器返回一小份数据给客户端,并且存放在客户端上。 Session是,数据存放在服务器端。

### 2. Cookie 和 Session的对比

cookie

数据存放在客户端

数据不安全

减轻服务器压力, 用户磁盘占用比较多。

存放的数据有限


session

数据存放在服务器端

数据相对比较安全。

服务器压力大一点。

存放的数据 依赖服务器的内存空间。


### 3. Session的简单使用

~~~java
1. 获取session
   HttpSession session  =  request.getSession()
2. 存值
   session.setAttribute(name ,value);
3. 取值:
   session.getAttribute(name);
4. 移除值
   session.removeAttribute(name);
5. 让session失效 作废
   session.invalidate();
6. 获取id值
   session的id值就是这一块内存空间的唯一标识符。  session.getId() .

~~~

### 4. Session的背后细节



####1. 实验

> 定义两个方法,一个方法负责使用session存值, 另一个方法负责从session里面取值。

* 存值的方法

~~~java
@RequestMapping("testSession02")
public String testSession02 (HttpServletRequest request){

    HttpSession session = request.getSession();

    String id = session.getId();
    System.out.println("sessionId = " + id);

    //存值
    session.setAttribute("address","深圳宝安");

    //取值
    String value =(String) session.getAttribute("address");
    System.out.println("value = " + value);

    return "test02 success = " +value;
}
~~~

* 取值的方法

~~~java
@RequestMapping("testSession03")
public String testSession03 (HttpServletRequest request){

    HttpSession session = request.getSession();
    String id = session.getId();
    System.out.println("sessionId = " + id);

    //取值
    String value =(String) session.getAttribute("address");
    System.out.println("value = " + value);

    return "test02 success = "+value;
}
~~~

* 操作步骤

1. 打开浏览器,访问testSession02方法,然后不要关闭浏览器,打开新的选项卡,接着访问testSession03, 结果是新的选项卡可以看到数据
2. 打开浏览器,访问testSession02方法,然后关闭浏览器,重新打开浏览器,访问testSession03 ,无法看到数据

####2. 实验解释

- 第一次访问

> session没有创建,并且咱们的请求对象里面也不带任何的cookie过来。 那么这个时候会在内存中给你创建一个新的session区域,并且把这个session的id值放到cookie里面给我们的浏览器返回。 注意这些事情都是服务器端自己做的。所以cookie是一个临时性的cookie。 关闭浏览器就销毁cookie了

- 第二次访问

> 如果是第二次访问,那么浏览器会把之前的那个cookie给带过来, 服务器收到了cookie,里面有我们上一次给的sessionid 值, 那么这个时候再调用request.getSesion() , 它先会拿我们待过来的id ,到内存里面去找有没有session的 id值跟这个cookie带过来的一样 ,如果有,就直接返回这个内存空间, 否则就创建新的session空间 



![这里写图片描述](https://img-blog.csdn.net/20180907163548393?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3d3dzI5NDk5Mzc0MQ==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)

### 5. session 销毁的细节!

> 针对刚才第二个实验,关闭浏览器,再次打开获取到的是全新的session,那么第一次操作的session是否已经被销毁了? 

![这里写图片描述](https://img-blog.csdn.net/20180907163605134?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3d3dzI5NDk5Mzc0MQ==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)

> 1. 关闭浏览器并不能让session销毁。 因为session是服务器的一块内存空间, 它的销毁不依赖客户端的浏览器关闭还是不关闭。
> 2. 再次访问的时候,我们的浏览器已经没有了那个sessionid值,所以就找不到了以前的那一块空间。所以打开浏览器重新访问,会拿到的是全新的session空间。 但是原来的那个session还存在,只不过我们无法操作它而已。
> 3. 如果真的项操作以前的session,我们要手动往cookie里面存session id 值,并且设置成持久性cookie
>
>  `注意: cookie的那个name不能乱写。 JSESSIONID ` 

* 销毁session 

1. invaidate()
2. 关闭服务器
3. 自动失效。 有效期。 默认30分钟

### 6. Session的作用范围 & 生命周期

- 作用范围

  > sesison 作用范围:  一次会话范围内有效。 不关闭浏览器, 

- 生命周期

  > 何时创建

  ```
  第一次调用request.getSession()就创建。 
  第二次会根据Cookie中携带的信息JSESSIONID找到第一次创建的Session对象
  ```

  > 何时销毁

  ```
  关闭服务器 

  session超时了。默认30分钟。 从最后一次请求开始计时。

  调用invalidate . 让空间失效、作废
  ```

### 7. 购物车案例

> 日常生活中,大家都是有过购物的经验,喜欢什么商品,先把它添加到购物车中,后续再付款。 那么购物车是如何实现的呢? 其实购物车可以使用cookie来实现,也可以使用session来实现。真正的企业的购物车要实现起来还得配合缓存数据库来实现。咱们接下来使用session来实现一个简易的购物车即可

![这里写图片描述](https://img-blog.csdn.net/2018090716362476?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3d3dzI5NDk5Mzc0MQ==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)

* 准备工作

> 需要在项目的build.gradle 中添加  thymeleaf依赖

~~~groovy
compile("org.thymeleaf:thymeleaf-spring4:3.0.9.RELEASE")
~~~

* 在模板页面上,要记得引入命名空间

~~~html
 <html xmlns:th="http://www.thymeleaf.org" >
~~~

#### 1. 显示商品列表

* index.html

~~~html
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <h2><a href="list">开始购物</a></h2>
</body>
</html>
~~~

* 商品实体类

~~~java
public class Product {


    private int id; // 商品的编号
    private String name; //商品名字
    private double price ; //商品价格

    get & set 方法
    ...
}
~~~

* 商品controller

~~~java
@Controller
public class ProductController {
    private static final String TAG = "ProductController";

    //准备商品数据
    private static List<Product> list;
    static{
        list = new ArrayList<Product>();

        list.add(new Product(1,"iphone6s" ,3788));
        list.add(new Product(2,"小米8" ,2788));
        list.add(new Product(3,"vivo20" ,4788));
        list.add(new Product(4,"meizu6" ,3690));
        list.add(new Product(5,"诺基亚200" ,188));
    }


    @RequestMapping("/prod")
    public String products(Model model){

        model.addAttribute("list",list);

        return "products.html";
    }
}
~~~

* 商品列表  list.html  位于 /resources/templates/下

~~~html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org" >
<head>
    <meta charset="UTF-8"/>
    <title>商品列表页面</title>
</head>
<body>

    <h2>商品列表</h2><br/>

    <table>

        <thead>
            <td width="100px">编号</td>
            <td width="100px">名称</td>
            <td width="100px">价格</td>
            <td width="100px">操作</td>
        </thead>

        <!--从这里开始就是真正的商品数据显示了
            p 遍历 list集合的出来的每一个元素对象, 其实就是商品对象。
        -->
        <tr th:each="p : ${list}">
            <td th:text="${p.pid}">111</td>
            <td  th:text="${p.name}">111</td>
            <td  th:text="${p.price}">111</td>
            <td  >
                <a href="#" th:href="'/add?pid='+${p.pid}">添加</a>
            </td>
        </tr>
    </table>
</body>
</html>
~~~

#### 2. 加入购物车

> 点击加入购物车需要跳转到controller , 根据提供的索引下标,获取到对应的商品,添加到购物车 `Map` 中

* 添加购物车方法

~~~java
    @RequestMapping("/add")
    public String add(HttpServletRequest request,int pid){

        System.out.println("pid:"+pid);

        //将对应的pid的Product 数量 保存到 一个容器(购物车) Map<Product商品,Integer数量>

        //定义一个容器,保存购物车对应的数据
        HttpSession session = request.getSession();
        Map<Product,Integer> cartMap = (Map<Product, Integer>) session.getAttribute("cartMap");

        //session中可能没有cartMap
        if(cartMap == null){
            cartMap = new HashMap<Product,Integer>();
        }

        //根据pid从list中找出对应的product对象
        Product product = list.get(pid);

        //若cartMap中没有product,则数量为1
        if(!cartMap.containsKey(product)){
            cartMap.put(product,1);
        }else{
            //若cartMap中有product,则需要取出数量,数量上+1
            Integer count = cartMap.get(product);
            cartMap.put(product,count+1);
        }

        //将cartMap保存到session中
        session.setAttribute("cartMap",cartMap);

        //测试:遍历打印cartMap中的数据
       /* for (Map.Entry<Product, Integer> entry : cartMap.entrySet()) {
            System.out.println(entry.getKey()+"="+entry.getValue());
        }*/
        //如果我们做的对数据增删改的操作: 重定向, 若为查询操作: 转发
        return "redirect:/showCart";
    }
~~~





#### 3. 显示购物车

> 通过点击去购物车查看, 即可跳转到购物车页面,显示商品。

* cart.html 

> 该页面是用于显示动态数据,所以需要放到模板 templates下。

~~~html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <!--直接获取session中的内容
    <div th:text="${session}"></div>-->
    <table border="1">
        <tr>
            <th>商品编号</th>
            <th>商品名称</th>
            <th>商品价格</th>
            <th>商品数量</th>
            <th>商品小计</th>
            <th>操作</th>
        </tr>
        <!--遍历cartMap-->

        <tr th:each="entry : ${session.cartMap}">
            <td th:text="${entry.key.pid}">111</td>
            <td th:text="${entry.key.name}">111</td>
            <td th:text="${entry.key.price}">111</td>
            <td th:text="${entry.value}">111</td>
            <td th:text="${entry.key.price * entry.value}">111</td>
            <td>
                <a href="#" th:href="'/delete?pid='+${entry.key.pid}" >删除</a>
            </td>
        </tr>

        <a href="/clear">清空购物车</a>
    </table>
</body>
</html>
~~~
* 作业 : 
  * 完成上次访问时间
  * 完成购物车实现
    * 清空购物车


Cookie: 小甜点,小饼干, 小票,主要是用于中会话的过程保存少量数据
将数据保存在用户浏览器中,当用户访问网站的时候,浏览器会自动的携带cookie给服务器
常用的方法:
new Cookie(key,value)
response.addCookie(cookie)
request.getCookies()
setMaxAge : 设置存活时间 ,单位是秒
展示上次访问的时间
Cookie的问题:
浏览器保存cookie的数量有限制
cookie保存的内容大小有限制
cookie不安全
cookie存中文的问题 tomcat 8.5以上支持的, tomcat7

Session:
服务器端的会话技术,将用户的数据保存在服务器端,但是它需要依赖cookie技术

session的流程:
    第一次访问:只有当我们第一次调用Request.getSession()才会申请session空间,会得到一个JessionID的cookie
    第二次访问: 浏览器会自动的携带jsessionid来到服务器
              服务器这边再次调用request.getSession()就会找到第一次创建的session对象

session销毁的细节:
    invalidate()
    超时30分钟
    服务器关闭

注意,当浏览器关闭之后,JSESSEIONID这个cookie就会自动销毁,这个时候服务器的sessioin还没有销毁
session生命周期:
创建: 第一次调用Request.getSESSION()
销毁: 超时,手动

session主要作用是在服务器端进行多个请求资源之间的共享数据
“`

猜你喜欢

转载自blog.csdn.net/www294993741/article/details/82498934