/

HTTP系列(一):HTTP的基础概念概述

前言

在网上看到这篇很棒的文章, 所以接下来, 我会边学习边翻译HTTP系列这篇文章。
如果想阅读原文, 你可以访问这个网站: https://code-maze.com/

原作者是一个很nice的人, 也花了挺多时间写这篇文章还有维护这个网站的, 相信大家能在网站里找到更多自己想要的东西。

正文

本文会对HTTP的基础概念进行介绍。

为什么要学HTTP?

你可能会问:为什么要了解HTTP协议?

如果你是软件开发人员, 学习HTTP协议通信过程, 能让你知道怎么样才能写出更好的应用程序。
如果你是系统架构师或者是网络管理员, 学习HTTP通信过程, 能让你进一步学习如何设计复杂的网络体系结构。

表现层状态转换(REST, Representational State Transfer)是现在一种很重要的架构风格, 使用这个架构时我们需要先了解HTTP协议的特性, 因此, 学习HTTP协议就变得尤为重要了。如果你想做好RESTful架构的产品, 你就必须先学习HTTP协议。

我必须提醒你, REST架构并不是只能使用HTTP协议的, 它也能用其他的协议来实现。但是, 由于HTTP应用的广泛性, 它在那场战役(选择实现REST架构的协议的那场战争)中获得了胜利, 因此给予找不到用其他协议来实现的REST架构。

难道, 你想错过这个了解、学习万维网和网络通信的基本概念的机会吗?

我觉得你不会想错过的🙂

本文会尽可能简单地介绍HTTP重要的部分。这篇文章整合了HTTP中有用的信息, 省去你们花费在书籍和RFC文档上寻找需要信息的时间。

本文是HTTP系列的第一篇文章, 它简要地介绍HTTP的基础概念。

文章概要:

  • HTTP的定义
  • 资源
  • 如何在WEB客户端和服务器之间交换信息?
  • 报文实例
  • MIME类型
  • 请求方法
  • 报文首部
  • 状态码

闲话少说, 直接进入下面的学习吧!

HTTP的定义

纵观HTTP的发展史,有两个重要人物,一个是发明万维网和HTTP协议的Tim Berners-Lee, 另一个是REST架构风格的发明者Roy Fielding。

超文本传输协议(The Hypertext Transerfer Protocol)是应用程序之间的通信协议。本质上, HTTP协议是负责委派互联网中客户端和服务器端之间的媒体文件(包括HTML、图片、文本文件、电影等客户端和服务器端之间传输的所有文件), 通过HTTP协议媒体文件能够快速、可靠地传输。HTTP被用于应用层通信, 因此HTTP协议是应用层协议而不是传输层协议。为了让你们记起相关知识, 我在下面放出了网络栈的构成图。

在上图中, 你可以清晰地看到, HTTP是应用层协议, 而TCP才是传输层协议。

资源


因特网上所有的内容都是资源(Resource), HTTP正是处理这些资源的协议——包括文件、(数据)流、 服务和其他所有的东西。HTML页面、youtube上的视频、WEB应用程序上日常任务电子表格…这些都是资源。(相信通过上面的举例)你已经知道了什么是资源了。

接下来就要思考另一个问题了, 如何区分不同的资源?

通过给资源标识URLs(Uniform resource locators, 统一资源定位符)——这里URL和URI(统一资源标识符)在大部分时候都可以混用。

URL指向唯一的(资源存放)位置。

如何在Web客户端和服务器端之间交换信息?

所有的内容、资源都是存放在一些WEB服务器(HTTP服务器)上的。想要获取资源, 那么需要你发送HTTP请求给这些(HTTP)服务器。

但是, 怎么样才能从WEB服务器请求(得到)资源?

当然, 你必须先有一个支持HTTP协议的客户端🙂。

你现在阅读文章时所使用的Web浏览器, 就是HTTP客户端。

Web浏览器与HTTP服务器进行通信,之后, 将从服务器上获取的资源传送到你的电脑中。

(市面上)最流行的客户端有:Google的Chrome浏览器、Mozilla的火狐浏览器、Apple的Safari浏览器、(居然还有)臭名昭著的Internet Explorer浏览器。
注:作者原本写的是unfortunately,我感觉这种“哀其不幸, 怒其不争”的语气去吐槽还有人用着Internet Explorer这件事, 用“居然”会稍微合适一点

报文实例

那, HTTP报文到底长什么样?

多说无益, 下面列出HTTP报文的示例:

GET请求

