HTTP请求和响应的基础知识

74 阅读14分钟

学习万维网的基础知识是至关重要的,特别是如果你打算建立网络应用。而且,HTTP是你可以建立的任何网络应用的核心。

HTTP是超文本传输协议的缩写。它是一种无状态的应用层协议,用于在分布式系统之间进行通信,是现代网络的基础。

在这篇文章中,我将从基础知识开始。我将解释任何潜在的网络开发人员应该知道的HTTP的各个方面。然后,我们将深入探讨HTTP的更深层次和高级理论。

HTTP是一种超文本传输协议

当你在浏览器中打开一个网站时,你会看到文本、图像和嵌入式内容。所有这些内容都是从网络上其他地方的服务器上加载的。浏览器--也称为客户端--的作用是为这些内容提出请求。该请求被发送到服务器,而服务器则向浏览器发送一个响应。请求和响应都是以人类可读的文本形式发送。

浏览器发出的每个请求都是独立的。HTTP协议是无状态的。这意味着,每个单独的请求都需要携带履行请求所需的所有信息。在HTTP请求中,这些信息是通过头文件 传递的

HTTP协议支持混合的网络配置。浏览器是许多可能的客户端之一,它可以提出请求。请求是通过TCP/IP层发送的,而响应是通过TCP/IP层接收的。HTTP通信的默认端口是80端口,但对于不同的应用可以进行不同的配置。

HTTP的版本

  • 目前,客户端使用HTTP/2.0版本。最近发布的Chrome、Firefox、Safari和Edge都支持HTTP/2.0。这个版本的HTTP允许客户端同时发送多个请求。 这种技术被称为多路复用。它减少了加载一个页面所需的时间。
  • HTTP/1.1是在2014年修订的。这个版本只允许每个TCP/IP连接有一个未完成的请求。这种机制也被称为基线**。**
  • HTTP/0.9 是该协议的原始版本。目前,这个版本已经完全废弃了。

下面的流程图解释了为什么HTTP/2.0比HTTP/1.1更好。

正如所见,HTTP/2.0允许客户端同时请求样式和脚本。同样地,样式和脚本也会同时被加载。整个过程在一个单一的TCP连接中完成。

HTTP/1.1 vs HTTP/2.0!

此外,HTTP/2.0还具有智能数据包管理策略和头压缩机制,以减少延迟。

HTTP URLs

请求被发送到用统一资源定位器(URLs)指定的服务器。我相信你已经对URLs很熟悉了,但为了完整起见,我还是要把它放在这里。URLs有一个简单的结构,由以下部分组成。

www.domain.com:1234/path/to/res…

  • https指定协议。它可以是http或https,这使得通信安全。
  • www.domain.com 是主机。
  • 1234 是端口。在许多情况下,浏览器会隐藏端口。默认是80。
  • path/to/resource 是资源路径。它帮助服务器识别一个特定的资源,并产生正确的响应。
  • a=b&x=y 是查询字符串参数。查询字符串参数被服务器用来发现正确的资源。

URL确定了客户想要与之通信的特定主机。它不执行任何行动。这就是请求的来源。HTTP有一个正式的方法来构筑请求,捕获所有需要的信息,这可以被任何种类的应用程序所应用。

HTTP请求

网络通信的核心是请求信息。一个请求是由以下部分组成的。

  • 请求行:这说明正在请求什么。由一个动词、一个路径和HTTP版本组成。HTTP动词说的是要求主机采取什么行动,例如,GET资源或POST表单数据。
  • 标头:关于消息、请求者和通信格式的额外信息。
  • body (可选):请求的内容。对于像网页这样的静态资源的简单请求,它将是空的。对于一个表单的提交,这将包含表单的信息。主体与头文件之间有一个空行。

下面是一个典型的HTTP请求。

GET /articles/http-basics HTTP/1.1
Host: www.articles.com
Connection: keep-alive
Cache-Control: no-cache
Pragma: no-cache
Accept: text/html, application/xhtml+xml, application/xml;q=0.9, */*;q=0.8

第一行有动词、资源路径和HTTP版本。在这种情况下,我们试图获取**/articles/http-basics**的资源。其余的请求行是头文件,这个请求没有正文。

HTTP请求动词

在一个请求中,有四个普遍适用的HTTP动词。

  • GET:从服务器上获取一个资源。对于一个GET请求,URL应该携带所有需要的信息,以便服务器能够找到正确的资源。它没有一个信息体。
  • POST: 创建一个新的资源。该请求有可选的有效载荷,帮助服务器创建一个新资源。
  • PUT: 更新一个现有资源。该请求应该有一个可选的有效载荷,以帮助服务器更新一个现有的资源。
  • DELETE: 删除一个现有的资源。

以上四个动词是最流行的动词。有趣的是, PUTDELETE 有时被认为是 POST 动词 的专门化版本 。在某些情况下,PUT 可能被打包成一个 POST 请求,其有效载荷包含确切的动作: 创建、更新或删除。

