diff --git a/18.md b/18.md index 632ee98..fe76d17 100644 --- a/18.md +++ b/18.md @@ -1,10 +1,22 @@ ## 十八、HTTP 和表单 +> 原文:[HTTP and Forms](http://eloquentjavascript.net/18_http.html) +> +> 译者:[飞龙](https://github.com/wizardforcel) +> +> 协议:[CC BY-NC-SA 4.0](http://creativecommons.org/licenses/by-nc-sa/4.0/) +> +> 自豪地采用[谷歌翻译](https://translate.google.cn/) +> +> 部分参考了[《JavaScript 编程精解(第 2 版)》](https://book.douban.com/subject/26707144/) + > 通信在实质上必须是无状态的,从客户端到服务器的每个请求都必须包含理解请求所需的所有信息,并且不能利用服务器上存储的任何上下文。 > > Roy Fielding,《Architectural Styles and the Design of Network-based Software Architectures》 -我们曾在第 13 章中提到过超文本传输协议(HTTP),万维网中通过该协议进行数据请求和传输。在本章中会对该协议进行详细介绍,并解释浏览器中JavaScript访问HTTP的方式。 +![](img/18-0.jpg) + +我们曾在第 13 章中提到过超文本传输协议(HTTP),万维网中通过该协议进行数据请求和传输。在本章中会对该协议进行详细介绍,并解释浏览器中 JavaScript 访问 HTTP 的方式。 ## 协议 @@ -36,23 +48,23 @@ Last-Modified: Mon, 08 Jan 2018 10:29:45 GMT GET /17_http.html HTTP/1.1 ``` -请求中的第一个单词是请求方法。GET表示我们希望得到一个我们指定的资源。其他常用方式还有DELETE,用于删除一个资源;PUT用于替换资源;POST用于发送消息。需要注意的是服务器并不需要处理所有收到的请求。如果你随机访问一个网站并请求删除主页,服务器很有可能会拒绝你的请求。 +请求中的第一个单词是请求方法。`GET`表示我们希望得到一个我们指定的资源。其他常用方式还有`DELETE`,用于删除一个资源;`PUT`用于替换资源;`POST`用于发送消息。需要注意的是服务器并不需要处理所有收到的请求。如果你随机访问一个网站并请求删除主页,服务器很有可能会拒绝你的请求。 方法名后的请求部分是所请求的资源的路径。在最简单的情况下,一个资源只是服务器中的一个文件。不过,协议并没有要求资源一定是实际文件。一个资源可以是任何可以像文件一样传输的东西。很多服务器会实时地生成这些资源。例如,如果你打开`github.com/marijnh`,服务器会在数据库中寻找名为`marijnjh`的用户,如果找到了则会为该用户的生成介绍页面。 -请求的第一行中位于资源路径后面的HTTP/1.1用来表明所使用的HTTP协议的版本。 +请求的第一行中位于资源路径后面的`HTTP/1.1`用来表明所使用的 HTTP 协议的版本。 在实践中,许多网站使用 HTTP v2,它支持与版本 1.1 相同的概念,但是要复杂得多,因此速度更快。 浏览器在与给定服务器通信时,会自动切换到适当的协议版本,并且无论使用哪个版本,请求的结果都是相同的。 由于 1.1 版更直接,更易于使用,因此我们将专注于此。 -服务器的响应也是以版本号开始的。版本号后面是应答状态,首先是一个三位的状态码,然后是一个可读的字符串。 +服务器的响应也是以版本号开始的。版本号后面是响应状态,首先是一个三位的状态码,然后是一个可读的字符串。 ```http HTTP/1.1 200 OK ``` -以2开头的状态码表示请求成功。以4开头的状态码表示请求中有错误。404是最著名的HTTP状态码了,表示找不到资源。以5开头的状态码表示服务器端出现了问题,而请求没有问题。 +以 2 开头的状态码表示请求成功。以 4 开头的状态码表示请求中有错误。404 是最著名的 HTTP 状态码了,表示找不到资源。以 5 开头的状态码表示服务器端出现了问题,而请求没有问题。 -请求或响应的第一行后可能会有任意个协议头,多个形如“name:value”的行表明了和请求或响应相关的更多信息。这些是示例响应中的头信息。 +请求或响应的第一行后可能会有任意个协议头,多个形如`name: value`的行表明了和请求或响应相关的更多信息。这些是示例响应中的头信息。 ```http Content-Length: 65585 @@ -60,17 +72,17 @@ Content-Type: text/html Last-Modified: Thu, 04 Jan 2018 14:05:30 GMT ``` -这些信息说明了响应文档的大小和类型。在这个例子中,响应是一个65585字节的HTML文档,同时也说明了该文档最后的更改时间。 +这些信息说明了响应文档的大小和类型。在这个例子中,响应是一个 65585 字节的 HTML 文档,同时也说明了该文档最后的更改时间。 -多数大多数协议头,客户端或服务器可以自由决定需要在请求或响应中包含的协议头,不过也有一些协议头是必需的。例如,指明主机名的Host头在请求中是必须的,因为一个服务器可能在一个IP地址下有多个主机名服务,如果没有Host头,服务器则无法判断客户端尝试请求哪个主机。 +多数大多数协议头,客户端或服务器可以自由决定需要在请求或响应中包含的协议头,不过也有一些协议头是必需的。例如,指明主机名的`Host`头在请求中是必须的,因为一个服务器可能在一个 IP 地址下有多个主机名服务,如果没有`Host`头,服务器则无法判断客户端尝试请求哪个主机。 -请求和应答可能都会在协议头后包含一个空行,后面则是消息体,包含所发送的数据。GET和DELETE请求不单独发送任何数据,但PUT和POST请求则会。同样地,一些响应类型(如错误应答)不需要有消息体。 +请求和响应可能都会在协议头后包含一个空行,后面则是消息体,包含所发送的数据。`GET`和`DELETE`请求不单独发送任何数据,但`PUT`和`POST`请求则会。同样地,一些响应类型(如错误响应)不需要有消息体。 ## 浏览器和 HTTP -正如上例所示,当我们在浏览器地址栏输入一个URL后浏览器会发送一个请求。当HTML页面中包含有其他的文件,例如图片和JavaScript文件时,浏览器也会一并获取这些资源。 +正如上例所示,当我们在浏览器地址栏输入一个 URL 后浏览器会发送一个请求。当 HTML 页面中包含有其他的文件,例如图片和 JavaScript 文件时,浏览器也会一并获取这些资源。 -一个较为复杂的网站通常都会有10到200个不等的资源。为了可以很快地取得这些资源,浏览器会同时发送多个`GET`请求,而不是一次等待一个请求。此类文档都是通过GET方法来获取的。 +一个较为复杂的网站通常都会有 10 到 200 个不等的资源。为了可以很快地取得这些资源,浏览器会同时发送多个`GET`请求,而不是一次等待一个请求。此类文档都是通过`GET`方法来获取的。 HTML页面可能包含表单,用户可以在表单中填入一些信息然后由浏览器将其发送到服务器。如下是一个表单的例子。 @@ -92,7 +104,7 @@ GET /example/message.html?name=Jean&message=Yes%3F HTTP/1.1 问号表示路径的末尾和查询字符串的起始。后面是多个名称和值,这些名称和值分别对应`form`输入字段中的`name`属性和这些元素的内容。`&`字符用来分隔不同的名称对。 -在这个 URL 中,经过编码的消息实际原本是“Yes?”,只不过浏览器用奇怪的代码替换了问号。我们必须替换掉请求字符串中的一些字符。使用%3F替换的问号就是其中之一。这样看,似乎有一个不成文的规定,每种格式都会有自己的转义字符。这里的编码格式叫作URL编码,使用一个百分号和16进制的数字来对字符进行编码。在这个例子中,3F(十进制为63)是问号字符的编码。JavaScript提供了encodeURIComponent和decodeURIComponent函数来按照这种格式进行编码和解码。 +在这个 URL 中,经过编码的消息实际原本是`"Yes?"`,只不过浏览器用奇怪的代码替换了问号。我们必须替换掉请求字符串中的一些字符。使用`%3F`替换的问号就是其中之一。这样看,似乎有一个不成文的规定,每种格式都会有自己的转义字符。这里的编码格式叫作 URL 编码,使用一个百分号和16进制的数字来对字符进行编码。在这个例子中,3F(十进制为 63)是问号字符的编码。JavaScript 提供了`encodeURIComponent`和`decodeURIComponent`函数来按照这种格式进行编码和解码。 ```js console.log(encodeURIComponent("Yes?")); @@ -101,7 +113,7 @@ console.log(decodeURIComponent("Yes%3F")); // → Yes? ``` -如果我们将本例HTML表单中的method属性更改为POST,则浏览器会使用POST方法发送该表单,并将请求字符串放到请求体中,而不是添加到URL中。 +如果我们将本例 HTML 表单中的`method`属性更改为`POST`,则浏览器会使用`POST`方法发送该表单,并将请求字符串放到请求正文中,而不是添加到 URL 中。 ```http POST /example/message.html HTTP/1.1 @@ -118,7 +130,7 @@ name=Jean&message=Yes%3F ## Fetch -浏览器 JavaScript 可以通过`Fetch`接口生成 HTTP 请求。 由于它比较新,所以它很方便地使用了`Promise`(这在浏览器接口中很少见)。 +浏览器 JavaScript 可以通过`fetch`接口生成 HTTP 请求。 由于它比较新,所以它很方便地使用了`Promise`(这在浏览器接口中很少见)。 ```js fetch("example/data.txt").then(response => { @@ -166,7 +178,7 @@ fetch("example/data.txt", {headers: {Range: "bytes=8-19"}}) // → the content ``` -览器将自动添加一些请求头,例如`"Host"`和服务器需要的协议头,来确定正文的大小。 但是对于包含认证信息或告诉服务器想要接收的文件格式,添加自己的协议头通常很有用。 +浏览器将自动添加一些请求头,例如`Host`和服务器需要的协议头,来确定正文的大小。 但是对于包含认证信息或告诉服务器想要接收的文件格式,添加自己的协议头通常很有用。 ## HTTP 沙箱 @@ -182,25 +194,25 @@ Access-Control-Allow-Origin: * ## 运用 HTTP -当构建一个需要让浏览器(客户端)的JavaScript程序和服务器端的程序进行通信的系统时,有一些不同的方式可以实现这个功能。 +当构建一个需要让浏览器(客户端)的 JavaScript 程序和服务器端的程序进行通信的系统时,有一些不同的方式可以实现这个功能。 一个常用的方法是远程过程调用,通信遵从正常的方法调用方式,不过调用的方法实际运行在另一台机器中。调用包括向服务器发送包含方法名和参数的请求。响应的结果则包括函数的返回值。 -当考虑远程过程调用时,HTTP只是通信的载体,并且你很可能会写一个抽象层来隐藏细节。 +当考虑远程过程调用时,HTTP 只是通信的载体,并且你很可能会写一个抽象层来隐藏细节。 -另一个方法是使用一些资源和HTTP方法来建立自己的通信。不同于远程调用方法addUser,你需要发送一个PUT请求到/users/larry,不同于将用户属性进行编码后作为参数传递,你定义了一个 JSON 文档格式(或使用一种已有的格式)来展示一个用户。PUT请求的正文则只是这样的一个用来建立新资源的文档。由GET方法获取的资源则是自愿的URL(例如,/users/larry),该URL返回代表这个资源的文档。 +另一个方法是使用一些资源和 HTTP 方法来建立自己的通信。不同于远程调用方法`addUser`,你需要发送一个`PUT`请求到`users/larry`,不同于将用户属性进行编码后作为参数传递,你定义了一个 JSON 文档格式(或使用一种已有的格式)来展示一个用户。`PUT`请求的正文则只是这样的一个用来建立新资源的文档。由`GET`方法获取的资源则是自愿的 URL(例如,`/users/larry`),该 URL 返回代表这个资源的文档。 -第二种方法使用了HTTP的一些特性,所以使得整体更简洁。例如对于资源缓存的支持(在客户端存一份副本用于快速访问)。HTTP中使用的概念设计良好,可以提供一组有用的原则来设计服务器接口。 +第二种方法使用了 HTTP 的一些特性,所以使得整体更简洁。例如对于资源缓存的支持(在客户端存一份副本用于快速访问)。HTTP 中使用的概念设计良好,可以提供一组有用的原则来设计服务器接口。 ## 安全和 HTTPS 通过互联网传播的数据,往往走过漫长而危险的道路。 为了到达目的地,它必须跳过任何东西,从咖啡店的 Wi-Fi 到由各个公司和国家管理的网络。 在它的路线上的任何位置,它都可能被探测或者甚至被修改。 -如果对某件事保密是重要的,例如您的电子邮件帐户的密码,或者它到达目的地而未经修改是重要的,例如帐户号码,您使用它在银行网站上转账,纯 HTTP 就不够好了。 +如果对某件事保密是重要的,例如你的电子邮件帐户的密码,或者它到达目的地而未经修改是重要的,例如帐户号码,你使用它在银行网站上转账,纯 HTTP 就不够好了。 安全的 HTTP 协议,其 URL 以`https://`开头,是一种难以阅读和篡改的,HTTP 流量的封装方式。 在交换数据之前,客户端证实该服务器是它所声称的东西,通过要求它证明,它具有由浏览器承认的证书机构所颁发的证书。 接下来,通过连接传输的所有数据,都将以某种方式加密,它应该防止窃听和篡改。 -因此,当 HTTPS 正常工作时,它可以阻止某人冒充您想要与之通话的网站,以及某人窥探您的通信。 这并不完美,由于伪造或被盗的证书和损坏的软件,存在各种 HTTPS 失败的事故,但它比纯 HTTP 更安全。 +因此,当 HTTPS 正常工作时,它可以阻止某人冒充你想要与之通话的网站,以及某人窥探你的通信。 这并不完美,由于伪造或被盗的证书和损坏的软件,存在各种 HTTPS 失败的事故,但它比纯 HTTP 更安全。 ## 表单字段 @@ -208,21 +220,21 @@ Access-Control-Allow-Origin: * 但是它们的元素是 DOM 的一部分,就像页面的其他部分一样,并且表示表单字段的 DOM 元素,支持许多其他元素上不存在的属性和事件。 这些使其可以使用 JavaScript 程序检查和控制这些输入字段,以及可以执行一些操作,例如向表单添加新功能,或在 JavaScript 应用程序中使用表单和字段作为积木。 -一个网页表单在其
标签中包含若干个输入字段。HTML允许多个的不同风格的输入字段,从简单的开/关选择框到下拉菜单和进行输入的字段。本书不会全面的讨论每一个输入字段类型,不过我们会先大概讲述一下。 +一个网页表单在其``标签中包含若干个输入字段。HTML 允许多个的不同风格的输入字段,从简单的开关选择框到下拉菜单和进行输入的字段。本书不会全面的讨论每一个输入字段类型,不过我们会先大概讲述一下。 -很多字段类型都使用标签。标签的type属性用来选择字段的种类,下面是一些常用的类型。 +很多字段类型都使用``标签。标签的`type`属性用来选择字段的种类,下面是一些常用的``类型。 -+ text:一个单行的文本输入框。 ++ `text`:一个单行的文本输入框。 -+ password:和text相同但隐藏了输入内容。 ++ `password`:和`text`相同但隐藏了输入内容。 -+ checkbox:一个复选框。 ++ `checkbox`:一个复选框。 -+ radio一个多选择字段中的一个单选框。 ++ `radio`:一个多选择字段中的一个单选框。 -+ file:允许用户从本机选择文件上传。 ++ `file`:允许用户从本机选择文件上传。 -表单字段并不一定要出现在标签中。你可以把表单字段放置在一个页面的任何地方。但这样不带表单的字段不能被提交(一个完整的表单才可以),当需要和JavaScript进行响应时,我们通常也不希望按常规的方式提交表单。 +表单字段并不一定要出现在``标签中。你可以把表单字段放置在一个页面的任何地方。但这样不带表单的字段不能被提交(一个完整的表单才可以),当需要和 JavaScript 进行响应时,我们通常也不希望按常规的方式提交表单。 ```html

(text)

@@ -236,7 +248,7 @@ Access-Control-Allow-Origin: * 这些元素的 JavaScript 接口和元素类型不同。 -多行文本输入框有其自己的标签结束标签并使用标签之间的文本作为初始值,而不是使用value属性存储文本。 +多行文本输入框有其自己的标签``结束标签并使用标签之间的文本作为初始值,而不是使用`value`属性存储文本。 ```html ``` -`标签用来创造一个可以让用户从一些提前设定好的选项中进行选择的字段。 ```html ``` -当一个表单字段中的内容更改时会触发change事件。 +当一个表单字段中的内容更改时会触发`change`事件。 ## 聚焦 -不同于HTML文档中的其他元素,表单字段可以获取键盘焦点。当点击或以某种方式激活时,他们会成为激活的元素,并接受键盘的输入。 +不同于 HTML 文档中的其他元素,表单字段可以获取键盘焦点。当点击或以某种方式激活时,他们会成为激活的元素,并接受键盘的输入。 因此,只有获得焦点时,你才能输入文本字段。 其他字段对键盘事件的响应不同。 例如,` @@ -278,35 +290,35 @@ three ``` -对于一些页面,用户希望立刻使用到一个表单字段。JavaScript可以在页面载入完成时将焦点放到这些字段上,HTML提供了autofocus属性,可以实现相同的效果,并让浏览器知道我们正在尝试实现的事情。这向浏览器提供了选项,来禁用一些错误的操作,例如用户希望将焦点置于其他地方。 +对于一些页面,用户希望立刻使用到一个表单字段。JavaScript 可以在页面载入完成时将焦点放到这些字段上,HTML 提供了`autofocus`属性,可以实现相同的效果,并让浏览器知道我们正在尝试实现的事情。这向浏览器提供了选项,来禁用一些错误的操作,例如用户希望将焦点置于其他地方。 -浏览器也允许用户通过TAB键来切换焦点。通过tabindex属性可以改变元素接受焦点的顺序。后面的例子会让焦点从文本输入框之间跳转到OK按钮而不是到帮助链接。 +浏览器也允许用户通过 TAB 键来切换焦点。通过`tabindex`属性可以改变元素接受焦点的顺序。后面的例子会让焦点从文本输入框跳转到 OK 按钮而不是到帮助链接。 ```html (help) ``` -默认情况下,多数的HTML元素不能被聚焦。但是可以通过添加tabindex属性使任何元素可聚焦。`tabindex`为 -1 使 TAB 键跳过元素,即使它通常是可聚焦的。 +默认情况下,多数的 HTML 元素不能拥有焦点。但是可以通过添加`tabindex`属性使任何元素可聚焦。`tabindex`为 -1 使 TAB 键跳过元素,即使它通常是可聚焦的。 ## 禁用字段 -所有的表单字段都可以通过其disable属性来禁用。它是一个可以被指定为没有值的属性 - 事实上它出现在所有禁用的元素中。 +所有的表单字段都可以通过其`disable`属性来禁用。它是一个可以被指定为没有值的属性 - 事实上它出现在所有禁用的元素中。 ```html ``` -禁用的字段不能被聚焦或更改,浏览器使它们变成灰色。 +禁用的字段不能拥有焦点或更改,浏览器使它们变成灰色。 当一个程序在处理一些由按键或其他控制方式出发的事件,并且这些事件可能要求和服务器的通信时,将元素禁用直到动作完成可能是一个很好的方法。按照这用方式,当用户失去耐心并且再次点击时,不会意外的重复这一动作。 ## 作为整体的表单 -当一个字段被包含在元素中时,其DOM元素会有一个form属性指向form的DOM元素。元素则会有一个叫作elements属性,包含一个类似于数据的集合,其中包含全部的字段。 +当一个字段被包含在``元素中时,其 DOM 元素会有一个`form`属性指向`form`的 DOM 元素。``元素则会有一个叫作`elements`属性,包含一个类似于数据的集合,其中包含全部的字段。 -一个表单字段的name属性会决定在form提交时其内容的辨别方式。同时在获取form的elements属性时也可以作为一种属性名,所以elements属性既可以像数组(由编号来访问)一样使用也可以像映射一样访问(通过名字访问)。 +一个表单字段的`name`属性会决定在`form`提交时其内容的辨别方式。同时在获取`form`的`elements`属性时也可以作为一种属性名,所以`elements`属性既可以像数组(由编号来访问)一样使用也可以像映射一样访问(通过名字访问)。 ```html @@ -325,9 +337,9 @@ three ``` -一个type属性为submit的按钮在点击时,会提交表单。在一个form被聚焦时,点击enter键也会有同样的效果。 +`type`属性为`submit`的按钮在点击时,会提交表单。在一个`form`拥有焦点时,点击`enter`键也会有同样的效果。 -通常在提交一个表单时,浏览器会将页面导航到form的action属性指明的页面,使用GET或POST请求。但是在这些发生之前,“submit”事件会被触发。这个事件可以由JavaScript处理,并且处理函数可以通过调用事件对象的preventDefault来禁用默认行为。 +通常在提交一个表单时,浏览器会将页面导航到`form`的`action`属性指明的页面,使用`GET`或`POST`请求。但是在这些发生之前,`"submit"`事件会被触发。这个事件可以由 JavaScript 处理,并且处理器可以通过调用事件对象的`preventDefault`来禁用默认行为。 ```html @@ -343,17 +355,17 @@ three ``` -在JavaScript中submit事件有多种用途。我们可以编写代码来检测用户输入是否正确并且立刻提示错误信息,而不是提交表单。或者我们可以禁用正常的提交方式,正如这个例子中,让我们的程序处理输入,可能使用`fetch`将其发送到服务器而不重新加载页面。 +在 JavaScript 中`submit`事件有多种用途。我们可以编写代码来检测用户输入是否正确并且立刻提示错误信息,而不是提交表单。或者我们可以禁用正常的提交方式,正如这个例子中,让我们的程序处理输入,可能使用`fetch`将其发送到服务器而不重新加载页面。 ## 文本字段 -由type属性为text或password的标签和textarea标签组成的字段有相同的接口。其DOM元素都有一个value属性,保存了为字符串格式的当前内容。将这个属性更改为另一个值将改变字段的内容。 +由`type`属性为`text`或`password`的``标签和`textarea`标签组成的字段有相同的接口。其 DOM 元素都有一个`value`属性,保存了为字符串格式的当前内容。将这个属性更改为另一个值将改变字段的内容。 -文本字段selectionStart和selectEnd属性包含光标和所选文字的信息。当没有选中文字时,这两个属性的值相同,表明当前光标的信息。例如,0表示文本的开始,10表示光标在第十个字符之后。当一部分字段被选中时,这两个属性值会不同,表明选中文字开始位置和结束位置。 +文本字段`selectionStart`和`selectEnd`属性包含光标和所选文字的信息。当没有选中文字时,这两个属性的值相同,表明当前光标的信息。例如,0 表示文本的开始,10 表示光标在第十个字符之后。当一部分字段被选中时,这两个属性值会不同,表明选中文字开始位置和结束位置。 和正常的值一样,这些属性也可以被更改。 -想象你正在编写关于Knaseknemwy的文章,但是名字拼写有一些问题,后续代码将 @@ -377,7 +389,7 @@ three ``` -replaceSelection函数用给定的字符串替换当前选中的文本字段内容,并将光标移动到替换内容后让用户可以继续输入。Change事件不会在每次有输入时都被调用,而是在内容在改变并失焦后触发。为了及时的响应文本字段的改变,则需要为input事件注册一个处理函数,每当用户有输入或更改时就被触发。 +`replaceSelection`函数用给定的字符串替换当前选中的文本字段内容,并将光标移动到替换内容后让用户可以继续输入。`change`事件不会在每次有输入时都被调用,而是在内容在改变并失焦后触发。为了及时的响应文本字段的改变,则需要为`input`事件注册一个处理器,每当用户有输入或更改时就被触发。 下面的例子展示一个文本字段和一个展示字段中的文字的当前长度的计数器。 @@ -394,7 +406,7 @@ replaceSelection函数用给定的字符串替换当前选中的文本字段内 ## 选择框和单选框 -一个选择框只是一个双选切换。其值可以通过其包含一个布尔值的checked属性来获取和更改。 +一个选择框只是一个双选切换。其值可以通过其包含一个布尔值的`checked`属性来获取和更改。 ```html