1
2
3
4
5
GET /repos/CodeMazeBlog/ConsumeRestfulApisExamples HTTP/1.1
Host: api.github.com
Content-Type: application/json
Authorization: Basic dGhhbmtzIEhhcmFsZCBSb21iYXV0LCBtdWNoIGFwcHJlY2lhdGVk
Cache-Control: no-cache

POST请求

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
POST /repos/CodeMazeBlog/ConsumeRestfulApisExamples/hooks?access_token=5643f4128a9cf974517346b2158d04c8aa7ad45f HTTP/1.1
Host: api.github.com
Content-Type: application/json
Cache-Control: no-cache

{
"url": "http://www.example.com/example",
"events": [
"push"
],
"name": "web",
"active": true,
"config": {
"url": "http://www.example.com/example",
"content_type": "json"
}
}

通过上面的例子, 我们来快速地比较一下GET请求报文和POST请求报文。

请求报文的第一行是请求行。请求行包括请求方法、请求URI和HTTP版本。

接下来几行是请求首部字段。请求首部字段提供请求报文的附加信息, 如请求响应的内容类型、授权信息等。

对于GET请求来说, HTTP报文到这里结束了。而POST请求还可以传输(实体)主体, 并且可以以报文主体的形式携带附加信息。在创建GitHub webhook(网络回调)时, 会产生有附加信息的JSON数据——关于如何为有URI标识的特定项目创建webhook——这个数据在创建webhook时是必不可少的, 通过POST请求向GitHub API 提供该数据。

注:《图解HTTP》一书中提到实体主体和报文实体的区别, 一般来说实体主体和报文主体是相同的, 只有在编码时会改变实体主体,也就是说如果我分别用deflate(zlib)和identity(不进行编码)对内容进行编码, 只会影响实体主体而不会影响报文实体。

HTTP报文的请求行和各种首部字段——即报文首部——后面必须有 (回车符\r和换行符\n), 并且报文首部与的报文主体之间存在一个空行, 该空行仅包含 CRLF。

HTTP 请求协议相关内容参照: https://www.w3.org/Protocols/rfc2616/rfc2616-sec5.html

之后, 服务器端会如何响应这些请求呢?

响应报文

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
HTTP/1.1 200 OK
Server: GitHub.com
Date: Sun, 18 Jun 2017 13:10:41 GMT
Content-Type: application/json; charset=utf-8
Transfer-Encoding: chunked
Status: 200 OK
X-RateLimit-Limit: 5000
X-RateLimit-Remaining: 4996
X-RateLimit-Reset: 1497792723
Cache-Control: private, max-age=60, s-maxage=60

[
{
"type": "Repository",
"id": 14437404,
"name": "web",
"active": true,
"events": [
"push"
],
"config": {
"content_type": "json",
"insecure_ssl": "0",
"url": "http://www.example.com/example"
},
"updated_at": "2017-06-18T12:17:15Z",
"created_at": "2017-06-18T12:03:15Z",
"url": "https://api.github.com/repos/CodeMazeBlog/ConsumeRestfulApisExamples/hooks/14437404",
"test_url": "https://api.github.com/repos/CodeMazeBlog/ConsumeRestfulApisExamples/hooks/14437404/test",
"ping_url": "https://api.github.com/repos/CodeMazeBlog/ConsumeRestfulApisExamples/hooks/14437404/pings",
"last_response": {
"code": 422,
"status": "misconfigured",
"message": "Invalid HTTP Response: 404"
}
},
]

除了第一行(状态行), 响应报文与请求报文的结构几乎相同, 惊奇地发现响应报文的状态行(实际上)是响应状态的相关信息。

在状态行之后, 是响应报文的各种首部字段和响应报文主体。

注:响应状态包括相应结果的状态码, 原因短语和HTTP版本

HTTP响应报文参照: https://www.w3.org/Protocols/rfc2616/rfc2616-sec6.html

MIME类型

MIME类型是互联网中用于描述文件类型的一种标准化方法。 (我们使用的)浏览器上就有一个MIME类型的列表,同样的, WEB服务器端也有一个MIME类型的列表, 通过这种方法, 我们就可以在不同的操作系统间传输常见类型的文件。

有趣的是, 实际上MIME是多用途因特网邮件扩展的意思,它们一开始是邮件用来处理不同类型的数据的。但是从那之后, MIME(就)被改造成为HTTP协议和其他协议使用的机制了。

MIME类型由类型(type)、子类型(subtype)、可选参数(optional parameters)构成, 格式为:类型/子类型;可选参数。

例子如下:

