结语与期望
最后更新于:2022-04-02 01:32:01
##贡献者
**翻译贡献者名单:**
* [Cifer-Y](https://github.com/Cifer-Y),开源贡献者,承担了本书第一稿的全部翻译工作
* [happypeter](https://github.com/happypeter),[happycasts 创始人](http://happycasts.net/),逐字逐句校正了全书各个章节
* [billie66](https://github.com/billie66),[好奇猫共同创始人](http://haoqicat.com/),维护本电子书及其网页版
* [mainframer](https://github.com/mainframer),IBM 中国,参与本书校对工作
* [Junaito Fatas](https://github.com/JunaitoFatas),台湾 Ruby 社区大牛,参与本书校对工作
* [Ruchee](https://github.com/ruchee),开源贡献者,参与本书校对工作
另外,翻译过程中的难点也咨询了英文原书作者,在此表示感谢!
## 祝贺你
祝贺你看完了这本书! HTTP 是整个互联网的核心,所以从 HTTP 开始学习 web 开发是一个很好的起点。理解 HTTP 和诸如 `GET`,`POST`,会话,cookies 还有“无状态”是什么意思是必不可少的。理解 HTTP 的基础就意味着在你构建一个 web 应用的时候你了解“在屏幕后面”发生着些什么事情。 学习 web 安全相关的知识意味着你要提防安全威胁比如 `XSS`,会话劫持等,还要了解对抗这些威胁的策略。 如果这是你第一次学习 HTTP,书里的某些章节你最好多读两遍。当你学习一个新的东西的时候,重读,复习和探索都是正常的,我们也鼓励你使用其他资源加深你对所学知识的理解。 虽然这本书没有对 HTTP 进行全面讨论,我们希望本书的知识能成为你 web 开发者职业道路上的坚实基础。
## 最后一个请求
如果你觉得这本书很不错,请让更多的人知道它。
';
安全性
最后更新于:2022-04-02 01:31:58
正如我们在这本书中反复指出的, HTTP 的特性让它不容易被居心叵测的人控制,但是同样使得让 http 变得非常安全变得富于挑战性。现在你知道 Web 应用如何优雅的解决了 HTTP 无状态的问题,但是也可想而知还是有一些安全问题需要注意。举个例子,如果有人把我浏览器里的会话 id 偷走,他是不是就能用我身份登陆了?或者当我访问别的网站的时候,它们会不会窥视我 Reddit 和 Facebook 的 cookie 里存储的一些信息,比如会话 id ?本章我们就来讨论一下常见的 HTTP 安全问题。
## 安全的 HTTP(HTTPS)
在客户端和服务器互相发送请求和响应的时候,所有的请求和响应里的信息都是通过明文字符串发送的。如果一个恶意的黑客连接到同一网络,他就可以利用数据包嗅探技术来读取来回发送的消息。正如我们已知道的,请求可以包含会话 id ,它唯一地标识你到服务器之间的联系,所以如果别人复制了这个会话 id ,他们可以手动创建到服务器的请求,伪装成你的客户端,甚至都不需要你的用户名和密码就可以自动登陆。
这种情况就需要安全的 HTTP 也就是 HTTPS 来帮忙啦。通过 HTTPS 访问资源的时候,通常以 `https://` 开头而不是 `http://` ,而且通常在边上都会有个小锁子的图标:![https](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-07-17_55a8a2b3d68dc.png)
通过 HTTPS 发送的请求和响应在发送前都会被加密。这意味着如果一个恶意的黑客监听 HTTP 通信,他得到的信息都是加密的和无用的。HTTPS 通过一个叫做 [TLS](http://en.wikipedia.org/wiki/Transport_Layer_Security) 的加密协议来加密消息。在 TLS 开发完成前,早期 HTTPS 使用 `SSL` ( Secure Sockets Layer )。这些加密协议在加密数据之前,需要先使用证书来与远程服务器进行通信来交换安全密钥。你可以点击 `https://` 前面那个小锁子的图标来查看这些证书:![certificates](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-07-17_55a8a2b636fd9.png)
在与一个网站进行交互之前,大多数现代浏览器都会替你对网站的证书做一些检查,但是有时候自己手动查看一下证书也可以作为一个额外的安全保障。
## 同源策略( Same-origin policy )
同源策略是一个重要的概念,它允许来自同一站点的资源进行互相访问而不受限制,但是会阻止其他不同站点对文档/资源的访问。换句话说它可以阻止另一个站点通过脚本来操纵本站点的文档。同源的文档必须有相同的协议,主机名和端口号。举个例子,`http://www.test.com/aboutus.html` 上的 HTML 文档可以嵌入`http://www.test.com/fancy.js` 这个 javascript 文件,因为它们是同源的,有相同的协议,主机名和端口号(默认的 80) 。
反过来说,这就意味着 `http://www.test.com` 上的文档不能嵌入 `http://www.example.com` 上的文档,因为它们不是同源的。
> 同源策略涉及的是访问文件内容,而不是链接,你可以随意链接到任何 URL。
虽然这样很安全,但是有时 web 开发人员需要进行跨域的内容访问就会很麻烦,所以就有了跨域资源共享技术 CORS 。 CORS 是一种机制,允许我们绕过同源策略,从一个域名向另一个域名的资源发起请求。CORS 的原理是添加新的 HTTP 头部,来对一些域名授权,那这些域名就可以发起对本页面资源的请求。
同源策略是防范会话劫持(见下一节)的重要手段,并作为 web 应用安全的基石。下面让我们来看一些 HTTP 的安全威胁和其对应的防范措施。
## 会话劫持( Session Hijacking )
我们已经知道,会话在维持 HTTP 的状态上扮演着重要的角色。我们也知道会话 id 作为一个唯一的令牌来唯一标识一个会话。通常,会话 id 是作为 cookie 存储在计算机上的一个随机字符串. 会话 id 随着每一个到服务器的请求被送往服务器用于唯一标识这个会话。事实上,这也就是很多 web 应用的用户认证系统所在做的事情,当用户的用户名和密码匹配之后,会话 id 会存储在用户的浏览器里,这样他们的下一个请求就不用重新认证了。
不幸的是,如果一个攻击者拿到了这个会话 id ,他就会跟用户共享这一个会话,同时也就能访问这个 web 应用了。在会话劫持攻击中,用户根本意识不到一个攻击者甚至不用知道她的用户名和密码就可以访问她的会话了。
### 会话劫持的对策
* 一个比较流行的会话劫持防范措施是重置会话。也就是对于一个用户认证系统来说,一次成功的登录包括验证旧的会话 id 和生成一个新的会话 id 。完成此步骤后,在下一个请求里,会要求受害者进行身份验证。然后会话 id 就会改变,这样攻击者就无法访问到这个会话了。很多网站都采取这种办法,当用户在进行敏感操作的时候保证用户身份的正确性,比如给信用卡充值或者删除账户的时候。
* 另一个很有用的方法是给会话设置过期时间。那些不会过期的会话给了攻击者太多的时间去伪装成一个合法用户。如果设置了过期时间,比如 30 分钟,这样一来攻击者就不会那么从容的进行攻击了。
* 最后,其实我们已经讲过了,另一个办法就是整站使用 HTTPS 把攻击者能得到会话 id 的可能性降至最低。
## 跨站脚本攻击 (XSS)
我们最后要讨论的这个安全问题,对所有 web 开发者来说都很重要,叫做跨站脚本攻击或者 **XSS** 。当你允许用户输入的 HTML 和 javascript 在你自己的网站上直接显示的时候,就有可能遭受这种攻击。
举个例子,下面这个表格允许你输入评论,然后把评论直接显示在网页上:![xss](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-07-17_55a8a2b895bf2.png)
因为这是一个普通的 HTML 文本框,所以用户可以在里面输入任何东西。也就意味着用户可以直接输入原始的 HTML 和 javascript 代码,并把它提交给服务器:![raw](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-07-17_55a8a2be209f7.png)
如果服务器端对于用户的输入不做任何无害处理的话,这些内容就会注入到网页的内容中去,然后浏览器就会解释执行这些 HTML 和 javascript 代码。在本例中会弹出一个警告框,这当然不是我们想要的结果。恶意用户可以使用 HTML 和 javascript 代码对服务器或者以后访问这个页面的用户发起毁灭性的攻击。举个例子,一个攻击者可以使用 javascript 代码去获取所有在他之后访问这个页面的用户的会话 id ,然后伪装成其他用户。而这一切都发生在受害者一无所知的情况下。而且要注意的是,这种攻击也能绕过同源策略,因为这段恶意代码是存在于当前这个网站上的。
### 跨站脚本攻击的解决方案
* 阻止此类攻击的一个办法就是总是对用户输入的内容做无害处理。消除有问题的输入,比如``标签,或者使用一个更安全的输入格式,比如 Markdown,这样就可以阻止 HTML 和 javascript 同时出现在用户的输入里。
* 第二个办法就是在显示之前转义用户输入的所有数据.如果你需要用户能够输入 HTML 和 javascript 代码,那么当你显示这些输入内容的时候要确保它们被正确转义,这样浏览器就不会把它们当做代码给执行了。
## 小结
本章,我们讨论了 web 应用关于安全的诸多方面。不消说,这是一个宽泛的话题,我们只是蜻蜓点水一般掠过几个常见问题而已。本章的主要目的是展示一下在 HTTP 之上开发出的 web 应用是多么的脆弱,和确保一个 web 应用的安全性是多么的困难。
';
有状态的WEB应用
最后更新于:2022-04-02 01:31:56
## 简介
HTTP 协议是无状态的。换句话说,在你的各次请求之间,服务器是不会保留你的 “状态” 信息。
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/53340912fe4fbc2f4f1b17a2ca5266ae_592x249.png)
每一次请求都被认为是一次全新的请求,不同的请求之间并不知道对方的存在.这种” 无状态性 “使得 HTTP 和互联网都是 “去中心化” 的,不会轻易被人掌控。 但也是因为这种属性,使得 web 开发者在开发有状态的 web 应用时十分的困难。
当我们看看我们熟悉的 web 应用,我们会觉得这些应用大都是有状态.比如,我们登录到 Facebook 或者 Twitter ,会看到我们的用户名显示在网页上方,这表示我们的目前状态是通过了身份验证。如果我们在页面上随便点点(对服务器发起新的请求),我们并不会突然就退出登录了。 服务器返回的响应页面里依然有我们的用户名显示着,这样看来这些应用似乎都会维持它们的状态。
在本章,我们会通过讨论一下这是怎么回事,看看 web 开发者常用的实现 “有状态” 体验的技术手段。同时,也会讨论一些用于高效展示动态页面信息的技术。会讨论以下技术:
* 会话( session )
* Cookies
* 异步 javascript 调用( AJAX )
## 一个有状态的应用
让我们来看一个有状态的应用。当你发起一个请求到`http://www.reddit.com`的时候,主页是这样的:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/eb2e157529c3db528500b2ca58dc24c8_1394x825.png)
然后输入你的用户名和密码进行登录:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/50689a8ccfc5db6f230a9e09c52bfe86_1394x825.png)
登录后,在页面上方你就会看到你的用户名,表示你已经成功通过身份验证。如果你刷新页面,就会向`http://www.reddit.com`服务器发起一个新的请求,你会看到,页面还是那个样子,你的登录状态还在。这是怎么回事呢? HTTP 不是一个无状态协议么?服务器是怎么知道你的用户名,并动态显示在页面上的?哪怕刷新页面发起新的请求也不影响你的登录状态。这种情况非常常见,我们都习以为常了。 这就是你的网络购物车在你往里加新商品的时候如何保留着你之前的选择,有时候哪怕过了几天,你也能看到你购物车里的东西。这就是 Gmail 如何认出你,并在页面上显示针对你名字的欢迎信息,所有的现代 web 应用都是这样工作的。
## 会话 ( session )
显然,人们可以把这个无状态的 HTTP 协议通过某种方式保持状态。在客户端(一般就是指浏览器)的帮助下,HTTP 的行为会让人觉得它会在客户端与服务器之间维护一个有状态的连接,尽管实际并没有。达到这种效果的一个办法就是, 服务器在发送响应数据给客户端的时候带一个唯一的令牌(英文叫 token,就是一串数)。随后不论何时客户端向服务器发起请求的时候都把这个令牌附加在后面,让服务器能够辨识这个客户端。在 web 开发领域我们把这个来回传递的令牌叫做会话标识符( session identifier )。
这种在客户端与服务器之间传递`会话 id`的机制,能让服务器创建一种各次请求之间的持续连接状态。Web 开发人员利用这种人造的状态,来构建复杂的应用程序。即使这样,每一个请求严格上来说还是无状态的,各次请求之间并不知道彼此的存在。
这种人造状态,会有几个后果。第一,必须检查每个请求,查看它是否包含会话标识符。第二,如果请求有会话标识符,也就是有一个会话 id,服务器必须检查每一个会话 id ,确保这些会话 id 是没有过期的,也就是服务器需要维护一些关于如何处理会话过期,如何存储会话数据的规则。第三,服务器要基于这个会话 id 取出这个会话的数据。最后,服务器要根据取出的会话数据重新创建应用程序的状态( 比如,一个请求对应的 HTML ),然后将其作为响应返回给客户端。
这就意味着服务器必须非常辛勤的工作,来模拟这个有状态的用户体验。每一个请求都会有一个独立的响应,哪怕这次的响应跟前一个响应没有任何区别。举个例子,如果你登录到 Facebook 上,服务器会给你一个响应,生成你看到的主页。这个响应是一个十分复杂的 HTML 页面。Facebook 的服务器会把页面上所有照片和留言的赞和评论都组合起来,然后显示在你的时间线上。生成这样一个页面的成本非常高。现在,如果你点了某个照片下面的” 赞 “链接,理论上,Facebook 会重新生成整个页面,它会把你赞过的照片的被赞数加 1,然后把整个 HTML 作为响应返回给你,尽管除了这个赞数以外大部分内容都没有改变。 庆幸的是,实际中 Facebook 使用 Ajax 代替了全页面刷新。不然的话,刷新一个页面会花费很长时间。
服务器使用了很多先进的技术来优化会话和实现安全机制,不过这些话题都超出了本书的范围,暂且放下。现在我们来聊一个常用的存储会话信息的方法: 浏览器 cookie 。
## Cookies
cookie 就是在一个请求/响应周期内,服务器发送给客户端(通常就是浏览器),并存储在客户端的一段数据。Cookies 或者 HTTP cookies,就是存储在浏览器里包含着会话信息的小文件。默认情况下,大部分浏览器的 cookies 都是启用的。当你第一次访问一个网站的时候,服务器会给你发送会话信息并将其存储在你本地电脑浏览器的 cookie 里。要注意的是真正的会话数据是存在服务器上的。在客户端发起每一个请求的时候,服务器就会比对客户端的 cookie 和服务器上的会话数据,用来标识当前的会话。通过这种方法,当你再次访问同一个网站的时候,服务器就会通过 cookie 和里面的信息来认出你的会话。
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/e2c351e0c813cc0d4092dbcb84f66143_728x263.png)
我们来看一个真实的案例。用审查器看看 cookies 是如何被创建的。我们要向`http://www.yahoo.com`发起一个请求。要注意的是,如果你的浏览器里已经有了 Yahoo 的 cookie ,你可能需要换一个网站。
保持审查器打开(页面上右键,点击 “查看元素 “),输入这个网址,然后看看我们的请求头部:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/5d5092c4c2231b3b0a5c755fafc3d522_1394x825.png)
注意,里面没有任何有关 cookies 的东西,接下来我们看看响应头部:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/af5e4c255e7463978dbe0c657e68d323_1394x825.png)
你会看到有个`set-cookie`头部把 cookie 数据加到响应里。 在首次访问这个网站的时候这个 cookie 数据会被设置。最后,我们发起一个相同的请求然后再来看看请求头部:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/68817db18a6e018073d524192d9e116b_1394x825.png)
你会看到有个`cookie`头部出现了(注意这个是请求头部,就是说这是要从你的客户端发送到服务器的)。 里面的内容是上一个响应头部`set-cookie`的值。这一小段数据,会出现在你每一个发起的请求里,用来唯一标识你 --- 或者说的清楚点, 标识你的客户端,也就是你的浏览器。cookie 是存在浏览器里的。现在,就算你关掉浏览器,关掉电脑, cookie 里的信息也不会消失的。
现在让我们回到本章最初的那个例子,关于 Reddit 和其他 web 应用是如何在我们发起的一个又一个请求中记住我们的登录状态。记住,每一个请求都是独立的, 不知道彼此存在的。 那么问题来了,应用程序是如何 “记住” 我们的登录状态呢?如果你要跟着做, 保持审查器打开,然后按照下面的步骤来:
1. 点击 resources 标签然后访问`http://www.reddit.com`
2. 把 cookies 那部分展开, 然后点击`www.reddit.com`,你就能在 value 那一列看到第一次发起请求后服务器返回给我们的 cookie 了:![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/98c49e6ed8688adff8a026ae97db5c51_1394x825.png)
3. 然后登录,你应该能看到在最后一行出现了一个唯一的会话 id 。这个会话 id 会存在你浏览器的 cookie 里,从此后你每一个到 Reddit.com 的请求都会附上这个会话 id 。![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/49c0b07cdd1035d63595490cf246a728_1394x825.png)
现在每一个请求都会包含这个会话 id ,这样服务器就能唯一确认你这个客户端啦。当服务器接收到一个带有会话 id 的请求,它就会根据这个 id 去找对应的数据,在这个对应的数据里就有服务器"记住"的客户端的状态,或者说就是这个会话 id 的状态。
> #### 会话数据存在哪里?
>
> 一句话:服务器上的某个地方。有时候,存在内存里,其他时候,可能会存在某个持久化存储介质上,比如数据库或者键 / 值存储。会话数据存在哪里不是我们现在需要关心的。现在重要的是要理解会话 id 存储在客户端,它是访问存储在服务器上的会话数据的 “钥匙”。web 应用就是这样解决 http 无状态这个问题的。
还有一点非常重要,在一个会话里发出的会话 id 是唯一的,而且有一个很短的过期时间。对上面的例子来说,在会话过期后你需要重新登录。如果我们退出登录, 会话 id 就会消失。
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/28187242670e5dfd23b00c640e4d36d5_1394x825.png)
如果你手动删掉会话 id 也是同样的效果(在审查器里,右键 cookies 然后删除它),这样就退出登录了。
简单回顾一下,会话数据是由服务器生成并存储在服务器上,会话 id 以 cookie 的形式发送到客户端上。我们还看到了 web 应用程序如何充分利用这些来模拟在 web 上的有状态体验。
## AJAX
最后,我们来简单看看 AJAX 和它在 HTTP 请求/响应周期里的作用.AJAX 是”异步 javascript 和 XML “ 的简称( Asynchronous JavaScript and XML )。它的主要特点就是允许浏览器发送请求和处理响应的时候不用刷新整个页面。举个例子,如果你登录到 Facebook 上,服务器会给你一个响应,生成你看到的主页。 这个响应是一个十分复杂的 HTML 页面。Facebook 的服务器会把各种信息组合起来,显示在你的时间线上。在前面的讨论中,我们知道,为每一个请求都重新生成一次页面的成本是非常高的(记住,你的每一个动作,点个链接,提交个表单,都会发起一个新的请求)。
当使用 AJAX 的时候,所有客户端发送的请求都是异步的,就是说页面不会刷新。举个例子,当我们在 google 上搜索的时候:
* 访问 Google 主页`http://www.google.com`,然后打开审查器,看 Network 标签,里面内容是空的。![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/b3372a5cd28c60924a362c7a9e6597b1_1392x828.png)
* 当你开始搜索的时候,你会在 Network 标签看到请求如潮水般发起。
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/433d1595e8792726a9d6978a27f66af2_1392x828.png)
很明显发起了很多请求,但是你应该能注意到,页面没有整个刷新。 然而这个 Network 标签的内容让我们看清: 每敲一个字都会发起一个新的请求,也意味着你每按一下键都会触发一个 AJAX 请求。这些请求的响应会通过一些回调来处理。你可以这样理解`回调`,就是你把一些逻辑存放在某个函数里,当某个条件被触发之后再回来执行你前面存放的逻辑。在本例中,当响应返回的时候,回调就会被触发。你可能已经猜到了,回调函数会用新的搜索结果去更新网页上的 HTML 。
我们不去深究回调到底是什么样的或者如何发起一个 AJAX 请求。最主要的一点要记住的是,AJAX 请求就像是普通请求:发送到服务器的请求依然跟普通请求一样有着一个 HTTP 请求该有的所有组成部分,并且服务器处理 AJAX 请求的方法跟处理普通请求也是一样的。唯一不同就是,不是通过浏览器刷新来处理响应,而通常由客户端的一些 javascript 代码来处理。
## 小结
本章我们介绍了一些 web 开发者用来在无状态的 HTTP 协议上构建有状态应用的技术。你学习了 cookie 和会话,以及现代 web 应用如何记住客户端的状态。也使用审查器了解了 cookies 和会话 id 。最后,了解了 AJAX 在 web 应用里展示动态内容时所扮演的角色。
';
处理响应
最后更新于:2022-04-02 01:31:54
## 简介
目前为止我们已经发起了各种各样的请求,也看到了服务器发回的原始 HTTP 数据。服务器返回的原始数据就是所谓的**响应**。本章我们会分析 HTTP 响应里的各种组成部分。
## 状态码
我们要看的第一个组成部分是 HTTP 状态码.`状态码`是服务器接收到请求后返回的标识请求状态的三位数.在`状态码`的旁边,就是描述这个状态码的`状态文本`.一般这些出现在审查器的`Status`这列:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/0319dff5e5064f2b8d2d57ad1eb85079_1394x825.png)
你遇到的最常见的响应状态码应该是 200,意思是请求被正确处理.让我们来看看其他有用的状态码:
| 状态码 | 状态文本 | 含义 |
| --- | --- | --- |
| 200 | OK | 请求被正确处理 |
| 302 | Found | 所请求的资源已暂时更改.通常会重定向到另一个 URL |
| 404 | Not Found | 所请求的资源无法找到 |
| 500 | Internal Server Error | 服务器出现一般性错误 |
作为一个 web 开发者,你应该熟知上面的响应状态码和其代表的含义。
让我们来挨个举例看看:
## 302 Redirect(重定向)
当一个资源的位置移动了会发生什么呢? 最通用的解决方案是把对旧 URL 的请求重新引导到新 URL 上.这种重新引导请求的行为有一个术语叫`重定向`( redirect )。当你的浏览器看到一个 302 响应状态码的时候,他就知道这个资源已经移动到别处了,然后就会自动跳转到 `Location` 响应头部里指定的 URL 。在本节中,我们会用浏览器和 HTTP 工具来演示重定向。
比如说你想要看 [GitHub](http://www.github.com/) 上的账户配置,你就要访问这个链接 `https://github.com/settings/profile` 。但是,要有访问账户配置页面的权限,你必须先登录。如果你没有登录就访问这个链接,浏览器会把你送到登录页面去。当你填写正确的登录信息后,你就会被重定向到你最早想访问的页面。这个是大多数 web 应用的通用工作流程。让我们看看浏览器和 HTTP 工具是怎么处理这个流程的。
首先你在浏览器里输入 `https://github.com/settings/profile` 。
因为浏览器很聪明,它会直接按照重定向的指示给你展示出 GitHub 的登录页面:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/ccbd64286387c327a9cb5b2cdf111822_1394x825.png)
相比之下我们来看看 HTTP 工具 (注意状态码),它并没有自动跟进重定向:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/13df7ac7ac335c388503930e0816206a_1394x825.png)
注意这个 `Location` 响应头部(有点难找到,在第 12 行)。你应该能看到
~~~
Location: https://github.com/login?return_to=https%3A%2F%2Fgithub.com%2Fsettings%2Fprofile
~~~
这个 URL 里有一个 _return_to_ 参数,它的值就是在登录之后客户端要重定向到的 URL。对比一下浏览器里那张图,你应该能发现,这个 URL 和浏览器地址栏里的 URL 是一样的。
## 404 Not Found(未找到)
接下来,我们看看浏览器里的 404 响应状态码是什么样。当所请求的资源没有找到的时候,服务器会返回这个状态码。要记得,资源可以是任何东西,包括音频文件,CSS 文件,javascript 文件,图片等等。让我们给`http://www.dropbox.com` 发一个 `GET` 请求索要一张图片,`https://www.dropbox.com/awesome_file.jpg`:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/81909bfffcb3fe0e086b001893adc4d4_1394x825.png)
我们看到了 Dropbox 那漂亮的 404 页面。现在让我们在 HTTP 工具里看看同样的请求的响应:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/a6af96144ca5d77b90893becd65c7068_1394x825.png)
因为我们索要的资源并不存在,浏览器给我们展示了一个格式友好的文字提示,但 HTTP 工具会给我们展示响应状态码和原始响应数据。
## 500 Internal Server Error(内部服务器错误)
一个 500 状态码意思就是'服务器那儿好像出了点儿问题'。 这只是一个一般性的错误状态码,造成这个错误的原因可能是服务器配置,也可能是跑在服务器上的应用里的代码少写个逗号。但是不管是什么问题,总之是服务器端有问题。有服务器访问权限的人必须去调试和解决这个问题,这就是为什么很多含糊的报错信息都会提醒你去联系你的系统管理员。在实际应用上,500 错误也可以像 404 那样以多种多样的形式显示出来。下面就是 rails 应用默认的 500 页面:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/8ff210b607d3509234ada8723b0a4516_1394x825.png)
使用 HTTP 工具,我们能看到状态码和原始数据:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/48312e9ee358d0561839cf48d10a18ae_1394x825.png)
## 响应头部
跟请求头部一样,我们也可以用审查器去看响应头部:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/8d86707880709d990cb3aa3f91ea6e9f_1392x831.png)
响应头部提供了更多关于服务器返回的资源的信息。让我们来看看一些常见的响应头部:
| 头部名称 | 描述 | 举例 |
| --- | --- | --- |
| Content-Encoding | 数据的编码类型 | Content-Encoding: gzip |
| Server | 服务器的名称 | Server:thin 1.5.0 codename Knife |
| Location | 通知客户端新的资源位置 | Location: [http://www.github.com/login](http://www.github.com/login) |
| Content-Type | 响应数据的类型 | Content-Type:text/html; charset=UTF-8 |
还有很多响应头部,但是跟请求头部一样,没有必要去背下来。 响应头部对返回的数据有着微妙的影响,有些情况下,它们巧妙的用于工作流程中(比如,你的浏览器自动跟进 `Location` 响应头部指向的 URL )。你需要理解的仅仅是,响应头部包含了一些关于返回的响应数据的额外信息(译注:描述数据的信息/数据,通常被称为为元数据,meta-data or meta information )。
## 小结
在本章,我们讨论了 HTTP 响应的组成部分。我们也了解了如何使用审查器去查看 HTTP 响应的头部。尽管我们只是揭开了 HTTP 协议面纱的一角,我希望这些知识在你需要的时候能给你 [深入研究 HTTP](http://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol) 的能力。
总之,我们已经看到,HTTP 只不过是一个协议,用于指示客户端与服务器之间如何使用某种格式的文本进行通信。
HTTP 响应中最重要的部分如下:
* 状态码
* 头部
* 消息正文,里面有原始响应数据
试试看,在下面这个图里你能不能找到上面那几个部分都在哪儿:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/3bf3e54d708bbd453cb540cd38277603_1394x825.png)
';
发起请求
最后更新于:2022-04-02 01:31:51
## 浏览器的 HTTP 请求
发起一个 HTTP 请求很容易。比如你说你想用浏览器访问 Reddit 。你所需要做的仅仅是启动浏览器然后在地址栏输入 [http://www.reddit.com](http://www.reddit.com/),然后你就可以看到:
![reddit](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-07-17_55a89e11d484a.png)
承载 Reddit 网站的服务器处理你的请求并返回给你的浏览器一个响应。你的浏览器足够聪明去处理这个响应,然后将所有的颜色,图片,文字,样式给你展示成上图那样。
## 用 HTTP 工具发起 HTTP 请求
因为浏览器给我们展示的是处理过的服务器响应,我们看不到服务器发给我们的响应的本来面目。怎么样才能看到原始的 HTTP 响应数据呢?
为此,我们可以使用一个 HTTP 工具,就像用浏览器的时候我们输入网址一样,我们可以用 HTTP 工具发送一个请求到 [http://www.reddit.com](http://www.reddit.com/)。我们的 HTTP 工具,Paw,不会处理响应数据,这样能让我们看到原始的响应数据,大概长这样:
![paw_response](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-07-17_55a89e3a5657c.png)
跟你在浏览器里看到的简直就是天壤之别啊!如果你以前从来没见过原始的 HTTP 响应数据,这可能让你有点吃惊。你现在看到的这些,其实就是你浏览器接收到的数据,只不过是浏览器把这一堆数据处理成了人类友好的格式。
如果你学习 HTTP 的知识是为了成为一个 web 开发者,你需要学着阅读原始的 HTTP 响应数据。当然你不用在你的脑海里把这些数据解析成一个高清晰度的画面,但是你应该对这些响应代表着什么有一个整体上的概念。等你有了足够的经验,你就可以深入原始的 HTTP 响应数据去做一些 debug 工作和一探其究竟。
## 使用审查器 (Inspector)
所有现代浏览器都有查看 HTTP 请求和响应的方法,通常都叫做**审查器**。我们使用 [Chrome 审查器](https://developer.chrome.com/devtools),演示一下如何用它来分析你浏览器的 HTTP 通信。
1. 打开 Chrome 浏览器,点击右上角的菜单,选择工具,然后选择开发者工具,这样就打开审查器了。
2. 在地址栏输入 [http://www.reddit.com](http://www.reddit.com/),来给 Reddit 重新发送一个新的请求。
3. 以上两个步骤要确保审查器开着啊,然后点击 Network 标签:![inspector_network](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-07-17_55a89e5dde679.png)
4. 你应该注意的第一件事是,这里会有很多项。每一项都是一个单独的请求,也就是说,仅仅是访问了这一个 URL,你的浏览器就发起了多个请求,一个请求对应着一个资源 (图片,文件等等)。点击一下对主页的第一个请求,就是`www.reddit.com`那项:![inspector_first](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-07-17_55a89e98e5fdb.png)
5. 这里,你就能看到特定的请求头部,cookies,还有原始的响应数据:![inspector_data](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-07-17_55a89ec162bc5.png)在默认的子标签 _Headers_ 里显示了发给服务器的请求头部和接收到的服务器返回来的响应头部。接下来点击 _Response_ 子标签去看看原始响应数据。![inspector_raw](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-07-17_55a89f00a58b9.png)这个响应数据看起来应该跟我们前面使用 HTTP 工具得到的差不多。
另一个要注意的事情是,当我们使用审查器的时候,在 _Network_ 标签下,除了第一个请求,还有一堆其他请求的返回:![inspector_others](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-07-17_55a89f1d94305.png)
为什么会出现这些额外的响应,谁发起的这些请求? 事情是这样的,当我们请求资源的时候,最初的对于`www.reddit.com`的请求,返回了一些 HTML。 这些 HTML 里又引用了其他的资源比如图片,CSS 文件,javascript 文件等等。你的浏览器,很聪明也很给力。它明白,为了展示出一个能够给人看的网页,它必须去把这些引用到的资源也一并拿来. 因此浏览器就会对得到的初次响应里的每一个资源再一一发起请求。当你在_Network_ 标签里往下滚动页面,你就能看到所有的相关资源。这些其他的请求保证了这个网页和其他一些东西能正常良好的显示在你的屏幕上.整体来看,浏览器的审查器对于相关的引用资源给你了一个良好的体验.另一方面,一个纯粹的 HTTP 工具,只会返回**一个**巨大的响应数据,并不会自动拉取引用的资源.一个用`curl`发起的请求可以这样写:
~~~
$ curl -X GET "http://www.reddit.com/" -m 30 -v
~~~
然后你看到的应该只是一个请求,一个响应包含着 HTML,但是没有那些你在浏览器里看到的自动发起的额外请求。
## 请求方法
让我们重温一下上面第 3 步里的图,看看 _Network_ 标签里的响应数据。你会发现有这么两列 **Method** 和 **Status**。![inspector_method](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-07-17_55a89f37cb508.png)
本节我们来看看这几列里的信息是什么意思。
在 **Method** 这一列里的数据呢,就是常说的 HTTP 请求方法 ( Request Method )。你可以这样想,把这个 Method 当做是一个动词,用来告诉服务器你想对某个资源做什么。最常见的两个 HTTP 请求方法是 `GET` 和`POST` 。当你想要获取信息的时候,使用`GET`这个最常用的 HTTP 请求方法。在上面那张图你应该能看到,所有使用 `GET` 的请求,都是为了去拿到那些需要显示在网页上的资源。
**Status** 这列显示了每一个请求的响应状态码。在本书的后面章节我们会详细讨论响应。现在你需要理解的是每一个请求都会得到一个响应,哪怕这响应是一个错误响应 -- 那它也是个响应嘛。(这句话其实不是百分百正确,因为有些响应会**超时**,但是现阶段我们不用在意这些细节。)
## GET 请求
`GET` 请求一般出现在超链接或者浏览器的地址栏里。当你在你的浏览器地址栏里输入类似`http://www.reddit.com` 这样的地址的时候,你就是在发起一个 GET 请求。你让浏览器去取这个地址上的资源,这就意味着我们在整本书里一直在使用`GET`请求。在你点击 web 应用上的超链接的时候也会发生同样的事情。超链接的默认行为就是向一个 URL 发送`GET`请求。让我们用 HTTP 工具向 `http://www.reddit.com` 发起一个简单的 `GET` 请求。要确保你选择了 `GET` 这一项,然后输入网址:![paw_get](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-07-17_55a89f61a6740.png)
在右边窗口你就能看到服务器发回来的原始 HTTP 响应数据和一些其他信息。
使用 `curl` 的读者呢,可以在终端里敲入以下命令:
~~~
curl -X GET "http://www.reddit.com/" -m 30 -v
~~~
我们也可以用 HTTP 工具发送带查询字符串的请求.举个例子,我们发起一个带查询字符串的请求去搜索`https://itunes.apple.com/`里所有跟`Michael Jackson`有关的东西.请求使用的 URL 长这样:
~~~
https://itunes.apple.com/search?term=Michael%20Jackson
~~~
还是那句话,用 Paw 的,发起请求前确保选的是`GET`。
![paw_query](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-07-17_55a89f88481a6.png)
这里我们仅仅是给`https://itunes.apple.com/`的服务器发送了一个带着参数 `term=Michael%20Jackson` 的 HTTP `GET` 请求,其中`%20`是空格的 URL 编码字符。
这个例子所用的 `curl` 命令是这样的:
~~~
$ curl -X GET "https://itunes.apple.com/search?term=Michael%20Jackson" -m 30 -v
~~~
以上就是你现阶段需要知道的所有关于发起 HTTP `GET` 请求的所有知识。主要的概念有以下几点:
* `GET` 请求经常用于取得一个资源,而且大部分超链接都是 `GET` 请求。
* 一个 `GET` 请求的响应可以是任何东西,但是如果响应是一个 HTML 并且里面引用了其他资源,你的浏览器会自动对这些资源发起请求,而一个纯粹的 HTTP 工具则不会。
* 使用 `GET` 请求的时候在大小和安全性上有一些限制。
## POST 请求
我们已经看到了怎么使用 `GET` 去从一个服务器取得和请求数据,但是如果我们需要给服务器提交一些数据怎办呢? 这就要提到另一个非常必要和重要的 HTTP 请求法方法`POST`啦。当你想在服务器上采取一些行动,或者给服务器发送数据的时候,就要用到 `POST` 了。让我们用 HTTP 工具来举个例子:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/755d0179546dd66100842f80a1c24d44_1394x825.png)
按照惯例给出 curl 命令:
~~~
$ curl -X POST "http://echo.httpkit.com" -m 30 -v
~~~
上面的图片显示了对 `http://echo.httpkit.com` 发起的 `POST`请 求和服务器返回的响应。浏览器里的典型`POST` 使用案例就是你提交一个表单的时候。`POST` 请求允许我们向服务器发送更大或者敏感的数据,比如图片或者视频。举个例子,比如我们要把我们的用户名和密码发送到服务器上去做验证。我们完全可以使用 `GET` 通过附加查询字符串把数据发给服务器。这样做的毛病很明显: 我们的验证信息在 URL 上是可见的。这必然不是我们想要的。在表单提交上使用 `POST` 请求能够解决这个问题。而且 `POST` 请求也能避免你使用 `GET` 请求时的查询字符串长度限制问题。通过 `POST` 请求,我们可以给服务器发送更大的数据。
我们再来看一个使用 `POST` 请求提交 web 表单的例子。在浏览器里我们的示例表单长这样:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/3659e809c745d755ea217fd6bdf91ec1_1394x825.png)
提交表单后,你会被重定向到这样一个页面:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/9ef0dc62d0227d39fa5f679782196f14_1394x825.png)
现在让我们切换到我们的 HTTP 工具,来模拟一下我们刚才在浏览器里做的事情。我们要发起一个 `POST` 请求到 `http://al-blackjack.herokuapp.com/new_player` 来代替在浏览器里填写表单。下面是我们填写第一个表单(就是那个我们写名字的那个)的方法:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/3b93f159ec9c9bd8335cc6032946a0e0_1394x825.png)
或者用 curl:
~~~
$ curl -X POST "http://al-blackjack.herokuapp.com/new_player" -d "player_name=Albert" -m 30 -v
~~~
注意在图片和 curl 命令里我们都提供了额外的参数: `player_name=albert` 。 这个跟我们在第一个表单的 “What's your name?” 输入框里填写内容并提交是一样的。
我们可以使用审查器查看内容 (右键选择`审查元素`)。你能看到,我们在 `POST` 请求里发送的参数`player_name` 就是这个表单里面 input 元素的 name 属性里的内容:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/dd12d61f88fa61fc5887c20185ca832f_1394x825.png)
这个的奥秘在于,我们是怎样不通过 URL,仅仅是提交表单就把数据发送到服务器的呢?答案是 HTTP 的正文 ( Body )。正文包含了正在传输的 HTTP 消息,这个内容是可选的。换句话说就是,可以发送一个没有正文的 HTTP 消息。当你要使用正文的时候,可以包含 HTML,图片,音频等等。你可以把正文看成包裹在信封里发出的信件(译注:URL 就好比是信封上写的地址,是可见的,而信的内容在信封里是不可见的)。
HTTP 工具和 curl 发起的 `POST` 请求跟你在浏览器里填写表单然后提交是一样的,紧接着我们会被重定向到下一个页面。仔细看看 HTTP 工具的图片,看看里面的原始响应数据,把我们重定向到下一个页面的关键信息在这一行 `Location: http://al-blackjack.herokuapp.com/bet` 。 `Location` 和它对应的数据,就是所谓的 HTTP 响应头部里的一部分 (是的,你可能也想到了,请求也有头部,但是在现在这个例子里,这是一个响应头部)。不要太在意这些细节,在后面章节我们会讨论头部。当你的浏览器看到这个响应头部,然后就会自动向`Location` 头部里的 URL 发起一个全新的,完全独立的请求。你看到的那个 "Make a bet" 表单,就是这第二个请求的响应内容。
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/37cb2a75b4e431e8822257159042ca69_1394x825.png)
如果你对上面几段内容感到困惑,那就再多看两遍。使用浏览器的时候有一点很重要,那就是浏览器对你隐藏了大量的 HTTP 请求/响应的细节。你的浏览器发起最初的一个 `POST` 请求,得到了一个包含 `Location` 头部的响应,然后在不用你参与的情况下发起另一个请求,然后把第二个请求得到的响应内容展示给你。再重申一次,如果你使用的是一个纯粹的 HTTP 工具,你能看到第一个 `POST` 请求的 `Location` 响应头部,但是这个工具不会自动发起第二个请求。(有些 HTTP 工具有这个功能,你可以看看 "automatically follow redirects" 选项)
## HTTP 头部
HTTP 头部允许客户端和服务器在请求/响应的 HTTP 周期里发送额外的信息。头部,通常是以冒号分隔的键值对儿,一般是纯文本格式的。我们可以使用**审查器**来看看这些头部。下面这张图你能看到请求和响应都有头部:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/f4855ac86b869dee11493b07c64dc370_1394x825.png)
上图显示了在一个请求/响应周期里传递的各种头部。更进一步,在 `Request Headers` 下面,我们能看到请求和响应包含着不同的头部:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/5918e4ba00478cd3498592327edd6c9e_1392x831.png)
## 请求头部
请求头部提供更多关于服务器和要获取的资源的信息。一些有用的请求头部是:
| 字段名 | 描述 | 举例 |
| --- | --- | --- |
| Host | 服务器域名 | Host:[www.reddit.com](http://www.reddit.com/) |
| Accept-Language | 可接受的语言 | Accept-Language: en-US,en;q=0.8 |
| User-Agent | 一个标识客户端的字符串 | User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_5) AppleWebKit/537.36 (KHTML,like Gecko) Chrome/38.0.2125.101 Safari/537.36 |
| Connection | 客户端连接的类型 | Connection: keep-alive |
没有必要去记忆任何一个请求头部,但是一定要知道这是发送到服务器请求里的一部分。在下一章我们会讨论响应头部。
## 小结
本章我们做了一个简单的 HTTP 请求介绍。读完本章你应该能熟练应用:
* 使用审查器查看 HTTP 请求
* 使用 HTTP 工具发起 GET/POST 请求
关于 HTTP 请求最终的部分是理解以下内容:
* URL
* 参数
* 头部
* 消息正文 (在 `POST` 请求里的)
下一章我们将通过学习 HTTP 响应来继续了解 HTTP 。
';
HTTP
最后更新于:2022-04-02 01:31:49
准备工作
最后更新于:2022-04-02 01:31:47
对于 HTTP 的基础做了些概览之后,让我们来熟悉一下会在本书中用到,用于展示 HTTP 如何工作的工具。其实,阅读本书的时候,使用什么工具都无所谓,所以挑一个你用着顺手的就好。
## 浏览器插件
Google 的 Chrome 浏览器有几个插件你可以用,比如 [Postman](https://chrome.google.com/webstore/search/Postman?hl=en-US) 和 [REST HTTP API Client](https://chrome.google.com/webstore/detail/dhc-resthttp-api-client/aejoelaoggembcahagimdiliamlcdmfm)。
## HTTP GUI 工具
在本书中我们会经常使用图形化的 HTTP 工具。说到 HTTP 工具这个话题那能说的就太多了,所以长话短说,我们使用 [Paw2](http://luckymarmot.com/paw).虽然这是一个 Mac App Store 里的付费应用,不过它也有免费试用期,够你看这本书的时候用了。
尽管如此,还有很多其他备选方案。简单介绍几个很棒的吧: [HTTP Client](http://ditchnet.org/httpclient/),[Fiddler](http://www.telerik.com/fiddler) 和 [Cocoa Rest Client](http://ditchnet.org/httpclient/)。
## HTTP 命令行工具
[curl](http://curl.haxx.se/) 是一个免费的命令行工具,经常被用于发送 HTTP 请求。
### Mac OS X/Linux:
OS X 和大部分的 Linux 发行版应该都预装了 curl,你可以在命令行里非常方便的使用它,类似这样:
~~~
$ curl www.usa.gov
~~~
### Windows:
> 警告
>
> 以下操作需要对 Windows 些底层工具比较熟悉。 如果你在使用 Windows 并且对以下安装操作感到不适,强烈推荐你使用 GUI 工具,不论是浏览器插件还是安装到你系统上的软件都行。 下面的操作不是必须的,跳过这些你也可以良好的阅读本书。
Windows 用户可以参照以下步骤安装`curl`:
1. 安装 [Visual C++ 2008 Redistributable Package](http://www.microsoft.com/en-us/download/details.aspx?id=15336)。
2. 安装 [Visual C++ 2010 Redistributable Package](http://www.microsoft.com/en-us/download/details.aspx?id=14632)。
3. 接下来安装 [OpenSSL](http://www.shininglightpro.com/products/Win32OpenSSL.html):
* 32 位系统: Win32 OpenSSL v1.0.1j Light
* 64 位系统: Win64 OpenSSL v1.0.1j Light
4. 现在去 curl 的 [主页](http://curl.haxx.se/) 并选择 [下载](http://curl.haxx.se/download.html)。
5. 在下载页面,找到 Windows 相关的地方,然后点击基于你自己系统的 binary SSL-enabled 版本进行下载:![windows_curl_download](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-07-17_55a89d2871310.png)下载这个文件,然后把`curl.exe`放到一个新的文件夹里。
6. 从 [这里](http://curl.haxx.se/docs/caextract.html) 安装一个 CA 证书.确保右键另存`cacert.pem`这个链接,并且把文件存成`.pem`格式的:![CA](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-07-17_55a89d5c15c8a.png)把这个证书拷贝到你刚才放`curl.exe`的文件夹里
7. 最后打开你的控制台,进入到你放`curl.exe`的文件夹然后跑下面这个命令: `curl -I GET "http://www.reddit.com/" -m 30 -v`
![command](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-07-17_55a89db4a6c1b.png)
可以的话,书中会展示用`curl`发起请求的命令,以帮助使用`curl`的读者。
';
什么是URL
最后更新于:2022-04-02 01:31:45
## 简介
当你想去某人家里的时候,你要知道他家的地址。如果你想给你朋友打电话,你要知道你朋友的电话号码。没有这些信息,你想去别人家或者给人打电话都是不可能的。换句话说,如果你有别人的住址或者电话号码,你能立刻知道谁是谁,因为地址和电话号码都有其唯一性。
在互联网上寻找和访问服务器也是同样的一个概念。当你想上 Facebook 的游戏站的时候,你会打开浏览器然后输入 [http://www.facebook.com/games](http://www.facebook.com/games)。web 浏览器向这个地址发送一个 HTTP 请求,然后拿回这个地址的响应结果。你刚刚输入的那个地址,[http://www.facebook.com/games](http://www.facebook.com/games) 就是所谓的统一资源定位符 (Uniform Resource Locator) 或 URL。URL 就像是你跟你朋友沟通所需要的电话号码或者地址一样的存在。URL 应该是统一资源标识符 (Uniform Resource Identifier) 这个笼统的概念的一种最常见的形式。本章我们讨论什么是 URL 及其组成部分,和对你这个 web 开发者来说 URL 意味着什么。
## URL 组成部分
当你看到一个 URL,比如`http://www.example.com/home`,其实是由几个组件构成的。我们可以把这个 URL 分解成 3 部分:
* `http`:通常被称为 URL 模式( scheme )。总是出现在冒号和两个斜杠之前,作用是告诉 web 客户端怎样去访问一个资源。在本例中,它告诉 web 客户端使用超文本传输协议也就是 HTTP 去发起一个请求。常见的 URL 模式还有`ftp`,`mailto`和`git`。
* `www.example.com`:URL 的第二个部分,就是资源路径或主机 (host)。它告诉客户端,资源的确切位置。
* `/home/`:URL 的第三个部分就是 URL 路径。它代表了客户端正在请求什么样的本地资源 (对于服务器来说)。
![http_components](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-07-17_55a89cef3dcb6.png)
有时候,这个路径指向了一个主机上特定的资源。比如,`www.example.com/home/index.html`指向了 example.com 服务器上的一个 HTML 文件。
另外,URL 可以包含一个主机用来监听 HTTP 请求的端口号。一个`http://localhost:3000/profile`这样的 URL,通过 3000 端口去监听 HTTP 请求。web 客户端用来监听 HTTP 请求的默认端口号是 80,如果一个 URL 中没有指定其他的端口号,那就等价于写了80 **除非指定了其他的端口号代替,不然端口号80会被默认用于正常的 HTTP 请求**。
## 查询字符串 / 参数
一个查询字符串或者参数是 URL 的一部分并且通常都包含一些要发往至服务器的各种类型的数据。一个简单的带查询字符串的 URL 长这样:
~~~
http://www.example.com?search=ruby&results=10
~~~
让我们拆开来看看:
| 查询字符串组件 | 描述 |
| --- | --- |
| ? | 这是个保留字,标识着查询字符串的开始 |
| search=ruby | 这是一个参数的键/值对儿 |
| & | 这是个保留字,需要给查询字符串添加参数时使用 |
| results=10 | 这也是一个参数的键/值对儿 |
现在我们再来看一个例子。假设我们有下面这个 URL:
~~~
http://www.phoneshop.com?product=iphone&size=32gb&color=white
~~~
![sample_url](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-07-17_55a89cf49d81a.png)
在上面这个例子里,键/值对儿`product=iphone`,`size=32gb`,`color=white` 通过 URL 传给了服务器。这个请求告诉`www.phoneshop.com`的服务器,把要请求的资源条件限制在`产品 iphone`,`大小 32gb`和`颜色白色`。服务器怎么样使用这些参数取决于服务端的应用的处理逻辑。
另一个经常见到查询字符串的情况是当你在搜索引擎上搜索东西的时候。**因为查询字符串是通过 URL 传递的,他们仅使用 HTTP 的 GET 请求**。在本书后面的章节里我们会讨论不同的 HTTP 请求,但是现在你所需要知道的是,当你不论什么时候在浏览器的地址栏里输入网址进行浏览的时候,你就是在发起 HTTP 的 GET 请求。大部分超链接都是 HTTP 的 GET 请求,偶尔会有一些例外。
![get_request](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-07-17_55a89cf76558f.jpg)
使用查询字符串向服务器传递附加信息是个很棒的方法,但是对于查询字符串的使用,以下是一些限制:
* 查询字符串有最大长度。所以,如果你大量的数据需要传输,还是不要用查询字符串的好。
* 查询字符串中使用的键/值对儿是显示在 URL 上的。所以,不推荐用查询字符串传输敏感信息比如用户名或密码。
* 查询字符串中无法使用空格和特殊字符比如`&`。它们必须用 URL 编码代替,我们接下来会讨论这个。
## URL 编码 (URL Encoding)
URL 在设计的时候就默认只接受 ASCII 码。因此,不安全的或者不是 ASCII 码的字符就要进行转义或者编码来适应这个格式。URL 编码的原理是将不符合格式的字符替换成`%`开头后面跟着两个十六进制数字代表的 ASCII 码的一串字符。
下面是一些常见的 URL 编码和实例 URL:
| 字符 | ASCII 码 | URL |
| --- | --- | --- |
| space | 020 | [http://www.thedesignshop.com/shops/tommy%020hilfiger.html](http://www.thedesignshop.com/shops/tommy%020hilfiger.html) |
| ! | 041 | [http://www.thedesignshop.com/moredesigns%041.html](http://www.thedesignshop.com/moredesigns%041.html) |
| + | 053 | [http://www.thedesignshop.com/shops/spencer%053.html](http://www.thedesignshop.com/shops/spencer%053.html) |
符合下列条件的字符都要进行编码处理:
1. 没有对应的 ASCII 码。
2. 字符的使用是不安全的。比如`%`就是不安全的,因为它经常用于对其它字符进行转义。
3. 字符是有特殊用途的 URL 模式保留字。有些字符用于保留字是有着特殊的意义;它们在 URL 中的存在具有特殊用途。比如`/`,`?`,`:`,和`&`,都是需要进行编码的保留字。
比如`&`被保留用于查询字符串的分隔符。`:`也被保留用于分隔主机/端口号和用户名/密码。
那么什么样的字符能在 URL 里安全地使用呢?只有字母表里的和`$-_.+!'(),"`这些字符可以。如果保留字符被用于它应有的特殊用途,那么是可以不用编码的,不然也是必须要编码的。
## 小结
本章,我们讨论了什么是 URL。也认识了 URL 的组件并以对 URL 编码的探索结尾。在准备工作章节之后,我们会稍微深入一点,一起去看看请求和响应,还有它们的构成。
';
背景
最后更新于:2022-04-02 01:31:42
## 概述和历史回顾
当你在浏览器地址栏输入 URL 的时候,你希望看到在你的浏览器里显示这个网站。当你点了一个链接,或者提交了一个表单,你的浏览器可能会显示下一个页面,或者在你的表单中显示一些错误提示,以便你能纠正它们并再次提交。你的浏览器就是一个接口,或者说一扇窗,通过她你就可以与万维网进行互动与交互。
在你的浏览器的背后,有一堆文件 -- CSS,HTML,Javascript,视频文件,图片文件等等 -- 这些文件构成了你所看到的页面。所有这些东西都是通过一个叫做 HTTP 的传输协议从**服务器**传输到你的浏览器,也就是**客户端**里(所以你现在知道为什么在浏览器地址栏输地址的时候前面加的是 `http://` 了吧)。
HTTP,或者超文本传输协议,是上世纪80年代由蒂姆.伯纳斯.李发明的。它是一个规则系统,是一种协议,把应用程序和超文本文档之间的传输联系起来。换句话说,HTTP 就是机器之间彼此沟通的一个协议,或者说一个消息格式。HTTP 遵循一个简单的模型:从客户端发出请求到服务器并等待响应。因此它也被认为是一种“请求--响应协议”。请求和响应都是文本信息,或者说是字符串,信息写法遵循着一个规则,能保证其他机器能够理解上面的内容。
HTTP 协议从创立之初已经经过了一些变化。最开始的时候这个协议仅仅返回 HTML 页面。在 1991年,第一个文档 HTTP/0.9 发布。1992年,HTTP/1.0 发布,并支持传输不同的文件类型,比如 CSS 文件,视频,脚本和图片。1995年,HTTP/1.1 介绍了连接复用和其他的一些特性。1999年又对 HTTP/1.1 做了一些改进,我们现在看到的就是这一版本。在撰写本文时,HTTP/2 正处于开发初期。
## 互联网是如何工作的
互联网是由数以百万计的互相连接的网络构成,网络中计算机和其他设备可以互相通信。按照惯例,网络内的所有设备都提供独特的标签。这种标签的总称是互联网协议地址或 IP 地址,是类似于在互联网上的计算机的电话号码。此外,IP 地址还有端口号,这会提供更多关于如何与其交流的细节 (类似于分机号码)。IP 地址一般都长这样:
~~~
192.168.0.1
~~~
当需要一个端口号的时候,这样:
~~~
192.168.0.2:1234
~~~
IP 地址是`192.168.0.2`,端口号是`1234`。 IP 地址是设备或服务器的标识符,可以包含数以千计的端口,每个端口都用于不同的交互目标。
互联网是比网络更大的一个概念,互联网上每个设备都有 [互联网服务供应商 (ISP)](http://en.wikipedia.org/wiki/Internet_service_provider) 提供的公网 IP 地址,通过这些地址,它们可以进行通信。当我们想访问 Google 的主页的时候,我们并没有在浏览器里输入它的 IP 地址,我们只是输入 Google 的 URL [http://www.google.com](http://www.google.com/),但是像 [http://www.google.com](http://www.google.com/) 这样一个地址,你的电脑是怎么知道它的 IP 地址的呢?
## DNS
URL 和 IP 地址之间的对应由域名解析系统也就是常说的 DNS 来控制。DNS 是一个分布式数据库,把像[http://www.google.com](http://www.google.com/) 这样的域名翻译成 IP 地址,并将请求映射到远程服务器。换句话说,DNS 在互联网上记录 URL 和它对应的 IP 地址。所以像 [http://www.google.com](http://www.google.com/) 这样的域名会被解析成一个 IP 地址`197.251.230.45`。顺便提一下,通过在浏览器地址栏里敲 IP 地址也能访问网站。
不过,比起记一串数字来说,大多数人还是喜欢使用一个用户友好的地址如 [http://www.google.com](http://www.google.com/) 这样的。DNS 数据库被安装在叫做 DNS 服务器的设备上。重要的一点是,DNS 服务器集群是分层级的,没有任何一个单一的 DNS 服务器中包含所有数据。如果一个 DNS 服务器里没有一个请求需要的域名,这个 DNS 服务器就会把请求转发给这个集群上更上一层节点的 DNS 服务器。最终,这个域名会在某个 DNS 服务器上的数据库里被发现,然后它对应的 IP 地址所代表的设备就会来接受这个请求。
你通过浏览器与互联网交互的一个典型实例是这样的:
1. 在浏览器里输入类似 [http://www.google.com](http://www.google.com/) 这样的地址。
2. 你的请求被发送到你设备的网络接口。
3. 这个请求的互联网之旅从搜索 [http://www.google.com](http://www.google.com/) 的 IP 地址开始。在屏幕后面,[http://www.google.com](http://www.google.com/) 代表了一个与某个远程服务器关联的 IP 地址的人类友好的名称。
4. 远程服务器接受请求并将响应通过互联网发送到你的网络接口,并把它交给你的浏览器。
5. 最终,浏览器把这个响应作为一个网页的形式呈现在你面前。
有一点你要明白,当你的浏览器发出请求的时候,它只是发送了一些文本到一个 IP 地址。因为客户端 (浏览器) 和服务器 (请求接收者) 之间有一个 HTTP 形式的约定,或者叫协议,这样服务器才可以分析请求,了解其组成并将响应发送回 web 浏览器。然后,web 浏览器将响应字符串处理成你能理解的内容。浏览像 Facebook,Google 和 Twitter 这样的网站,就意味着你一直在使用 HTTP。这些细节都被隐藏了,你的浏览器会自动处理好请求和响应。互联网的不同部分看起来像是这样的:
![different_parts](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-07-17_55a89c7e8cd5d.png)
## 客户端和服务器
最常见的客户端是你每天与之交互,被称为 Web 浏览器的应用程序。Web 浏览器常见的有 Internet Explorer、 火狐、 Safari,包括移动版本。Web 浏览器的职责是发送 HTTP 请求,并将响应处理成人类友好的形式显示在你的显示器上。web 浏览器并不是唯一的客户端,有很多工具或者应用都能发送 HTTP 请求。
你的请求的内容最终的接收者是被称为服务器的远程计算机。服务器也没啥神秘的,就是处理请求的设备,它们的工作就是发送一个响应回去。通常情况下,服务器发送回去的响应里面都包含了请求中指定的一些数据。
![client_and_server](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-07-17_55a89c9326c39.png)
## 资源 (Resources)
资源是一个通用术语,指的是互联网上你通过 URL 与其交互的东西。包括了图片,视频,网页和其他文件。资源并不限于文件或者网页。资源也可能是一个软件,一个炒股的软件,一个游戏。互联网上有无数的资源。
![resources](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-07-17_55a89c981e86c.png)
## 无状态的 (statelessness)
当一个协议设计成每一个请求/响应周期与前一个都是互相独立的话,我们就说这个协议是无状态的。对于 HTTP 要知道的一点就是,无状态协议对于服务器资源和易用性的影响。HTTP 协议下,服务器不需要在各次请求之间保留状态信息。结果就是如果一次请求出了问题,系统不必做任何清理。以上两个原因让 HTTP 协议变的很灵活,但同时也变的很难构建有状态的应用。因为 HTTP 本质上是个无状态的互联网协议,这就意味着 web 开发人员在构建有状态应用的时候,不得不努力想办法来模拟 web 应用中的有状态体验。 举个例子,当你上 Facebook 的时候,你先登录,然后你看到了一个 Facebook 的网页。这就是一个完整的请求/响应周期。你点了一张照片 -- 另一个请求/响应周期 -- 但是在第二个动作之后你并没有退出登录。如果 HTTP 是无状态的,它是怎么维持状态并且知道你刚刚已经登录过了呢?事实上,如果 HTTP 是无状态的,Facebook 是怎么知道哪个请求是你发出的?它是怎么区分你和其他用户的?这些都是 web 开发人员和 web 开发框架耍的小诡计,让 web 应用看起来像是有状态的,不过这些小诡计不在本书的讨论范围内。你所要记住的就是,尽管你觉得这个应用是有状态的,但是在它背后,这个 web 应用是构建在 HTTP,一个无状态协议之上的。以上,就是为什么 web 如此灵活和去中心化,同时又特别难控制,也是为什么 web 的安全性难以保证,为什么在 web 上构建应用不是易事。
## 小结
本章用一种简单的描述解释了互联网如何工作和几个术语。你也学到了无状态对 web 应用的影响。在下一章,我们会深入探讨 [http://www.google.com](http://www.google.com/) 这样的地址到底是什么以及它各部分的作用。
';
简介
最后更新于:2022-04-02 01:31:40
## 起步走
如果你跟大多数人一样,那么到现在你应该已经使用了很长时间的互联网了。每次都在浏览器顶部输入那些讨厌的 URL 网址,先写个 HTTP 啊,然后冒号,斜杠斜杠,然后三个大不留 (w),接着是一串所谓域名的东西,每次上网都重复这,但是你从来不知道这些到底代表了什么。
HTTP 是 web 的核心,也是动态 web 应用的核心。理解 HTTP 是理解现代 web 应用如何工作和如何构建的核心。本书会讲一些与 HTTP 相关的基本概念,通过本书你会更好的理解 web 和 web 应用是怎么工作的。
## 适合读者
这本书是写给那些经常使用互联网,但是从来没有自己动手构建过动态 web 应用的人。写给那些对 web 应用工作原理感兴趣的人,为学习更高级的 web 应用开发概念打下基础。
## 怎样阅读这本书
本书关注可读性,没有很多练习,因为主要是一本讲概念的书。不过呢,书中的例子还是请你边学边跟着做,毕竟要真正的理解一个知识点,动手做做是最好的方法。
';
背景知识
最后更新于:2022-04-02 01:31:38
前言
最后更新于:2022-04-02 01:31:36
## 致谢
这本书是由 Tealeaf Academy 团队共同协作完成的。
**[Tealeaf Academy](http://www.gotealeaf.com/) 联合创始人 [kevin Wang](http://www.gotealeaf.com/about_us) 推荐语:**
> 感谢 [@happypeter](https://selfstore.io/~happypeter) 和 Ruby China 社区的中文翻译工作,希望能对学习 web 开发的新手有所帮助。
### 贡献者
若没有 Aarti Parikh 和 Albert Agram 两位朋友的辛勤付出,这本书是不可能问世的。
特别感谢那些花费大量心血来打磨本书终稿的技术审阅者以及其他贡献者。
最后,感谢为本书提供无数建议和校正的学生们。是你们成就了这本书!
### 奉献爱心
亲爱的读者,若您在阅读本书的时候,发现任何错别字、错误或者对本书的内容安排有任何建议的话,都请告诉我们。
我们的邮箱 [books@gotealeaf.com](mailto:books@gotealeaf.com)。
## 版权声明
Copyright © 2014 Tealeaf Academy 保留所有权利
若未经 Tealeaf Academy 事先允许,本书内容不得用任何方式,电子、机械、影印、录音等抄袭、翻印、储存在检索系统上或传输。
';