转载:https://www.0php.net/posts/HTTP 常用知识点.html
0x00 报文格式
一个普通的 HTTP
请求报文格式如下:
[请求方法] [URI] [HTTP版本] Host: [主机地址] // ... 其它请求头信息 // 一行空行 [请求实体] |
简单例子如下:
GET / HTTP/1.1 Host: www.baidu.com |
上述例子有两个空行,但并非无意义。第一行空行为分割请求头和请求实体的标志,第二行是因为请求实体内容为空(如果有内容就不是空行)。
下面来看响应报文格式:
[HTTP协议版本] [响应状态码] [状态码含义] Content-Type: [响应内容类型] // ... 其它响应头信息 // 一行空行 [响应实体] |
简单例子如下:
HTTP/1.1 200 OK Content-Type: text/html Content-Length: 34 <html><h1>Hello World!</h1></html> |
0x01 重要的头信息
Content-Length
Content-Length
表示请求/响应实体内容的长度(单位是字节)。
当 Content-Length 的值与实际内容字节数不符会出现什么情况?
- 当内容的实际字节数大于
Content-Length
的值时:- 当服务端接收的请求实体长度超过
Content-Length
,服务端会直接关闭连接,导致本次请求失败。 - 当服务端返回的响应实体长度超过
Content-Length
,超出的部分会被截断,只保留Content-Length
长度的内容。
- 当服务端接收的请求实体长度超过
- 当内容的实际字节数小于
Content-Length
的值时:- 当服务端接收的请求实体长度小于
Content-Length
,因为接收到的长度不够服务端会继续等待后面的数据,直到超时。 - 当服务端返回的响应实体长度小于
Content-Length
,Chrome 浏览器会报net::ERR_CONTENT_LENGTH_MISMATCH
的错误。
- 当服务端接收的请求实体长度小于
注意: 在多数浏览器中发起 AJAX 请求时是不能自定义 Content-Length
头的(出于安全原因),以 Chorme 为例会报 Refused to set unsafe header "Content-Length"
的错误。
关于 PHP 的应用技巧:
一般来说浏览器在接收到 Content-Length
单位个字节后就会视为本次请求完毕,所以利用 Content-Length
可以实现在请求完毕后继续执行一些任务最后才结束我们的 PHP 进程。示例如下:
<?php header('Content-Length: 12'); echo 'i love http!'; sleep(6); file_put_contents('./1.txt', '123'); |
访问上述代码,可以发现输出 i love http!
后请求就完成,但是 PHP 的进程仍在运行,直到 6 秒后写入文件 1.txt
进程结束。
这种方式适用于能够计算响应内容长度的情况,但如果响应长度不确定,比如我们要返回一个很大的响应,因为要节省内存所以采用分段读入,处理,输出的策略,那么这种情况就不能用这个方法了,因为无法提前计算出内容的长度。不过如果你使用了 PHP-FPM
可以使用 fastcgi_finish_request 函数实现这个功能。
Transfer-Encoding
Transfer-Encoding
表示传输的编码,一般情况下值都是 chunked
,表示分块传输。使用 chunked
分块传输解决的就是 Content-Length
无法提前计算出来的情况,所以Transfer-Encoding
和 Content-Length
是互斥的,如果同时出现,浏览器以 Transfer-Encoding
为准。 使用 chunked
后响应的示例如下:
HTTP/1.1 200 OK Content-Type: text/plain Transfer-Encoding: chunked 7\r\n Mozilla\r\n 9\r\n Developer\r\n 7\r\n Network\r\n 0\r\n \r\n |
Content-Type
Content-Type
表示响应实体的 媒体类型。
Accept
Accept
表示客户端能接受响应实体的 媒体类型。
还有很多重要的头比如 cookie 相关的、跨域相关的、缓存相关的,篇幅所限暂不解释,请移步至 腾讯云社区 / HTTP 文档 / Headers。
0x02 请求方法
方法 | GET | POST | PUT | DELETE | PATCH | OPTIONS | HEAD |
---|---|---|---|---|---|---|---|
请求实体有内容 | 否 | 是 | 是 | 否 | 是 | 否 | 否 |
请求成功时响应实体有内容 | 是 | 是 | 否 | 否 | 否 | 是 | 否 |
安全 | 是 | 否 | 否 | 否 | 否 | 是 | 是 |
幂等 | 是 | 否 | 是 | 是 | 否 | 是 | 是 |
可缓存 | 是 | 否 | 否 | 否 | 否 | 否 | 是 |
允许出现在 HTML 表单 | 是 | 是 | 否 | 否 | 否 | 否 | 否 |
0x03 状态码
原谅我的懒惰,直接点击查看 状态码表格。
列出一些常用的状态码:
- 200 201 202 204
- 301 302 304
- 400 401 403 404 405 422
- 502 503 504
常见问题
301 和 302 的区别?
301 重定向作为永久重定向,也意味着它是可以被缓存的,举个例子:用户第一次访问 A.com,服务器响应 301 并重定向至 B.com,重定向的结果会默认被浏览器缓存下来,当用户再次访问 A.com 时,浏览器并不会向 A.com 发起请求,而是直接跳转到 B.com。而 302 重定向则默认不会被浏览器缓存下来,意味着每次都会请求原服务器,再按服务器返回的响应执行重定向操作。当然通过设置缓存控制头可以改变默认的缓存策略。
301 和 302 导致 POST 请求变 GET(POST 请求参数丢失)?
当使用 POST
方法请求时如果服务端返回 301 或 302 状态码并指定重定向地址,那么大多数浏览器会采用 GET
方法请求重定向地址,这意味着之前 POST
请求所带的参数会丢失!如果你不希望请求的方法被改变,则服务端应该返回 307(对应 302)或 308(对应 301)。