1
2
3
Content-Type: application/json
Content-Type: text/xml; charset=utf-8
Accept: image/gif

你可以在HTTP指南中查看常见的MIME类型和子类型列表。

请求方法

HTTP请求方法(也被称为HTTP动词)定义了将在资源上执行的操作。HTTP中有多种请求方法, 最常用的是GET方法和POST方法。

请求方法有幂等性和非幂等性之分。“幂等性”与“非幂等性”只是花里胡哨的术语, (实际上)它是用来反映某一请求方法多次调用同一资源的操作安全与否的。这意味着, 为了安全, GET方法—用于获取资源的方法—默认情况下应该是幂等性的方法。在用GET方法调用同一资源时, 不应该产生不同的响应结果。与之相对, POST方法就不应该是幂等性方法。

注:关于幂等性, 可以看看理解HTTP幂等性这篇文章, 里面的解释还是比较容易理解的。

HTTP/1.1版本之前,只有GET、POST、HEAD三种请求方法。在HTTP/1.1规范中, 引进了许多新的方法:OPTIONS、PUT、DELETE、TRACE、CONNECT。

如果想要进一步了解请求方法, 可以参考HTTP指南

报文首部

报文首部域是由冒号分割的首部字段名和字段值组成的, 反映了服务器端或客户端请求或响应的内容及属性, 位于请求报文或响应报文第一行之后。

这里有五种报文首部类型:

  • 通用首部字段(General headers):该首部字段被应用于客户端和服务器端双方。举一个常见的例子,日期首部字段(Date headers)就指明了创建报文的日期时间。

  • 请求首部字段(Request headers):该首部字段仅在请求报文中出现, 向服务器端提供附加信息。例如, “Accept:/“告知了服务器客户端可处理任何媒体类型。

  • 响应首部字段(Response headers)
    :该首部字段仅在响应报文中出现,想客户端提供附加信息。例如, “Allow:GET, HEAD, PUT”告知了客户端服务器能接受的请求资源方法。

  • 实体首部字段(Entity headers):该首部字段用于实体主体。例如”Content-Type: text/html”告知应用程序(传输)的数据是HTML文件。

  • 扩展首部字段(Extension headers):这些非标准首部字段可由应用程序开发人员创建, 尽管该首部字段不在HTTP规范中, 但仍可使用。
    你可以在HTTP指南中查看常用请求首部和响应首部字段。

状态码

状态码(Statue code)是一个三位数字, 表示请求的结果。原因短语(Reason phrase)是用自然语言表示的状态代码解释, 跟随在状态码之后。

例子如下:

  • 200 OK —— 表示从客户端发来的请求在服务器端正常处理了
  • 404 Not Found —— 服务器上无法找到请求资源
  • 500 Internal Server Error —— 服务器端在执行请求时发生错误

状态码根据不同的范围分为五种不同的类型。

如果想查看状态码的分类和完整的状态码列表及其含义, 可以看HTTP指南

总结

emmm, 这个信息量挺大的。

学习HTTP协议的基础概念, 虽然不能帮助你直接解决问题, 但是从中学习到的互联网通信的基本原则, 几乎适用于所有比HTTP协议更高级别的问题。不管是 REST、APIs、WEB应用程序开发还是网络方面出现的问题, 现在, 在解决这些问题时, 你至少可以更加自信一点。

不可否认, HTTP讲起来是一个相当大的话题, 之后的内容, 与HTTP协议的基本概念比起来多多了。

HTTP体系结构方面会在HTTP系列(二)/)中介绍。

名词解释与概述:

REST

REST(Representational State Transfer)——表现层状态转换, 是一种用来描述网络中Client和Server的交互形式的协议。
下面对名词进行解释:

  1. 表现层(Representation), 直白地说就是资源(信息实体)具体呈现出来的形式, 例如:文本表现为TXT格式、HTML格式或者是XML格式。

  2. 状态转换(State Transfer), 这个状态转换实际上是发生在Client访问Server的过程中。为什么会选择无状态HTTP作为实现REST架构的协议? 因为HTTP可发送四种状态:GET、POST、PUT、DELETE。这四个HTTP协议的动词可以用来进行不同的操作:GET用来获取资源, POST用来新建更新资源,PUT用来更新资源,DELETE用来删除资源。在写的时候我自己有点小迷糊, 什么是有状态的? 什么是无状态的呢? 无状态, 其实就是所有的状态都保存在Server。因此, 状态转换, 是在信息由Client传送到Server的中间过程实现的。
    RESTful