HTTP 方法
- GET 获取资源。当前网络请求中,绝大部分使用的是 GET 方法
- POST 传输实体主体。POST 主要用来传输数据,而 GET 主要用来获取资源
- HEAD 获取报文首部。和 GET 方法类似,但是不返回报文实体主体部分。主要用于确认 URL 的有效性以及资源更新的日期时间等。
- PUT 上传文件
- OPTIONS 查询支持的方法
- *DELETE 删除文件
- *PATCH 对资源进行部分修改
- *CONNECT 要求在与代理服务器通信时建立隧道
GET 和 POST 的区别
从用法上说,GET 一般用于无副作用、幂等的场景;POST 多用于有副作用、不幂等的情况(一次POST请求会服务器会添加一份新资源)。
冥等的定义:发送 M 和 N 次请求,服务器上资源状态一致。比如说,注册 10 个账号和 11 个账号是不冥等的,对文章进行了 10 次 11 次修改是幂等的,因为前者多了一个账号(资源),后者是更新同一个资源。
副作用的定义:副作用是指对服务器上资源做改变。比如搜索是无副作用的,但更新是有副作用的。
从本质上说,Post 和 Get 都取决于 HTTP,使用哪个方法与应用层传输没有必然的联系。HTTP 没有要求,如果是 POST,数据就要放在 BODY 中。也没有要求 GET,数据(参数)就一定要放在 URL 中而不能放在 BODY 中。
细节上有一些区别:
- Get 能请求缓存,但是 Post 不可以
- Post 支持更多编码类型
- Get 回退无害,Post 会再次提交
- Get 能被保存为书签,Post 不可以
- POST有消息体(body),长度无限制;GET一般没有body,数据只能放在url中,而url长度是有限的。其实也没有强制规定GET不能有body中,但是一般不会这么做
简而言之,就是“安全”和“不安全”的区别。eg. 点击同意某个协议,最好用POST
PUT 和 POST 的区别
用于向指定URL传送更新资源,是幂等的。比如修改用户密码,虽然提交的还是账户名跟用户密码这个俩个必填参数,但是每次提交都只是更新该用户密码,每次请求都只是覆盖原先的值。此时就该用PUT
POST用于提交请求,可以更新或者创建资源,是非幂等的。eg. 注册新用户用POST给服务器传输新用户名和密码
HTTP 常用首部
- 通用
- cache-control : 控制缓存行为
- connection : 连接的性质,比如 keep-alive
- user-Agent :用户信息
- Date :报文创建时间
- 请求
- Referrer Policy : 表示来源的(浏览器所访问的前一个页面),可以用于辅助检测 crsf 攻击,一般浏览器的默认值是 no-referrer-when-downgrade,意思是 https 降级 http 的时候不传原地址。
- Accept : 能正确接收的媒体类型
- Accept-XX(Accept-Charset/Accept-Encoding/Accept-Language):能正确接收的 xx
- Expect :期待服务端的指定行文
- If-Match :两端资源标记比较
- If-Modified-Since : 比较时间 未修改返回 304 Not Modified
- If-None-Match :比较标记 未修改返回 304 Not Modified
- Host: 请求的主机名
- 响应
- Location : 客户端重定向的 URI
- Server : 服务器名字
- ETag
- Age :响应存在时间
- Accept-Ranges :可以接受的范围类型
- 实体首部
- Content-Length 实体主体的大小
- Expires 实体主体的过期时间
- Last-Modified 资源的最后修改时间
- 非首部字段
- Cookie 和 Set-cookie
- Content-Disponsition
更多关于首部的资料可以查看:《HTTP 首部字段详细介绍》
cache-control 的参数
- 可缓存性
- public:浏览器/任何代理服务器等都能缓存
- private:只有发起请求的浏览请能缓存
- no-cache:强制向源服务器再次验证,同意了才能用缓存
- no-store:不缓存
- 到期
- max-age=[secend]:缓存的保存时间(s)
- s-maxage=[secend]:和 max-age 并存时会替代 max-age,但是只有在代理服务器中才会生效
- max-stale:在和 max-age 并存的时候,意思是在发起请求的地方(浏览器)缓存过了 max-age 之后但是还没过 max-stale,则浏览器还可以继续用这个过期了的缓存
- 重新验证
- must-revalidate:在设了 max-age 的缓存中,如果过期了必须去原服务端发起请求
- proxy-revalidate:在设了 max-age 的缓存服务器中,如果过期了必须去源服务端发起请求
- 其他
- no-transform:用在代理服务器中。缓存不能进行压缩改动等
帮助缓存的资源验证 Last-Modified 和 Etag(见 浏览器-缓存策略)
connection=keep-alive(长连接)
在 http 请发送之前会建立 TCP 连接,如果请求发送完之后连接不会关闭,则就是长连接,这样有新请求的时候就可以直接发送,不用经过 TCP 三次握手阶段,避免了连接建立和释放的开销。
但是,长时间的 TCP 连接容易导致系统资源无效占用,可以设置 keep-alive timeout(过多长时间没有新请求连接就自动关闭)。
cookie 和 session
cookie
- 服务端返回数据的时候通过 set-cookie 设置。浏览器保存 cookie 之后,在下次的同域请求中就会带上这个 cookie。cookie 是通过键值对的形式保存的,可以设置很多个。
- cookie 属性
- max-age=[second]或 expires: 设置过期时间(若没有设置则默认关闭浏览器 cookie 就被清除,请求不会带上 cookie)
- Secure: 只在 https 的时候发送 cookie
- HttpOnly: 无法通过 JS 的 document.cookie 访问(提高安全性)
- set-cookie 怎么写:例如在服务器的 response.writeHeader 写
'set-cookie': ['id=123; max-age=10', 'name=Jacleklm;HttpOnly']
session
在网站开发中,最常用的就是用 cookie 来保存 session,但是二者并不等价。cookie 是存在客户端,session 是存在服务器端。服务端根据客户发过来的 cookie(eg.cookie 中有 session_id)定位到这个客户的信息(session),给他提供最合适的响应
数据协商
客户端发送给服务端请求时候,请求头中会声明希望拿到的数据格式,以及其他与数据相关的一些限制,服务端会根据它的请求中的声明,来做针对性的返回
分类中包含请求和返回两种
请求
Accept(想要的数据类型),Accept-Encoding(数据是怎样的编码方式,主要是压缩方式),Accept-Language, User-Agent(浏览器的相关信息,移动端/PC)
返回
Content-Type,Content-Encoding,Content-Language
HTTP 状态码
- 1XX 信息性状态码,接收的请求正在处理
- 100 Continue 等待状态码,客户端应重新发送请求
- 101 Switching Protocols 改用协议 http 到 https 或 http1.1 到 2.0 之类
- 2XX 成功状态码,请求正常处理完毕
- 200 OK 操作成功
- 202 Accepted 服务器已接受请求,但尚未处理 (最后可能处理也可能不处理)
- 204 No Content 请求成功,但是报文不含实体的主体部分
- 206 Partial Content 进行范围请求
- 3XX 重定向状态码,需要附加操作以完成请求
- 301 Move Permanently 禁止从 POST 变成 GET。 永久性重定向,资源已经被分配到了新的 URI,以后应该使用资源现在的 URI 进行访问。
- 302 Found 禁止从 POST 变成 GET。临时重定向,资源已经被分配到了新的 URI,希望用户(本次)能用新的 URI 访问 实际上发部分客户端把它当成 303 处理
- 303 See Other 表示资源存在另一个 URI。应用 Get 获取资源
- 304 Not Modified 当客户端发送附带条件的请求后,服务端允许访问资源,但因为条件不符合,返回实体主体为空(也可能是服务器端资源未变,建议客户端直接用缓存)
- 307 Temporary Redirect 不会从 POST 变成 GET。 临时重定向,资源临时分配了 URL,但是希望客户端能够保持方法不变请求新地址(解决 302 被当成 303 处理的问题)
- 4XX 客户端错误状态码,服务器无法处理请求
- 400 Bad Request 请求报文存在语法错误
- 401 Unauthorized 需要认证,如果之前已经进行一次请求,表示认证失败
- 403 Forbidden 请求资源存在但被拒绝,常用于一个资源只允许在特定时间段内访问(如果不想透露可以谎报 404)
- 404 Not Found 找不到请求的资源
- 405 Method Not Found 不支持的请求方法,比如只支持 Get,但是收到了 Post 请求
- 5XX 服务器错误状态码,服务器处理请求错误
- 500 Inernal Server Error 执行请求出现错误
- 501 Not Implemented 不支持此请求方法(和 405 区别在于,405 是访问的资源不支持,而 501 表示服务器不能操作此方法)
- 502 Bad Gateway 代理与上行服务器之间出现问题
- 503 Service Unavailable 超负荷,停机维护
- 504 Gateway Timeout 网关等待服务器相应超时
HTTP1.1
HTTP版本迭代详见 HTTP 协议入门
和 1.0 的区别
- 持久链接
- 管道机制。在同一个TCP连接里面,客户端可以同时发送多个请求。以前的做法是,在同一个TCP连接里面,先发送A请求,然后等待服务器做出回应,收到后再发出B请求
- Content-Length。声明本次响应的数据长度,单位是字节。作用是区分数据包是属于哪一个响应的
- 分块传输编码。产生一块数据,就发送一块,采用”流模式”(stream)取代”缓存模式”(buffer)。1.1版规定可以不使用Content-Length字段,而使用”分块传输编码”(chunked transfer encoding)。只要请求或回应的头信息有
Transfer-Encoding: chunked
字段,就表明回应将由数量未定的数据块组成 - 新增方法:PUT、PATCH、HEAD、 OPTIONS、DELETE
1.1的缺点
- 队首阻塞。一个TCP无法并行进行多个http传输,只能排队。如果前面请求的响应特别慢的话,就会造成许 多请求排队等待的情况,这种情况被称为“队头堵塞”
- 连接过多。为了解决队首阻塞问题,解决方法就是同时打开多个连接请求
HTTP2
基于 SPDY 协议
HTTP1.1 和 HTTP2 的区别
- 多路复用,用一个 TCP 进行连接共享,一个请求对应一个 id,这样就可以发送多个请求,接收方通过 id 来响应不同的请求,解决了 http1.1 队首阻塞和连接过多的问题。因为 http2.0 在同一域名不论访问多少文件都只有一个连接,所以对服务器而言,提升的并发量是很大的。
- 二进制数据帧和流的概念,数据拆分成数据帧传输,并进行顺序标识,接收方收到数据后按序组合即可获取正确数据。这样就可以并行传输了,解决了 http1.0 只能串行传输的问题。
- Sever Push。 服务端可以在客户端某个请求之后,主动推送其他资源(用 Link 头部字段)。
- 压缩头部,解决了 http1.1 中头部反复传输资源浪费(请求的很多字段都是重复的,比如Cookie和User Agent)的问题。一方面,头信息使用gzip或compress压缩后再发送;另一方面,客户端和服务器同时维护一张头信息表,所有字段都会存入这个表,生成一个索引号,以后就不发送同样字段了,只发送索引号,这样就提高速度了
存在问题
http2.0 使用了多路复用,一般来说同一域名下只需要使用一个 TCP 连接。
但是当连接中出现丢包时,整个 TCP 都要开始等待重传,后面的数据也都被阻塞了。而 http1.0 可以开启多个连接,只会影响一个,不会影响其他的。
所以在丢包情况下,http2.0 的情况反而不如 http1.0。
HTTP3.0
为了解决 2.0 丢包性能的问题,Google 基于 UDP 提出了 QUIC 协议。
HTTP3.0 中的底层支撑协议就是 QUIC。所以 http3.0 也叫 HTTP-over-QUIC。
QUIC 协议
UDP 协议高效,但不可靠。QUIC 基于 UDP,在原来的基础上结合了 tcp 和 http 的精华使它可靠。
特点:
- 多路复用
HTTP2 虽然是多路复用,但是 TCP 协议是没有这个功能的。QUIC 原始就包含此功能,并且传输的单个数据流可以保证有序交付且不会影响其他数据流
其在移动端也会比 TCP 好,因为 TCP 基于 IP+端口识别连接,不适合多变的网络环境,但是 QUIC 是通过 ID 识别连接,不论网络如何变化,只要 ID 不变,就能迅速连上(实时手游就是这样实现的) - 纠错机制
假如说这次我要发送三个包,协议会算出这三个包的异或值并单独发出一个校验包,也就是总共发出了四个包。
当出现其中的非校验包丢包的情况时,可以通过另外三个包计算出丢失的数据包的内容。
当然这种技术只能使用在丢失一个包的情况下,如果出现丢失多个包就不能使用纠错机制了,只能使用重传的方式了。 - 0-RTT
通过使用类似 TCP 快速打开的技术,缓存当前会话的上下文,在下次恢复会话的时候,只需要将之前的缓存传递给服务端验证通过就可以进行传输了。