HTTP报文概述
HTTP概述在HTTP版本发展与Google的暗中助力 中已经说得差不多了,这篇文章来专门讲一下HTTP的报文格式

HTTP 协议的请求报文和响应报文的结构基本相同,由三大部分组成:
- 起始行(start line):描述请求或响应的基本信息;
- 头部字段集合(header):使用 key-value 形式更详细地说明报文;
- 消息正文(entity):实际传输的数据,它不一定是纯文本,可以是图片、视频等二进制数据。
这其中前两部分起始行和头部字段经常又合称为“请求头”或“响应头”,消息正文又称为“实体”,但与“header”对应,很多时候就直接称为“body”。
HTTP报文是面向文本的,因此header都是一些ASCII码串,可以很容易地用肉眼阅读,不用借助程序解析也能够看懂。(所以中文通常要经过urlencode)
HTTP 协议规定报文必须有 header,但可以没有 body,而且在 header 之后必须要有一个“空行”,也就是“CRLF”,十六进制的“0D0A”
。
请求行&状态行
请求行

请求行是请求报文中的第一行,请求行由三部分构成:
- 请求方法:是一个动词,如 GET/POST,表示对资源的操作。事实上,请求方法的语义并没有被严格遵守,直到RESTful API的广泛应用,才使得请求方法的语义变得重要了起来。
方法(操作) | 意义 |
---|---|
OPTION | 请求一些选项的信息 |
GET | 请求读取由 URL所标志的信息 |
HEAD | 请求读取由 URL所标志的信息的首部 |
POST | 给服务器添加信息(例如,注释) |
PUT | 在指明的 URL下存储一个文档 |
DELETE | 删除指明的 URL所标志的资源 |
TRACE | 用来进行环回测试的请求报文 |
CONNECT | 用于代理服务器 |
关于请求方法,我们通常谈到幂等性,相关内容可以参考Http请求的幂等性
- 请求目标:通常是一个 URI,标记了请求方法要操作的资源。
- 版本号:表示报文使用的 HTTP 协议版本。
这三个部分通常使用空格(space)来分隔,最后要用 CRLF 换行表示结束。
状态行
状态行是响应报文中的第一行,与请求行的结构类似。

