前言
看了很多博客文章,对一些概念都一直模模糊糊
希望能够对JavaWeb的一些知识深刻了解一下
目录
- HTTP的无状态
- Cookie
2.1. Cookie API
2.2. Cookie的属性
2.3. 一个Cookie的使用 - 验证Path属性
- 购物车应用
- 总结
HTTP的无状态
一般说到HTTP协议的特性就会想到无状态,无连接
无连接就是每次连接只处理一个请求。服务器处理完客户的请求,并收到客户的应答后,即断开连接,这样太耗时且麻烦(毕竟Http协议是基于TCP协议的,而TCP需要三次握手连接,四次挥手断开连接)
随着版本发展的发展,HTTP1.1已经默认是长连接了,也就是说响应完后不会立即关闭连接,无连接这个特性可以告别了
(关于连接HTTP1.1还有并行连接及管道化等)
那无状态是什么呢?
以前学习HTTP协议的时候并没有深究,看到这篇文章后有一些懂了,特地来说说
http协议无状态中的 “状态” 到底指的是什么?!
百度百科中是这么解释的:
HTTP无状态协议,是指协议对于事务处理没有记忆能力。缺少状态意味着如果后续处理需要前面的信息,则它必须重传,这样可能导致每次连接传送的数据量增大。另一方面,在服务器不需要先前信息时它的应答就较快。
对同一个url请求没有上下文关系
笼统但不够深刻,不是特别的清晰
结合上面那一篇文章,认为HTTP协议的无状态是指:
HTTP协议对于客户端和服务器的会话过程,不会保存临时数据,服务器不会记忆客户端的信息,每一次请求都是独立的
这是种什么情况?
最好的例子就是仅靠HTTP协议的购物车问题(纯HTTP协议没有cookie、session等):
- 我们在客户端使用表单的get方法,通过URL地址后面带着我们的用户名、密码访问服务器,服务器验证了数据库的用户登录信息,响应“可以登录并跳转到购物界面”的信息给客户端(由于HTTP的无状态,服务器不保存这个登录信息)
- 我们在购物过程中,选中了一个商品,想要加入我们的购物车,这时客户端发送加入购物车的请求给服务器,问题来了: 由于服务器的无状态,服务器端不知道我们是谁(找不到用户,自然找不到购物车),返回“失败”信息给我们,如何解决呢?如果单靠HTTP协议,就只能在URL地址后面带上我们的用户名、密码
因为HTTP协议的无状态性,购物车有几个问题
- 操作繁琐,每次都需要带上我们的用户信息
- 不安全,在URL上带着用户名、密码,是个人都能盗号
- 频繁访问数据库,一次登录就需要一次数据库验证用户信息,每点一次加入购物车就需要直接往数据库里插入数据
所以说无状态中的状态是指:保存【不同的客户端访问的关系,不同访问需要保存的数据记录(URL中保存的用户信息)等】数据的缓存空间
这个缓存空间保存了用户名、密码,缓存了我们访问数据库的操作
就如同无连接问题被解决了,无状态问题也被大佬们解决了
一个是保存在客户端的Cookie,一个是保存在服务器端的Session
这两不是HTTP协议的内容,但是被各种Web容器、浏览器支持
Cookie
最先出现的是Cookie技术,后面的Session技术也是在Cookie上实现的
Cookie技术是为了解决HTTP协议的无状态的问题
还是前面的购物车问题:当我登录时,客户端发送请求,服务器记录下客户端的信息(给用户发一个通行证:Cookie),然后向客户端响应response中带上这个Cookie,浏览器保存这个Cookie,当用户想要将物品加入购物车,客户端发送加入购物车请求,就仅需带上Cookie通行证,有了这张通行证,服务器就记得这是哪个用户,然后就能简单的处理请求
知道了Cookie的思想,再来了解一下Cookie API
Cookie API
API | 操作 |
---|---|
public Cookie(String name,String value) | 创建Cookie |
setValue与getValue方法 | 设置、获得key-value键值对value属性 |
setMaxAge与getMaxAge方法 | 过期时间,在设置的某个时间点后该 Cookie 就会失效 |
setPath与getPath方法 | 设置、获得路径,Path是该 Cookie 是在当前的哪个路径下生成的 |
setDomain与getDomain方法 | Domain是指生成该 Cookie 的域名,如 domain=“www.baidu.com” |
getName方法 | 获得key-value键值对key值-name |
Cookie是以键值对key-value保存数据
我们创建Cookie对象也是需要传入key-value键值对
Cookie的属性
我们查看源码,可以发现Cookie类有很多属性
我们来大致了解一下这些属性,这对理解Cookie有帮助
- CookieNameValidator:判断Cookie的名字是否有效,需要满足一定的规则才可以作为Cookie的名字
源码文档是这么说的:
The name must conform to RFC 2109. That means it can contain only ASCII alphanumeric characters and cannot contain commas, semicolons, or white space or begin with a $ character. The cookie’s name cannot be changed after creation.
也就是必须符合RFC 2109
- name 、 value
创建Cookie时传入
一个是Cookie的名字,一个是它的值,名字可以重复,值必须唯一
为什么是这样呢?
对于一个用户,名字可以重复的,但是value一定要唯一标识这个用户
也就是这个值是用来判断是哪个客户端(用户)
这里需要注意:private final String name;
name 是 final 修饰的,也就是说name是不可以更改的
- serialVersionUID
序列号,是在序列化和反序列化中通过判断序列化来验证版本的一致性
- version
版本,目前有两个版本,对应version值为0、1
version 0 : 由Netscape公司制定的,也被几乎所有的浏览器支持,Java中仅支持版本0。版本0限制了很多东西: Cookie的内容中不能出现空格,方括号,圆括号,等于号(=),逗号,双引号,斜杠,问号,@符号,冒号,分号
version 1:根据RFC2109文档制定的,放宽了很多限制。版本0中所限制的字符都可以使用。但为了保持兼容性,程序开发者要尽量避免使用这些特殊字符
- comment
浏览器显示这个Cookie的时候显示出来的对这个Cookie的意义的说明信息
- domain
指定关联的Web域名,如果希望多个Web服务器共享一个Cookie,可以设置domain
domain属性的默认值是创建cookie的网页所在服务器的主机名。不能将一个cookie的domain设置成服务器所在的域之外的域
例如设置为“.baidu.com”,所有以.baidu.com结尾的网站都可以使用该cookie
- maxAge(Expires)
Cookie的生存期(失效时间),整数,单位为秒
当前maxAge = -1,maxAge为负数即浏览器关闭,Cookie失效,本地不保存
maxAge=0,表示从客户端电脑或浏览器内存中删除此cookie
maxAge为正数,该Cookie可以保存在客户端电脑,以Cookie文件保存,但是不论关闭浏览器还是电脑,都会在到期才会删除
- path
Cookie的使用路径,也就是设置哪些路径可以访问这个Cookie
Cookie对应的页面(通常是一个目录),这个目录的子页面都可以访问这个Cookie,其他则不能访问,如果要设置为全局可以访问的,则设置为/
默认是/
- secure
指定该Cookie是否被使用过安全协议传输
安全协议:在网络上传输数据之前先将数据加密
默认是false,也就是不安全的,仅通过http协议传输
可以设置为true,通过HTTPS、SSL等安全协议
- httpOnly
限制cookie 对 HTTP 请求的作用范围
如果是true,只有在http请求头中会带有此cookie的信息,不能被其他方式如document.cookie来访问
为false就允许js等其他方式访问
默认是false
一个Cookie的使用
前面把Cookie的API和属性讲清楚了,可以实践一下
创建一个TestCookie类,继承HttpServlet类:
package test;
import javax.servlet.ServletException;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class TestCookie extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setCharacterEncoding("GBK");
Cookie cookie = new Cookie("name","zhangsan");
cookie.setMaxAge(100);
resp.addCookie(cookie);
resp.getWriter().write("这是一个Test Cookie,名字:"+cookie.getValue());
resp.getWriter().write("作用域为:"+cookie.getDomain());
}
}
记得去web.xml配置访问路径
运行发现,Domain是null,而浏览器显示的是localhost?
那是因为我们在服务器端创建Cookie,还没有传Domain进Cookie对象,初始化是null
验证Path属性
Path属性是设置该Cookie能被哪些Servlet访问
默认是/
我们有了上面这个TestCookie类,再创建一个TestCookie1类
package test;
import javax.servlet.ServletException;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class TestCookie1 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setCharacterEncoding("GBK");
Cookie[] cookies = req.getCookies();
for(Cookie cookie : cookies){
if (cookie.getValue().equals("zhangsan")){
resp.getWriter().write("TestCookie1获得了Cookie,value="+cookie.getValue());
}
}
}
}
记得要去web.xml配置一下访问路径
很简单,就是验证一下能不能得到vlaue='zhangsan’的Cookie
TestCookie创建Cookie成功
获得Cookie也成功了
所以说Path=’/’,就是所有的Servlet都可以获得Cookie
那我们设置一下Path
在TestCookie中添加。记得放在add方法前
cookie.setPath("/testCookie");
在访问http://localhost:8080/testCookie1
因为我是用数组迭代获得,因为没有Cookie给TestCookie1,所以空指针异常了
所以Path确实是限制Servlet访问Cookie
购物车应用
我们可以用Cookie来模拟购物车
写一个实体类Goods:
package test;
//货品类
public class Goods {
private String goodName;
private int goodId;
private float goodPrice;
public Goods(String goodName, int goodId, float goodPrice) {
this.goodName = goodName;
this.goodId = goodId;
this.goodPrice = goodPrice;
}
public String getGoodName() {
return goodName;
}
public int getGoodId() {
return goodId;
}
@Override
public String toString() {
return goodName + "仅卖" + goodPrice+" 元,快来买";
}
}
先给商场装货物:ShopGoods
package test;
import java.util.LinkedList;
public class ShopGoods {
LinkedList<Goods> goods= new LinkedList<Goods>();
{
goods.add(new Goods("苹果",1,10));
goods.add(new Goods("辣条",2,5));
goods.add(new Goods("毛巾",3,20));
}
public LinkedList getAll(){
return goods;
}
}
上面两个都是普通的Java类
接下来完成Servlet
商场界面:
package test;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.LinkedList;
public class ShoppingCar extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setCharacterEncoding("GBK");
Cookie cookie = new Cookie("shop","zhangsan");
ShopGoods shopGoods = new ShopGoods();
LinkedList<Goods> goods = shopGoods.getAll();
PrintWriter writer = resp.getWriter();
writer.write("<html><body>");
writer.write("---------------------商城大甩卖---------------------<br>");
for (int i=0;i<goods.size();i++) {
writer.write("---------------" +goods.get(i).getGoodName()+ "-----------------<br>");
writer.write("<a href='/goodsInfo?id=" + goods.get(i).getGoodId() + "''target=_blank'> " + "查看信息并加入购物车</a><br>");
}
writer.write("</body></html>");
writer.write("您的购物车:");
Cookie[] cookies = req.getCookies();
if (cookies != null){
System.out.println(cookies.length);
for (Cookie cookie1 : cookies){
String name = cookie1.getName();
for (Goods goods1:goods){
if (name.equals(String.valueOf(goods1.getGoodId()))){
writer.write(goods1.getGoodName()+" ");
}
}
}
}
}
}
货品界面:
package test;
import javax.servlet.ServletException;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.LinkedList;
public class GoodsInfo extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setCharacterEncoding("GBK");
String id = req.getParameter("id");
PrintWriter writer = resp.getWriter();
ShopGoods shopGoods = new ShopGoods();
LinkedList<Goods> all = shopGoods.getAll();
writer.write("<html><body>");
for (Goods good : all){
if (good.getGoodId() == Integer.parseInt(id)){
writer.write(good.toString());
}
}
Cookie cookie = new Cookie(id,id);
resp.addCookie(cookie);
writer.write("<a href='/shoppingCar' target=_blank'> " + "返回商店</a><br>");
writer.write("</body></html>");
}
}
记得在web.xml里添加这两个Servlet
运行:
购物车什么都没有,在点击一下苹果
返回商店
这里只是简单的完成了购物车的思想,没有前后端分离,而且仅仅依靠Cookie,显得有点粗糙
总结
- 详细的思考了HTTP协议的无状态性,无状态就是浏览器不会存储会话的临时数据
- 仔细的了解了Cookie的API及属性
- 简单是使用了一个Cookie,验证了Path属性
- 简单的实现了购物车
总结:算是理解了Cookie