Eloquent JavaScript 笔记 十七:HTTP
来源:互联网 发布:php bin2hex写入文件 编辑:程序博客网 时间:2024/06/06 00:11
1. The Protocol
在browser的地址栏输入 eloquentjavascript.net/17_http.html,browser会查找 eloquentjavascript.net 这个server,尝试与它的80端口建立TCP连接。
request:
如果连接成功,发送如下请求:
第一行:
method: GET, POST, DELETE, PUT
path: server 上某个资源的路径。是磁盘上的文件还是临时生成的html,由server决定。
version: HTTP 协议的版本号。
第二行及后面的都是header,以键值对方式出现。 Host 是必须的属性。
如果是POST,在header之后会有一个空行,空行之后是要上传的数据。
response:
server的应答如下:
第一行:
version: HTTP 协议版本
200: status code。 2xx 代表请求成功,4xx 代表 request 有问题(例如:404, 请求的资源不存在),5xx 代表服务器发生错误。
OK:status code对应的说明,便于人类阅读。
第二行及后面的都是header,以键值对方式出现。
空行以后是数据,通常叫做body。就是我们请求的html文件,browser会把它显示出来。
2. Browsers and HTTP
<form method="GET" action="example/message.html"> <p>Name: <input type="text" name="name"></p> <p>Message:<br><textarea name="message"></textarea></p> <p><button type="submit">Send</button></p></form>点击Send按钮,会发起request:
GET /example/message.html?name=Jean&message=Yes%3F HTTP/1.1
看看如何通过form生成这个path:
1. method="GET" 对应request中的 GET
2. action="example/message.html" 对应request中的path
3. <input type="text" name="name"> 对应 request中问号后面的第一个参数 name=Jean 。 我们假设用户在testfield 中输入了: Jean 。
4. <textarea name="message"> 对应request中的第二个参数 message=Yes%3F。我们假设用户在textarea中输入了: Yes? 。
5. ?name=Jean&message=Yes%3F 这叫querystring。
在url中一些字符有特殊的含义,例如:?,&,= 等等,遇到这些字符需要escape,如上面的 %3F,实际上它是个 ?。这叫 URL encoding。
如果我们自己的js代码中需要做URL encoding 或 decoding,可以使用js提供的两个函数:
console.log(encodeURIComponent("Hello & goodbye"));// → Hello%20%26%20goodbyeconsole.log(decodeURIComponent("Hello%20%26%20goodbye"));// → Hello & goodbye
如果<form method="POST" ...>,name=Jean&message=Yes%3F 会作为request的body发送,而不是作为path中的querystring。
3. XMLHttpRequest
使用XMLHttpRequest可以通过js代码发送http request。不用刷新整个html页面就可以更新页面的一部分。是Microsoft发明的,最初用在IE中。后来主流浏览器都实现了这个功能。
一个应用实例:搜索引擎的备选列表
4. Sending a Request
var req = new XMLHttpRequest();req.open("GET", "example/data.txt", false);req.send(null);console.log(req.responseText);// → This is the content of data.txt说明:
1. open() 的第一个参数,request method
2. open() 的第二个参数 "example/data.txt",这是一个相对地址,server上相对于当前页面的路径。如果以 / 开头,指server的根目录。
3. open() 的第三个参数,fasle - 同步请求,收到server的response之后,才会req.send() 才会返回。 true - 异步请求。
4. send() 发送数据,body,对于GET,可以是null。
5. req.responseText,response的body。
XMLHttpRequest 还提供了其他一些属性:
var req = new XMLHttpRequest();req.open("GET", "example/data.txt", false);req.send(null);console.log(req.status, req.statusText);// → 200 OKconsole.log(req.getResponseHeader("content-type"));// → text/plainreq.status, req.statusText, getResponseHeader() 等等。
注意,header名字是不区分大小写的。content-type 和 Content-Type 一样。
在发送请求前,我们可以用 setRequestHeader() 添加额外的 header。
5. Asynchronous Requests
鉴于网络环境的不确定性,最好使用异步请求:
var req = new XMLHttpRequest();req.open("GET", "example/data.txt", true);req.addEventListener("load", function() { console.log("Done:", req.status);});req.send(null);说明:
1. open() 的第三个参数是true。
2. send() 函数立即返回,而此时还没有收到server的response,所以requestText为空。
3. 添加 load 事件句柄,收到server的response时,该事件会被触发。
6. Fetching XML Data
XMLHttpRequest 之所以叫这个名字,是因为上世纪末,微软大力推广XML,期望XML成为互联网数据传输的标准。XMLHttpRequest为方便的处理XML文档,提供了辅助函数:
var req = new XMLHttpRequest();req.open("GET", "example/fruit.xml", false);req.send(null);console.log(req.responseXML.querySelectorAll("fruit").length);// → 3req.responseXML
但时至今日,人们更倾向于使用JSON:
var req = new XMLHttpRequest();req.open("GET", "example/fruit.json", false);req.send(null);console.log(JSON.parse(req.responseText));// → {banana: "yellow", lemon: "yellow", cherry: "red"}
7. HTTP SandBoxing
XMLHttpRequest 可以向任意url发送请求吗?
基于安全原因,它只能向同一网站发送请求。这也叫 sandbox。
如果server认为可以允许向其他网站发送请求,可以在自己的response中添加如下header:
Access-Control-Allow-Origin: *
8. Abstracting Requests
第十章讲AMD时,提到过 backgroundReadFile(),可以这么实现:
function backgroundReadFile(url, callback) { var req = new XMLHttpRequest(); req.open("GET", url, true); req.addEventListener("load", function() { if (req.status < 400) callback(req.responseText); }); req.send(null);}上面的代码没有做错误处理。 错误处理有两种:
1. 可预期的,如上面的 req.status,在 if 语句后面需要加上else。
2. 异常,对于异步请求,我们无法使用try catch捕获异常,因为,我们无法把异步代码用try包起来,send() 函数会立即返回,真正与服务器通信的代码不再我们控制范围之内。
try { backgroundReadFile("example/data.txt", function(text) { if (text != "expected") throw new Error("That was unexpected"); });} catch (e) { console.log("Hello from the catch block");}这个try只能捕获 Error("That was unexpected"); ,因为真正的通信代码都不在try块之内。
我们把backgroundReadFile() 写的更通用一些 getURL(),为了处理可预期的error,callback() 需要增加一个参数:
function getURL(url, callback) { var req = new XMLHttpRequest(); req.open("GET", url, true); req.addEventListener("load", function() { if (req.status < 400) callback(req.responseText); else callback(null, new Error("Request failed: " + req.statusText)); }); req.addEventListener("error", function() { callback(null, new Error("Network error")); }); req.send(null);}getURL的使用方法:
getURL("data/nonsense.txt", function(content, error) { if (error != null) console.log("Failed to fetch nonsense.txt: " + error); else console.log("nonsense.txt: " + content);});这么写对于异常的捕获没有任何帮助。
9. Promises
异步函数的异常如何处理? 如果有多个异步功能需要一个接一个的执行,代码应该怎么写? 想一想就头大。
还好,为了解决这个问题,有人写了一个库 promise (www.promisejs.org) 。 而且,这个功能已经被下一个js版本采纳。但现在,我们还是只能包含这个第三方库。
先去官网下载 promise.js,包含在自己的html中。
用Promise改写上面的例子:
function get(url) { return new Promise(function(succeed, fail) { var req = new XMLHttpRequest(); req.open("GET", url, true); req.addEventListener("load", function() { if (req.status < 400) succeed(req.responseText); else fail(new Error("Request failed: " + req.statusText)); }); req.addEventListener("error", function() { fail(new Error("Network error")); }); req.send(null); });}使用方法:
get("example/data.txt").then(function(text) { console.log("data.txt: " + text);}, function(error) { console.log("Failed to fetch data.txt: " + error);});说明:
“new Promise(function(succeed, fail) { ” 这里面的succeed和fail都是function,就是then() 函数传入的两个function。
看一个串联多个异步行为的例子:
function showMessage(msg) { var elt = document.createElement("div"); elt.textContent = msg; return document.body.appendChild(elt); } function getJSON(url) { return get(url).then(JSON.parse); } var loading = showMessage("Loading ..."); getJSON("example/bert.json").then(function (bert) { return getJSON(bert.spouse); }).then(function (spouse) { return getJSON(spouse.mother); }).then(function (mother) { showMessage("The name is " + mother.name); }).catch(function (error) { showMessage(String(error)); }).then(function () { document.body.removeChild(loading); });
功能流程:
1. 显示loading信息。
2. 读取 bert.json,
3. 完成后,读取他配偶的数据,
4. 完成后,读取配偶妈妈的数据,
5. 完成后,显示妈妈的名字。
6. 如果出错,显示error。
7. 最后,无论成功还是失败,移除loading信息。
如果一个Promise(叫它PromiseA)的then() 返回一个Promise(叫它PromiseB),那么,PromiseA执行完之后,就会执行PromiseB。上个例子中串联了多个异步调用。其中任何一个异步调用出错,都会跳转到catch。 最后一个then类似于异常处理中的finally,不论发生任何情况,都会被执行。
Promise 的原理和接口远不止这些,需要找一个专门的文档看一看。
10. Appreciating HTTP
bla bla bla,没啥好看的。
11. Security and HTTPS
通过HTTP访问网络时,中间不知道要经过多少台路由器、网关、服务器,而HTTP协议本身传输的内容都是明文(文本、字符串),如果有敏感/重要数据的话,这是很危险的,中途可能被人劫持、篡改。例如,银行账号、密码。
为了提高安全性,需要用HTTPS。通过HTTPS协议传输的数据都是加密的,使用非对称加密算法。由大家信任的机构分发证书,发送数据时,使用证书中的密钥加密。由于算法足够复杂,破解很困难,从而避免了数据劫持。
12. Exercise: Content Negotiation
客户端给server发送请求时,可以添加一种header:
accept: type
type就是文档类型,例如:text/html, appplication/json 等。
server根据type,发送不同的response。
function requestAuthor(type) { var req = new XMLHttpRequest(); req.open("GET", "http://eloquentjavascript.net/author", false); req.setRequestHeader("accept", type); req.send(null); return req.responseText;}var types = ["text/plain", "text/html", "application/json", "application/rainbows+unicorns"];types.forEach(function(type) { try { console.log(type + ":\n", requestAuthor(type), "\n"); } catch (e) { console.log("Raised error: " + e); }});
13. Waiting for Multiple Promises
对promise还不熟,以后再看。
- Eloquent JavaScript 笔记 十七:HTTP
- 《Eloquent JavaScript》笔记--函数;
- Eloquent JavaScript 笔记 三: Functions
- Eloquent JavaScript 笔记 十: Modules
- Eloquent JavaScript 笔记 十三:DOM
- 《Eloquent JavaScript》笔记--对象与数组
- 《Eloquent JavaScript》笔记--程序的结构;
- Eloquent JavaScript 笔记 二:Program Structure
- Eloquent JavaScript 笔记 五: High-Order Functions
- Eloquent JavaScript 笔记 四:Objects and Arrays
- Eloquent JavaScript 笔记 七: Electronic Life
- Eloquent JavaScript 笔记 十一:A Programming Language
- Eloquent JavaScript 笔记 十四:Handling Event
- Eloquent JavaScript 笔记 十五:A Platform Game
- Eloquent JavaScript 笔记 十六:Drawing on Canvas
- Eloquent JavaScript 笔记 十九:Node.js
- Eloquent JavaScript 笔记 二十:略有遗憾
- Eloquent JavaScript 笔记 十二:Javascript and the Browser
- lintcode(87)删除二叉查找树的节点
- 关于spring mvc中前台怎么获取session
- Android线程池
- Navicat Premium破解
- mysqlbinlog命令介绍
- Eloquent JavaScript 笔记 十七:HTTP
- Java篇--Java语言的基本语法3
- Elasticsearch5和head最新插件学习,JavaAPI demo
- Leetcode 474. Ones and Zeroes
- 刷清橙OJ--A1089.阶乘计算
- Unity 阻止手机熄屏
- JDK1.8的新特性——注解Annotation更多场景的使用
- PHP使用PDO调用sqlserver存储过程记录【分享个通用方法】
- Leetcode Add Binary