还有一些不太常用的动词。需要考虑的几个是。

  • HEAD与GET类似,但没有信息体。它用于检索特定资源的服务器头,通常是通过时间戳来检查资源是否已经改变。
  • TRACE 是用来检索一个请求在从服务器出发的往返过程中所采取的跳数。每个中间代理或网关将把它的IP或DNS名称注入Via header字段中。这可用于诊断目的。
  • OPTIONS用于检索服务器的能力。在客户端,它可以用来根据服务器支持的内容来修改请求。

HTTP标头

HTTP头为服务器提供了关于发件人、客户想要的交互方式和消息的信息。每个标头都是一个名-值对。HTTP协议规定了客户端和服务器可以使用的所有有效的HTTP标头。

请求和响应信息中都有一些通用的头信息。

  • **Cache-Control**缓存:一个控制CDN、代理或浏览器中如何进行缓存的指令。它从HTTP/1.1开始生效。
  • **Connection**网络连接:用于决定一个请求完成后,网络连接是需要关闭还是打开。可能的值是keep-alive ,或closed
  • **Pragma**是一个有趣的,而且在很大程度上是具体实施的头。它的提供只是为了向后兼容HTTP/1.0,后者不支持Cache-Control
  • Trailer: 告诉服务器它可以将元数据附加到消息正文中,例如完整性检查或数字签名。
  • Transfer-encoding: 定义了从服务器传输的有效载荷的编码。通常,这被称为逐跳 头,因为编码是在节点之间应用,而不是在服务器和客户端之间。
  • Via 在头中用于跟踪消息,以及客户端或服务器的能力。
  • Upgrade 仅在HTTP/1.1及以上版本中可用。如果允许客户端或服务器从一个协议转变为另一个协议,就必须设置这个头。例如:Upgrade: HTTP/2.0, SHTTP/1.3, IRC/6.9, RTA/x11

下面是一些特定于请求的头信息。

  • **Accept-**前缀的头表示客户端可接受的媒体类型、编码、语言和字符集。
  • **From**, Host, **Referer**和 User-Agent 有关于发起请求的客户端的详细信息。
  • **Authorization**密码:客户端用来提供证书,服务器可以进一步使用这些证书来验证请求。这对于访问受密码保护的资源很有用。
  • **If-**prefixed headers用于使请求有条件,服务器只有在条件符合时才返回资源。否则,它将返回一个304 Not Modified
  • **Referrer**前缀:包含请求页面的部分或绝对地址。

还有许多其他的HTTP标头在使用。如果你想了解HTTP协议的一些错综复杂的问题,请阅读更多关于标头的信息。

HTTP响应

响应在结构上与请求信息相似,除了状态行和头信息。

  • 状态行:包括一个状态代码,表明请求是否成功(状态代码200)或请求为何失败。它还包括HTTP版本和对状态的一个非常简短的描述。
  • 头信息:关于响应的额外信息。例如,内容类型,或关于服务器的信息。
  • body (可选):响应的内容。例如,这可能是请求的网页的HTML内容,或图像的二进制数据。

服务器的成功响应将有一个类似于HTTP/1.1 200 OK 的状态行。

响应标头

就像在请求中一样,响应信息可以有一些标头。下面是一些常用的响应头。

  • Age :包含请求页面的部分地址或绝对地址。 : 自信息在服务器上生成以来的时间(秒)。
  • ETag:实体的MD5哈希值,用于检查修改情况。
  • Location URL:在发送重定向时使用,包含新的URL。
  • Server: 识别生成该消息的服务器。

HTTP响应也可以有一个实体头信息的集合 实体标头的作用是提供关于消息体的元信息。下面是一些典型的实体头信息。

  • Allow: 定义了一个资源可能支持的方法集。如果该资源不被支持,你将收到一个状态代码405 Method Not Allowed
  • **Content-**前缀的头信息表明响应的媒体类型、编码、语言和消息有效载荷上的字符集。例如,Content-Encoding ,用于压缩传输的数据。
  • Expires:表示响应将变得无效的日期和时间,因此该资源不应该被缓存超过该点。
  • Last-Modified:携带服务器认为响应资源最后被修改的详细时间。

状态代码

有了这么多的理论概念,你可能觉得有点昏昏欲睡。但请坚持住。我们将以HTTP响应状态码来结束我们的理论。服务器的每个响应都会有一个状态码。 状态码很重要,它告诉客户如何解释服务器的响应

1xx:信息性消息

这类代码是在HTTP/1.1中引入的,纯属临时性质。服务器可以发送一个Expect: 100-continue ,告诉客户端继续发送请求的剩余部分,如果已经发送,则可以忽略。HTTP/1.0客户端应该忽略这个头。

2xx:成功

