由协议版本、状态码、描述信息组成的行被称为起始行,服务端返回的响应报文中的第一行便是它,然后是响应头和响应体。
而本篇文章,我们来详细聊一聊状态码,看看它都有哪些,以及含义是什么?
首先状态码由三位数字组成,按照第一个数字的不同,可分为五个类别,每个类别的含义如下。
- 1xx(信息响应):这类状态码表示临时响应,意思是告诉客户端,服务端已收到部分请求,请你继续发送剩余的部分;
- 2xx(成功):这类状态码表示客户端的请求已被成功处理;
- 3xx(重定向):这类状态码表示为了完成请求,需要进一步的操作,比如跳转到新位置;
- 4xx(客户端错误):这类状态码表示请求不合法,比如操作一个没有权限的资源或者不存在的资源等等;
- 5xx(服务端错误):这类状态码表示服务端内部在处理请求时出现错误,比如服务端的代码报错;
下面我们来分别介绍这五类状态码都有哪些,以及状态码对应的描述是什么。
1xx 状态码
1xx 系列的 HTTP 状态码属于信息响应类别,这类状态码用于指示客户端请求的初始部分已被接收,并且客户端应继续其请求过程。
那么该系列的状态码都有哪些呢?
100 Continue
如果客户端发送的请求体很大,比如上传大文件,那么在发送整个请求体之前,可以先发送请求的头部(包含一个 Expect: 100-continue 字段)。相当于告诉服务端,大的要来了,如果同意发送请求体,那么就返回一个 100 Contiune 响应。
而服务端在接收到带有 Expect: 100-continue 的请求头部时,可以先进行一些预检查,比如验证请求头部的有效性、检查是否有足够的资源处理请求、或者检查客户端是否有权限。如果这些初步检查通过,服务端则返回 100 Continue 响应,指示客户端继续发送请求体。
因此 100 Contiune 主要用于提高大型请求的处理效率,尤其是在带宽受限或服务端处理能力有限的情况下。然而并非所有的 HTTP 客户端和服务端都支持这个机制,在不支持的情况下,客户端将直接发送整个请求体,不会等待 100 Continue 响应。
1xx 系列的状态码从 HTTP/1.1 才开始支持,HTTP/1.0 不支持。
101 Switch Protocols
这个应该都很熟悉,它一般用于协议升级,比如将 HTTP 协议升级成 WebSocket。
首先不管使用什么协议,都有一个握手的过程,它是建立网络连接时双方进行的一系列交互,旨在确保双方能够成功地通信。不同的协议有不同的握手机制,但它们通常都包括以下基本步骤:
- 协议识别:在握手过程的开始,通信双方确定要使用的协议以及版本;
- 参数协商:根据所选协议,双方可能需要协商一些参数,如传输速率、加密方式、数据格式等;
- 身份验证:在某些协议中,如 SSL/TLS(用于 HTTPS),握手过程还包括身份验证,这是为了确保通信双方的身份是合法和可信的;
- 连接确认:一旦上述步骤完成,双方将相互确认连接已建立,此时数据传输便可以开始;
但有一些协议在实现握手时,搭了 HTTP 协议的便车,比如 WebSocket,它的握手过程其实就是一个 HTTP GET 请求。利用 HTTP 本身的协议升级特性,伪装成 HTTP,这样就能绕过浏览器沙箱、网络防火墙等限制。
因此建立 WebSocket 连接时会发送一个 GET 请求,并带上两个专用的头字段,表示这不是普通的 HTTP GET,而是要进行协议升级。
- Connection: Upgrade,表示要求协议升级;
- Upgrade: websocket,表示要升级成 WebSocket 协议;
另外,为了防止普通的 HTTP 消息被意外识别成 WebSocket,握手消息还额外增加了两个用于认证的头字段。
- Sec-WebSocket-Key:一个 Base64 编码的 16 字节随机数,作为简单的认证密钥;
- Sec-WebSocket-Version:协议的版本号,当前必须是 13
服务端收到 HTTP 请求报文,看到上面的四个字段,就知道这不是一个普通的 GET 请求了,而是 WebSocket 的升级请求,表示客户端想要建立 WebSocket 连接。
如果服务端同意建立 WebSocket 连接,那么会给客户端返回 101 Switching Protocols 响应报文,客户端收到之后,就知道服务端同意了。
102 Processing
该状态码用于 WebDAV 协议,WebDAV 请求可能包含很多涉及文件操作的子请求,需要很长一段时间才能完成。因此服务端可以直接返回 102 Processing,表示请求已被接收并正在处理中,但目前无响应可用。这样可以防止客户端因长时间得不到响应,而假设请求丢失。
关于 1xx 系列的状态码就是以上几种,它们被称为信息性状态码,用于表示请求的中间状态,因此在日常的 Web 浏览中并不常见。但 1xx 状态码在复杂的 HTTP 通信中扮演重要角色,特别是在优化性能和处理大型请求、复杂请求时。
2xx 状态码
2xx 系列的状态码应该最受开发者喜欢了,因为它表示请求被成功处理。
200 OK
这是最常见的成功状态码,它表示请求已成功处理,并且响应体中包含了请求的结果。
201 Created
同样表示请求已被成功处理,并且还创建了一个新资源。
当客户端通过 POST 请求向服务端发送数据以创建新资源(如新的数据库记录、文件等)时,如果请求成功并且新资源被创建,服务端会响应 201 Created。
与 200 OK 相比,201 Created 响应不仅表明请求成功,而且还指出了一个新的资源已经产生。响应中一般会包含一个 Location 字段,指明新创建资源的 URI,这对客户端来说非常有用,因为它可以直接使用这个 URI 来访问新创建的资源。
当然啦,由于习惯,很多时候我们还是会返回 200,并将新创建资源的信息放在响应体中。具体使用哪种看个人习惯,总之 200 和 201 没太大区别,只是 201 意味着要明确地告诉客户端,新的资源已经创建了。
202 Accepted
表示服务端已收到请求,但尚未处理完成,这种设计可以覆盖很多的场景,例如异步、需要长时间处理的任务等。至于最后请求是成功还是失败,则是未知的。
203 Non-Authoritative Information
该状态码表示客户端使用了代理服务器,代理服务器将客户端请求转发给服务端之后,被成功处理了。但代理服务器在将响应转发给客户端时,对原始响应内容进行了修改。
因此代理服务器也要将状态码(例如 200)修改为 203,来告知客户端这一情况,方便客户端做出相应的处理。注:203 响应可以被缓存。
204 No Content
该状态码表示请求已被成功处理,但服务端没有返回任何内容,指示客户端不需要更新当前的页面视图。
205 Reset Content
该状态码表示请求已被成功处理,服务端同样没有返回任何内容,但指示客户端需要更新当前的页面视图,例如清空表单内容或重置控件。
206 Partial Content
该状态码表示服务端已经成功处理了客户端发送的部分请求,主要用于以下场景。
分块请求:当客户端只请求资源的一部分,比如客户端使用 Range 请求头指定要下载文件的特定部分时,服务端会使用 206 Partial Content 响应。
断点续传:在文件下载过程中,如果下载被中断(例如因为网络问题),那么恢复正常时,客户端可以只请求未下载的部分。服务端对这种请求的响应就是 206 Partial Content,允许客户端继续从中断的地方下载,而不是重新开始。
流媒体:在流媒体应用中,客户端可能只请求媒体文件的一小部分,以便快速加载和播放,服务端对这种请求的响应也使用 206 Partial Content。
响应头信息:在返回 206 Partial Content 时,服务端会包含有关响应内容的信息,如 Content-Range 头部,这个头部指明了返回的部分内容在整个资源中的范围。
减少数据传输:使用 206 Partial Content 可以减少网络带宽的使用,因为它允许客户端仅请求和下载所需的数据部分。
206 Partial Content 状态码是一个高效处理大型资源请求的重要机制,特别是在网络条件不稳定或资源非常大的情况下,它可以显著提高数据传输的效率和可靠性。
207 Multi-Status
该状态码用于 WebDAV 协议,当一个客户端请求对多个资源产生作用时,服务端会以 XML 的形式返回多个资源的状态,此时状态码为 207。
所以 207 Multi-Status 状态码在处理操作多个资源的请求时非常有用,特别是在 WebDAV 等分布式文件系统中,它提供了一种高效且灵活的方式来同时报告多个资源的状态。
208 Already Reported
该状态码通常和 207 一起使用,在 WebDAV 中,一个操作可能涉及多个资源,而这些资源可能又包含在不同的集合中。通过使用 208 Already Reported,服务端能够高效地表示哪些资源已经被处理,从而避免在多状态响应中重复报告。
3xx 状态码
3xx 系列的状态码属于重定向类别,用于告知客户端资源的获取方式已经改变,需要采取额外动作以完成请求。
比如访问一个已经废弃的链接,服务端就会返回 3xx 状态码,并在响应头的 Location 字段中指定新链接。客户端发现状态码为 3xx 之后,就会自动重定向到 Location 中指定的新链接。
另外在 RFC2068 中,规定客户端重定向次数不应超过 5 次,以防止死循环。
300 Mutiple Choices
当请求的资源有多种表示时,服务端会返回 300,并提供一个资源列表,让客户端自行选择。由于缺乏明确的细节,因此该状态码不常用。
301 Moved Permanently
表示请求的资源已经被永久移动到了一个新的位置,该状态码的使用场景如下。
永久性重定向:当服务端返回 301 Moved Permanently 响应时,它表明请求的资源已经永久地移动到了由 Location 字段指定的 URL,未来所有对该资源的请求都应该使用这个新的URL。
更新书签和链接:301 状态码告诉客户端(如浏览器或搜索引擎)更新其链接或书签。对于搜索引擎优化(SEO)来说,这意味着应将原先页面的权重转移给新的 URL。
搜索引擎优化:在SEO的背景下,使用 301 Moved Permanently 是管理网站结构变化的最佳实践。它有助于维持旧 URL 的搜索排名和信誉,并将其传递给新 URL。
重定向方法:与临时重定向(如 302 Found 或 307 Temporary Redirect)不同,301 是永久性的。这告诉客户端在未来的所有请求中都应使用新的 URL,而不是临时性地查找资源。
302 Found
表示请求的资源临时位于不同的 URL,该状态码的使用场景如下。
临时性重定向:当服务端返回 302 Found 响应时,它表明请求的资源现在暂时位于由 Location 头部指定的不同 URI 中。与 301 Moved Permanently 不同,302 Found 表示这种重定向只是暂时的。
原始 URL 保持有效:服务端期望客户端在未来的请求中继续使用原始的 URL,这意味着临时重定向后,原始 URL 仍然被视为有效。
搜索引擎处理:对于搜索引擎优化(SEO)来说,由于 302 Found 指示的是临时重定向,搜索引擎通常保持对原始 URL 的索引,而不是转移到新的地址。
其它用途:302 Found 也可以用于在网站维护期间或在进行 A/B 测试时,重定向到不同的 URL,同时保持原 URL 的有效性。
303 See Other
该状态码表示请求资源的响应需要通过另一个 URI 获取,并且要通过 GET 方法访问该 URI。
当服务端处理完 POST 请求(如表单提交)后,它可以发送 303 See Other 状态码,告诉客户端通过 GET 方法从指定的 URI 获取资源或信息,这样做可以防止刷新页面时重新提交表单。
所以 303 See Other 状态码在处理特定类型的 Web 请求(尤其是表单提交)并进行安全重定向时非常有用,它通过强制将后续请求的方法改为 GET,防止了表单重复提交这类问题。
304 Not Modified
该状态码表示从上次请求后,请求的资源未发生更改,因此客户端可以继续使用其缓存的版本。
关于资源缓存,这里解释一下。当服务端返回的响应中包含以下字段,代表资源可以被缓存。
Cache-Control:这是最重要的缓存头部之一,它可以设置多种指令来控制资源的缓存行为。如 max-age 指定资源可以缓存多久,no-cache 和 no-store 指示不缓存资源等。
- Cache-Control: max-age=3600,缓存 3600 秒;
- Cache-Control: no-cache,不缓存资源;
- Cache-Control: no-store,不缓存资源;
关于 no-cache 和 no-store,虽然都表示不缓存资源,但它们之间还是有一些区别的。
- no-cache 指示响应不应该被缓存,而是在每次请求时都要向服务端验证其有效性。它并不意味着资源不能被缓存,而是在使用缓存的副本之前,客户端(如浏览器)必须向服务端进行确认,检查资源是否更新。这种方法通常用于保证获取到的信息是最新的,同时仍然允许利用缓存来减少一些不必要的数据传输。
- no-store 是一种更加严格的指令,它告诉浏览器和中间缓存(如代理服务器),不应该存储任何关于客户端请求和服务端响应的信息。该指令通常用于敏感数据,如银行页面或个人资料页面,确保这些数据在传输后不会留在缓存中,从而提供更高的隐私保护。
Expires:该头部提供一个日期,该日期之后资源被认为过期。如果提供了 Cache-Control,那么 Expires 头部通常会被忽略。
Last-Modified:指示资源最后被修改的时间,客户端可以在后续的请求中使用 If-Modified-Since 头部来检查自该日期以来资源是否被修改。
ETag:提供资源的特定版本的标识,客户端可以在后续请求中使用 If-None-Match 头部,带上这个标签来检查资源是否有更新。
当客户端拥有某资源的缓存副本,并通过发送一个条件性请求(通常包含 If-Modified-Since 或 If-None-Match)来检查资源是否更新时,如果资源未更新,服务端就会返回 304 Not Modified。这意味着客户端无需下载该资源,可以使用其缓存的副本,否则将返回 200 OK 和新的资源。
因此和其它 HTTP 响应不同,304 Not Modified 响应通常不包含响应体,这是因为实际的内容没有更改,不需要重新传输。
304 有助于减少不必要的网络带宽使用,因为它避免了重新下载未更改的资源。所以 304 常用于网页和 Web 应用中,以优化性能,例如浏览器可能缓存网站的静态资源,并在后续访问时使用 If-Modified-Since 头部来检查这些资源是否已更新。
总的来说,304 Not Modified 状态码在管理 Web 资源缓存方面发挥着重要作用,减少了不必要的网络流量和加载时间,从而提高了网站或 Web 应用的性能,特别是对于那些需要频繁检索大量静态资源的网站。
307 Temporary Redirect
注:305 和 306 已经被废弃了,这里简单了解一下即可。
- 305 Use Proxy:该状态码表示请求的资源必须通过代理访问,代理的地址由 Location 字段给出。
- 306 Switch Proxy:该状态码表示后续请求应使用不同的代理来访问资源。
305 和 306 在现如今的网络请求中不会遇到,因此简单带过,我们直接看 307。
首先 307 和 302 的作用相同,都表示临时性重定向,即要访问的资源临时移动到了另一个 URI 上,但在 HTTP 方法的处理方面,两者有所差异。
307 Temporary Redirect 要求客户端在后续的重定向请求中使用与原始请求相同的 HTTP 方法。例如原始请求是一个 POST 请求,那么在重定向后,客户端也必须使用 POST 方法发送到新的 URI。
而 302 Found 最初设计时也要求客户端保持相同的请求方法,但在实际使用中,许多客户端(如浏览器)会将后续的重定向请求改为 GET 方法,即使原始请求是 POST 或其它方法。虽然这种行为与 HTTP/1.1 规范不符,但已经成为事实上的标准。
所以 302、303、307 都表示临时性重定向:
- 302 Found 的原始设计意图是重定向时使用的方法不变,但许多客户端(如浏览器)会默认使用 GET,当然也有客户端没有这么做。因此这就增加了模糊性,于是便有了 303 和 307;
- 303 See Other 明确指出客户端在重定向时应该使用 GET 方法,无论原始请求使用的是哪种方法。因此 303 可以确保在处理完 POST 请求(如表单提交)后引导浏览器加载一个新页面,并且刷新或后退操作不会再次提交表单;
- 307 Temporary Redirect 则明确指出重定向时,使用的请求方法要保持不变;
308 Permanent Redirect
308 和 301 是类似的,都表示永久性重定向,但 301 可能会导致请求方法从 POST 转变为 GET,而 308 则明确要求请求方法保持不变。
以上就是 3xx 系列的状态码,它们使得资源的迁移、缓存、优化更加灵活和有效,是网络基础设施中不可或缺的一部分,帮助网站和应用程序管理资源的更改和更新。
4xx 状态码
4xx 系列的 HTTP 状态码表示客户端错误,这些状态码指出请求中有错误,导致服务端无法或不会处理该请求。
400 Bad Request
该状态码表示由于客户端错误,服务端无法处理请求,主要在以下情况发生。
- 无效的请求语法:请求的格式不正确,例如 HTTP 头部格式错误或不完整。
- 错误的请求数据:请求数据有误,例如无效的 JSON 数据,错误的参数;
- 大小问题:请求体过大,超过了服务端愿意处理的大小;
- 编码问题:请求的编码不被服务端支持;
- 无效的请求消息:请求中包含无效的消息,如错误的请求行或头部;
400 Bad Request 是一个通用的错误响应,表明服务端因客户端的错误而无法处理请求。这要求开发者对请求进行仔细检查和调整,所以正确的请求格式、有效的数据和合适的编码是避免这种错误的关键。
401 Unauthorized
该状态码表示请求未被服务端处理,因为它缺少有效的身份认证,主要在以下情况触发:
- 未提供认证信息:当请求需要身份验证,但客户端没有提供任何认证信息时。
- 认证失败:即使提供了认证信息,如用户名和密码,但这些信息不正确或无法验证。
需要注意的是,Unauthorized 这个单词的意思是未授权,而不是未认证,所以用它来描述 401 就不太准确。关于认证和授权,这两个概念容易让人混淆,我们解释一下。
认证(Authentication)就是验证当前用户的身份是否合法的过程,比如指纹打卡,当你的指纹和系统里录入的指纹相匹配时,就打卡成功。像用户名密码登录、邮箱发送登录链接、手机接收验证码等等都属于互联网中的常见认证方式,只要你能收到验证码,就默认你是账号的主人。认证主要是为了保护系统的隐私数据与资源。
授权(Authorization)则是谁(who)对什么(what)进行了什么操作(how),和认证不同,认证是确认用户的合法性,以及让服务端知道你是谁,而授权则是为了更细粒度地对资源进行权限上的划分。所以授权是在认证通过后,控制不同的用户访问不同的资源。
并且授权是双向的,可以是用户给服务端授权,也可以是服务端给用户授权。
- 用户给服务端授权:比如你在安装手机应用的时候,APP 会询问是否允许授予权限(访问相册、地理位置等权限);你在登录微信小程序时,小程序会询问是否允许授予权限(获取昵称、头像、地区、性别等个人信息)。
- 服务端给用户授权:比如你想追一个很火的剧,但被告知必须是 VIP 才能观看,于是你充钱成为了 VIP,那么服务端便会给你授予观看该剧(访问该资源)的权限。
因此 401 状态码表示的是客户端未认证,但 Unauthorized 的中文翻译是未授权。
403 Forbidden
该状态码表示服务端理解客户端的请求,但因客户端没有访问请求资源的权限,所以服务端拒绝执行该请求,即使请求是有效的。
和 401 不同,返回 401 是因为请求缺少有效的身份凭证,导致服务端不知道客户端的身份。而 403 意味着服务端知道客户端是谁,只是它没有足够的权限来触发请求的执行,因此拒绝访问。
所以回顾一下认证和授权:
- 未认证是 Unauthenticated;
- 未授权是 Unauthorized;
我们发现 401 Unauthorized 改成 401 Unauthenticated 会更合理一些,而 403 Forbbiden 对应的才是 Unauthorized,因为没有访问资源的权限不就是未授权吗,即客户端未被授予访问指定资源的权限。
认证和授权的中文很好区分,但英文就容易混淆了,因为长得比较像。
补充:为了安全起见,服务端可能会故意使用 404 Not Found 而非 403 Forbidden 来隐藏资源的存在,避免暴露敏感信息。
404 Not Found
这个应该是最知名的状态码了,它表示服务器找不到请求的资源。当客户端请求的资源在服务器上不存在或者未被发现,就会返回 404。
出现 404,主要是以下几个原因:
- 用户输入了错误的 URL;
- 本来存在的资源被删除、移动或重命名,但却没有重定向;
- 服务器或网站的配置问题,导致无法访问特定资源;
在 Web 开发中,我们通常也会自定义 404,比如你要找的页面去火星了······,等等之类的。
虽然 404 通常被视为错误,但合适的处理和用户友好的错误页面可以在很大程度上改善用户的浏览体验。对于网站管理员和开发者而言,妥善管理和最小化 404 错误是提高网站质量和用户满意度的重要部分。
405 Method Not Allowed
该状态码表示客户端以错误的 HTTP 方法去访问请求的资源,比如一个资源只接受 GET 和 POST 请求,但客户端尝试使用 PUT 或 DELETE。
而当返回 405 Method Not Allowed 时,服务端通常会在响应头中包含一个 Allow 字段,列出对该资源有效的请求方法。例如,Allow: GET, POST。
406 Not Acceptable
该状态码表示服务端无法提供与客户端在请求的 Accept 头字段中指定的内容特征相匹配的响应,换句话说,服务端无法生成客户端期望的响应格式。
比如客户端在 Accept 头部中指定了一种服务端不支持的媒体类型(如 application/xml)。
另外当客户端请求的语言(通过 Accept-Language 指定)或编码(通过 Accept-Encoding 指定)服务端无法提供时,也可能返回此状态码。
对于开发者来说,这个状态码强调了内容协商机制的重要性,即服务端需要根据客户端的要求提供不同格式的响应。对于终端用户来说,遇到 406 错误通常意味着他们的客户端(例如 Web 浏览器或应用)发送了服务端无法满足的请求。在某些情况下,用户可以尝试更改请求的格式。
407 Proxy Authentication Required
与 401 类似,但它表示请求需要通过代理发送,而客户端没有通过代理服务器的认证。
以上是 4xx 系列比较常见的状态码,还有一些不常见的,我们简单罗列一下。
- 408 Request Timeout:表示客户端未在服务端预备等待的时间内完成请求的发送;
- 409 Conflict:表示请求与服务端当前状态冲突,常见于并发编辑同一资源导致的问题;
- 410 Gone:表示请求的资源已被永久删除,类似于 404,但它是永久性的,明确告知客户端该位置永远找不到指定的资源;
- 411 Length Required:服务端要求请求必须包含 Content-Length 头部;
- 412 Precondition Failed:服务端未满足请求头中的一个或多个前提条件;
- 413 Payload Too Large:请求实体太大,服务端拒绝处理请求;
- 414 URI Too Long:请求的 URI 过长,服务端拒绝处理;
- 415 Unsupported Media Type:客户端在其请求中指定了服务端无法或不愿处理的媒体类型,比如上传一个服务端无法处理的文件格式。要注意它和 406 的区别:
406 指的是服务端无法生成客户端接受的响应类型;
415 指的是服务端不支持客户端上传的文件类型;
- 416 Range Not Satisfiable:客户端请求的范围无法满足;
- 417 Expectation Failed:服务器无法满足 Expect 请求头中的期望值;
- 429 Too Many Requests:客户端发送的请求过多,通常用于限制请求速率;
4xx 系列的状态码提供了服务端对于特定错误情况的反馈,帮助客户端开发者诊断问题并采取相应措施。
5xx 状态码
5xx 系列的 HTTP 状态码表示服务器错误,这些状态码表明客户端的请求本身可能没有问题,但由于服务器遇到问题而无法完成请求。
500 Internal Server Error
这是最常见的服务器错误状态码,表明服务端遇到了一个意外情况,阻止了其完成请求。比如服务端代码报错了,但又没有异常捕获,这个时候会直接抛出 500。
501 Not Implemented
该状态码表示服务器不支持当前请求所需要的功能,当服务器无法识别请求方法,并且无法支持其对任何资源的请求时,可能会返回这个状态码。
502 Bad Gateway
当服务器作为网关或代理,从上游服务器收到无效响应时,会返回此状态码。相信 502 大家也经常会遇到,伴随而来往往是 NGINX。
NGINX 将请求转发给后端服务,但后端服务报错了,没有提供正常响应,这时 NGINX 就会返回 502 给客户端。因此当你看到 502 时,就应该知道 NGINX 代理正常工作,但它背后的服务出错了。
503 Service Unavailable
该状态码表明服务器目前无法使用(由于超载或停机维护),通常这只是暂时状态。
504 Gateway Timeout
当服务器作为网关或代理,没有及时从上游服务器收到请求时,会返回这个状态码。
505 HTTP Version Not Supported
该状态码表明服务器不支持请求中使用的 HTTP 协议版本。
以上就是 5xx 系列常见的状态码,这些状态🐴表示客户端的请求正常,但问题出现在服务端。
小结
以上就是 HTTP 状态码,它是 Web 通信的核心组成部分,为理解客户端和服务端之间的交互提供了基础。这些状态码提供了关于请求是否成功,以及如果不成功,原因是什么的关键信息。
了解每个状态码的含义,可以让我们在 Web 开发中迅速定位到问题。