状态行包括三项内容:
- 版本号:表示报文使用的 HTTP 协议版本。
- 状态码:一个三位数,用代码的形式表示处理的结果。我在SpringBoot开发RESTful API中列举了常用的HTTP状态码。
- 短语:解释状态码的简单短语,帮助人理解原因。对于常见的状态码200,后面跟的短语可能就是
OK
。
头部字段(首部行)
请求行或状态行再加上头部字段集合就构成了 HTTP 报文里完整的请求头或响应头。请求报文和响应报文中的头部字段的格式都是一样的,只是字段可能有所区别。
头部字段是 key-value 的形式,key 和 value 之间用“: ”
分隔,最后用 CRLF 换行表示字段结束。比如在“Host: 127.0.0.1”
这一行里 key 就是“Host”
,value 就是“127.0.0.1”
。
使用头字段需要注意下面几点:
- 字段名不区分大小写,例如“Host”也可以写成“host”,但首字母大写的可读性更好;
- 字段名里不允许出现空格,可以使用连字符“-”,但不能使用下划线“_”。例如,
“test-name”
是合法的字段名,而“test name”``“test_name”
是不正确的字段名; - 字段名后面必须紧接着“:”,不能有空格,而“:”后的字段值前可以有多个空格;
- 字段的顺序是没有意义的,可以任意排列不影响语义;
- 字段原则上不能重复,除非这个字段本身的语义允许,例如 Set-Cookie。
Content-Type&Content-Encoding
Content-Type
:标识body的内容类型,取值通过“MIME type”
标准确定,MIME 把数据分成了八大类,每个大类下再细分出多个子类,形式是“type/subtype”的字符串,巧得很,刚好也符合了 HTTP 明文的特点,所以能够很容易地纳入 HTTP 头字段里。
这里简单列举一下在 HTTP 里经常遇到的几个类别:
text
:即文本格式的可读数据,我们最熟悉的应该就是text/html
了,表示超文本文档,此外还有纯文本text/plain
、样式表text/css
等。image
:即图像文件,有image/gif
、image/jpeg
、image/png
等。audio/video
:音频和视频数据,例如audio/mpeg
、video/mp4
等。application
:数据格式不固定,可能是文本也可能是二进制,必须由上层应用程序来解释。常见的有application/json
,application/javascript
、application/pdf
等,另外,如果实在是不知道数据是什么类型,像刚才说的“黑盒”,就会是application/octet-stream
,即不透明的二进制数据。
需要注意的是以x-
开头的类型是我们自定义的类型。使用*
代表任意类型。
Content-Encoding
:告诉数据是用的什么编码格式,这样对方才能正确解压缩,还原出原始的数据。如果响应报文里没有 Content-Encoding
字段,就表示响应数据没有被压缩。常见的Content-Encoding
:
+ gzip
:GNU zip 压缩格式,也是互联网上最流行的压缩格式;
+ deflate
:zlib(deflate)压缩格式,流行程度仅次于 gzip;
+ br
:一种专门为 HTTP 优化的新压缩算法(Brotli)。
Accept&Accept-Encoding
如果服务器一厢情愿发送特定的数据类型而客户端解析不了也不行,所以请求首部字段中通常会有Accept
字段,代表客户端可理解的 MIME type,用,
做分隔符列出多个类型。对应的,也存在Accept-Encoding
字段,代表客户端支持的压缩格式。服务器通常要通过这两个字段决定响应的数据类型。
其他Accept-和Content-字段
Accept-Language
代表客户端可理解的自然语言,Content-Language
告诉客户端实体数据使用的实际语言类型。
Accept-Charset
代表客户端可解析的字符集,响应报文并不会有Content-Charset
,而是在Content-Type字段的数据类型后面用“charset=xxx”来表示。
不过现在的浏览器都支持多种字符集,通常不会发送 Accept-Charset
,而服务器也不会发送 Content-Language
,因为使用的语言完全可以由字符集推断出来,所以在请求头里一般只会有 Accept-Language
字段,响应头里只会有 Content-Type
字段。
内容协商的质量值
我们称以上Accept-*
和Content-*
字段为客户端和服务端对传输的内容进行内容协商,在 HTTP 协议里用 Accept、Accept-Encoding、Accept-Language 等请求头字段进行内容协商的时候,还可以用一种特殊的“q”参数表示权重来设定优先级,这里的“q”是“quality factor”的意思。
权重的最大值是 1,最小值是 0.01,默认值是 1,如果值是 0 就表示拒绝。具体的形式是在数据类型或语言代码后面加一个;
,然后是q=value
。
例如:
Accept: text/html,application/xml;q=0.9,*/*;q=0.8
它表示浏览器最希望使用的是 HTML 文件,权重是 1
,其次是 XML 文件,权重是 0.9
,最后是任意数据类型,权重是 0.8
。服务器收到请求头后,就会计算权重,再根据自己的实际情况优先输出 HTML 或者 XML。
内容协商的结果
内容协商的过程是不透明的,每个 Web 服务器使用的算法都不一样。但有的时候,服务器会在响应头里多加一个Vary
字段,记录服务器在内容协商时参考的请求头字段,给出一点信息,例如:
Vary: Accept-Encoding,User-Agent,Accept
这个 Vary
字段表示服务器依据了 Accept-Encoding
、User-Agent
和 Accept
这三个头字段,然后决定了发回的响应报文。
其他常见的头部字段
Host
字段,它属于请求字段,只能出现在请求头里,它同时也是唯一一个 HTTP/1.1 规范里要求必须出现的字段,也就是说,如果请求头里没有 Host,那这就是一个错误的报文。Host 字段告诉服务器这个请求应该由哪个主机来处理,当一台计算机上托管了多个虚拟主机的时候,服务器端就需要用 Host 字段来选择,有点像是一个简单的“路由重定向”。
User-Agent
是请求字段,只出现在请求头里。它使用一个字符串来描述发起 HTTP 请求的客户端,服务器可以依据它来返回最合适此浏览器显示的页面。但由于历史的原因,User-Agent 非常混乱,每个浏览器都自称是“Mozilla”``“Chrome”``“Safari”
,企图使用这个字段来互相“伪装”,导致 User-Agent 变得越来越长,最终变得毫无意义。
Date
字段是一个通用字段,但通常出现在响应头里,表示 HTTP 报文创建的时间,客户端可以使用这个时间再搭配其他字段决定缓存策略。
Server
字段是响应字段,只能出现在响应头里。它告诉客户端当前正在提供 Web 服务的软件名称和版本号,例如nginx/1.**
。Server 字段也不是必须要出现的,因为这会把服务器的一部分信息暴露给外界,如果这个版本恰好存在 bug,那么黑客就有可能利用 bug 攻陷服务器。所以,有的网站响应头里要么没有这个字段,要么就给出一个完全无关的描述信息。
Content-Length
,它表示报文里 body 的长度,也就是请求头或响应头空行后面数据的长度。服务器看到这个字段,就知道了后续有多少数据,可以直接接收。如果没有这个字段,那么 body 就是不定长的,需要使用 chunked
方式分段传输。
原创文章,作者:彭晨涛,如若转载,请注明出处:https://www.codetool.top/article/%e8%ae%a1%e7%bd%91%e5%a4%8d%e4%b9%a007-%e5%ba%94%e7%94%a8%e5%b1%82%e4%b9%8bhttp%e6%8a%a5%e6%96%87%e6%a0%bc%e5%bc%8f/