这告诉客户端,该请求已被成功处理。最常见的代码是 **200 OK**.对于一个GET 请求的响应,服务器会在消息正文中发送资源。

  • **200 OK**表示请求被成功处理,响应体将包含任何请求的内容。
  • **202 Accepted**:请求被接受,但可能不包括响应中的资源。这对于服务器端的异步处理是很有用的。服务器可以选择发送信息进行监控。
  • **204 No Content**响应:响应中没有消息体。
  • 205 Reset Content: 表示给客户端重置其文档视图。
  • 206 Partial Content:表示响应中只包含部分内容。附加头信息表明确切的范围和内容过期信息。

3xx:重定向

这要求客户端采取额外的行动。最常见的使用情况是跳转到一个不同的URL以获取资源。

  • 301 Moved Permanently:资源现在位于一个新的URL上。
  • 303 See Other:该资源暂时位于一个新的URL上。Location 响应头包含临时URL。
  • 304 Not Modified:服务器已经确定该资源没有改变,客户应该使用其缓存的副本。这依赖于这样一个事实,即客户端正在发送ETag (实体标签)信息,这是内容的哈希值。服务器将其与自己计算的ETag 进行比较,以检查是否有修改。

4xx: 客户端错误

这些代码是在服务器认为客户端有错误时使用的,要么是请求了一个无效的资源,要么是提出了一个错误的请求。这类代码中最常见的是 **404 Not Found**,我想每个人都会认识到这一点。

  • 400 Bad Request: 该请求是畸形的。
  • 401 Unauthorized:请求需要认证。客户端可以用Authorization 标头来重复该请求。如果客户端已经包含了Authorization 标头,那么凭证是错误的。
  • 403 Forbidden: 服务器已拒绝访问该资源。
  • 404 Not Found: 表示该资源是无效的,不存在于服务器上。该类中的其他代码包括。
  • 405 Method Not Allowed:请求行中使用的HTTP动词无效,或者服务器不支持该动词。
  • 409 Conflict:服务器无法完成请求,因为客户试图修改一个比客户的时间戳更新的资源。冲突主要出现在对资源进行协作编辑时的PUT请求。

5xx: 服务器错误

这类代码用于表示在处理请求时服务器出现故障。最常用的错误代码是。

  • 500 Internal Server Error:服务器发生了某种崩溃或内部错误,使其无法完成请求。
  • 501 Not Implemented:服务器还不支持请求的功能。
  • 503 Service Unavailable:如果服务器上的一个内部系统出现故障或服务器过载,就可能发生这种情况。通常情况下,服务器甚至不会响应,请求会超时。

在网络框架和库中使用HTTP

最后,让我们看看框架是如何使用请求-响应对的。作为一个例子,让我们从NodeJS的ExpressJS开始

用Express接收HTTP请求

如果你在NodeJS中构建Web服务器,你很有可能已经考虑过 Express

Express提供了一个简单的API来编写Web服务器。我将不会介绍API的细节。相反,我将向你展示一个关于Express如何使用HTTP请求和响应的例子。

Express Hello World

const app = express()

app.get('/', (req, res) => {
    res.send('Hello World!')
})

app.get('*', (req, res) => {
    res.sendStatus(401);
})

app.listen(3000, () => {
    console.log(`Example app listening on port ${port}`)
})

在这个例子中,我们为一个路径为 / 的 GET 请求设置了一个处理程序。当服务器收到这个请求时,它将以200 OK 来回应,信息体将是Hello World。服务器将监听3000端口。对于任何其他路径的GET请求,服务器将返回一个401 Not Found 错误。

用Fetch API发送一个HTTP请求

最后,让我们看看如何从客户端发出一个HTTP请求。这曾经是通过jQuery完成的,但现代浏览器已经内置了Fetch API。而且,语法也没有变得更简单。

使用Fetch的GET请求

要从服务器上请求数据,使用fetch("https://example.com/something.html") 。这将返回一个Promise,你可以通过await 来获得响应。

let response = await fetch('https://example.com/movies.json')
let data = await response.json();
console.log(data);

在上面的例子中,我们向**example.com**发送了一个关于**movies.json**资源的 GET 请求。我们等待响应,然后提取响应体作为JSON。

使用Fetch的POST请求

为了将数据发布到服务器上,并带有可选的有效载荷,客户端使用了:fetch('https://example.com/profile', { method: 'POST'}) 。同样,它返回一个Promise,你可以等待它来获得请求的结果。

fetch('https://example.com/profile', {
  method: 'POST', 
  headers: {
    'Content-Type': 'application/json',
  },
  body: JSON.stringify(data),
})
.catch((error) => {
  console.error('Error:', error);
});

在这个例子中,我们向example.com.Weprofile 端点提交了一个post请求,希望向服务器发送JSON数据,所以我们将Content-Type 头部设置为application/json 。然后我们将数据字符串化为JSON并在请求正文中发送。

总结

恭喜你,你现在已经掌握了HTTP请求和响应的基础知识现在,你将能够在你的应用程序中使用该协议。此外,你应该能够为你的使用案例选择正确的HTTP动词和头信息。