网络篇

笔者最近在对原生JS的知识做系统梳理,因为我觉得JS作为前端工程师的根本技术,学再多遍都不为过。打算来做一个系列,以一系列的问题为驱动,当然也会有追问和扩展,内容系统且完整,对初中级选手会有很好的提升,高级选手也会得到复习和巩固。

第一章: 能不能说一说XSS攻击

什么是 XSS 攻击?

XSS 全称是 Cross Site Scripting(即跨站脚本),为了和 CSS 区分,故叫它XSS。XSS 攻击是指浏览器中执行恶意脚本(无论是跨域还是同域),从而拿到用户的信息并进行操作。 这些操作一般可以完成下面这些事情:

  • 窃取Cookie
  • 监听用户行为,比如输入账号密码后直接发送到黑客服务器
  • 修改 DOM 伪造登录表单
  • 在页面中生成浮窗广告

通常情况,XSS 攻击的实现有三种方式---存储型反射型文档型

存储型

存储型,顾名思义就是将恶意脚本存储了起来,确实,存储型的 XSS 将脚本存储到了服务端的数据库,然后在客户端执行这些脚本,从而达到攻击的效果。 常见的场景是留言评论区提交一段脚本代码,如果前后端没有做好 转义 的工作,那评论内容存到了数据库,在页面渲染过程中直接执行, 相当于执行一段未知逻辑的 JS 代码,是非常恐怖的。这就是存储型的 XSS 攻击。

反射型

反射型XSS指的是恶意脚本作为网络请求的一部分。比如我输入:

html 复制代码
http://www.kenguba.com?q=<script>alert("你完蛋了")</script>

这杨,在服务器端会拿到q参数,然后将内容返回给浏览器端,浏览器将这些内容作为HTML的一部分解析,发现是一个脚本,直接执行,这样就被攻击了。 之所以叫它反射型, 是因为恶意脚本是通过作为网络请求的参数,经过服务器,然后再反射到HTML文档中,执行解析。和存储型不一样的是,服务器并不会存储这些恶意脚本

文档型

文档型的 XSS 攻击并不会经过服务端,而是作为中间人的角色,在数据传输过程劫持到网络数据包,然后修改里面的 html 文档! 这样的劫持方式包括 WIFI路由器劫持 或者 本地恶意软件 等。

防范措施

明白了三种XSS攻击的原理,我们能发现一个共同点: 都是让恶意脚本直接能在浏览器中执行。 那么要防范它,就是要避免这些脚本代码的执行。 为了完成这一点,必须做到一个信念,两个利用

一个信念

千万不要相信任何用户的输入!无论是在前端和服务端,都要对用户的输入进行转码 或者过滤。 如:

html 复制代码
<script>alert('你完蛋了')</script>

转码后变为:

html 复制代码
&lt;script&gt;alert(&#39;你完蛋了&#39;)&lt;/script&gt;

这样的代码在 html 解析的过程中是无法执行的。 当然也可以利用关键词过滤的方式,将 script 标签给删除

利用 CSP

CSP(Content-Security-Policy),即浏览器中的内容安全策略,它的核心思想就是服务器决定浏览器加载哪些资源,具体来说可以完成以下功能:

  • 限制其他域下的资源加载。
  • 禁止向其它域提交数据。
  • 提供上报机制,能帮助我们及时发现 XSS 攻击

你可以使用 Content-Security-PolicyHTTP头部 来指定你的策略,像这样:

html 复制代码
Content-Security-Policy: policy

实例1: 一个网站管理者想要所有内容均来自站点的同一个源 (不包括其子域名)

html 复制代码
Content-Security-Policy: default-src 'self'

实例2: 一个网站管理者允许内容来自信任的域名及其子域名 (域名不必须与CSP设置所在的域名相同)

html 复制代码
Content-Security-Policy: default-src 'self' *.trusted.com

实例3: 一个网站管理者允许网页应用的用户在他们自己的内容中包含来自任何源的图片, 但是限制音频或视频需从信任的资源提供者(获得),所有脚本必须从特定主机服务器获取可信的代码.

css 复制代码
Content-Security-Policy: default-src 'self'; img-src *; media-src media1.com media2.com; script-src userscripts.example.com

实例4: 在这里,各种内容默认仅允许从文档所在的源获取, 但存在如下例外:

一个线上银行网站的管理者想要确保网站的所有内容都要通过SSL方式获取,以避免攻击者窃听用户发出的请求。

html 复制代码
Content-Security-Policy: default-src https://onlinebanking.jumbobank.com

该服务器仅允许通过HTTPS方式并仅从onlinebanking.jumbobank.com域名来访问文档。 实例5: 一个在线邮箱的管理者想要允许在邮件里包含HTML,同样图片允许从任何地方加载,但不允许JavaScript或者其他潜在的危险内容(从任意位置加载)。

html 复制代码
Content-Security-Policy: default-src 'self' *.mailsite.com; img-src *

注意这个示例并未指定script-src (en-US)。在此CSP示例中,站点通过default-src指令的对其进行配置,这也同样意味着脚本文件仅允许从原始服务器获取。

启用违例报告 默认情况下,违规报告并不会发送。为启用发送违规报告,你需要指定report-uri (en-US)策略指令,并提供至少一个URI地址去递交报告:

html 复制代码
Content-Security-Policy: default-src 'self'; report-uri http://reportcollector.example.com/collector.cgi

然后你需要设置你的服务器能够接收报告,使其能够以你认为恰当的方式存储并处理这些报告。 作为报告的JSON对象报告包含了以下数据: document-uri 发生违规的文档的URI。 referrer 违规发生处的文档引用(地址)。 blocked-uri 被CSP阻止的资源URI。如果被阻止的URI来自不同的源而非文档URI,那么被阻止的资源URI会被删减,仅保留协议,主机和端口号。 violated-directive 违反的策略名称。 original-policy 在Content-Security-PolicyHTTP 头部中指明的原始策略。

违例报告样本 我们假设页面位于example.com/signup.html。它使用如下策略,该策略禁止任何资源的加载,除了来自cdn.example.com的样式表。

html 复制代码
Content-Security-Policy: default-src 'none'; style-src cdn.example.com; report-uri /_/csp-reports

signup.html的HTML像这样:

html 复制代码
<!DOCTYPE html>
<html>
  <head>
    <title>Sign Up</title>
    <link rel="stylesheet" href="css/style.css">
  </head>
  <body>
    ... Content ...
  </body>
</html>

你能看出其中错误吗?样式表仅允许加载自cdn.example.com,然而该页面企图从自己的源 (example.com)加载。当该文档被访问时,一个兼容CSP的浏览器将以POST请求的形式发送违规报告到[http://example.co...%25E5%258A%25A0%25E8%25BD%25BD%25E3%2580%2582%25E5%25BD%2593%25E8%25AF%25A5%25E6%2596%2587%25E6%25A1%25A3%25E8%25A2%25AB%25E8%25AE%25BF%25E9%2597%25AE%25E6%2597%25B6%25EF%25BC%258C%25E4%25B8%2580%25E4%25B8%25AA%25E5%2585%25BC%25E5%25AE%25B9CSP%25E7%259A%2584%25E6%25B5%258F%25E8%25A7%2588%25E5%2599%25A8%25E5%25B0%2586%25E4%25BB%25A5POST%25E8%25AF%25B7%25E6%25B1%2582%25E7%259A%2584%25E5%25BD%25A2%25E5%25BC%258F%25E5%258F%2591%25E9%2580%2581%25E8%25BF%259D%25E8%25A7%2584%25E6%258A%25A5%25E5%2591%258A%25E5%2588%25B0%255Bhttp%3A%2F%2Fexample.com%2F_%2Fcsp-reports "http://example.com)%E5%8A%A0%E8%BD%BD%E3%80%82%E5%BD%93%E8%AF%A5%E6%96%87%E6%A1%A3%E8%A2%AB%E8%AE%BF%E9%97%AE%E6%97%B6%EF%BC%8C%E4%B8%80%E4%B8%AA%E5%85%BC%E5%AE%B9CSP%E7%9A%84%E6%B5%8F%E8%A7%88%E5%99%A8%E5%B0%86%E4%BB%A5POST%E8%AF%B7%E6%B1%82%E7%9A%84%E5%BD%A2%E5%BC%8F%E5%8F%91%E9%80%81%E8%BF%9D%E8%A7%84%E6%8A%A5%E5%91%8A%E5%88%B0%5Bhttp://example.com/_/csp-reports")](example.com/_/csp-repor...%25EF%25BC%258C%25E5%2586%2585%25E5%25AE%25B9%25E5%25A6%2582%25E4%25B8%258B%25EF%25BC%259A "https://example.com/_/csp-reports)%EF%BC%8C%E5%86%85%E5%AE%B9%E5%A6%82%E4%B8%8B%EF%BC%9A")

html 复制代码
{
  "csp-report": {
    "document-uri": "http://example.com/signup.html",
    "referrer": "",
    "blocked-uri": "http://example.com/css/style.css",
    "violated-directive": "style-src cdn.example.com",
    "original-policy": "default-src 'none'; style-src cdn.example.com; report-uri /_/csp-reports"
  }
}

如你所见,该报告在blocked-uri字段中包含了违规资源的完整路径 ,但情况并非总是如此。 比如,当signup.html试图从 anothercdn.example.com/stylesheet....加载CSS时,浏览器将不会包含完整路径,而只会保留源路径 (anothercdn.example.com)。%25E3%2580%2582 "http://anothercdn.example.com)%E3%80%82") CSP技术规范对此古怪行为给出了解释。大体上说,这样是为了防止泄露跨域资源的敏感信息。

利用 HttpOnly

很多 XSS 攻击脚本都是用来窃取Cookie, 而设置 Cookie 的 HttpOnly 属性后,JavaScript 便无法读取 Cookie 的值。这样也能很好的防范 XSS 攻击

第二章: 能不能说一说CSRF攻击?

什么是CSRF攻击?

CSRF(Cross-site request forgery), 即跨站请求伪造,指的是黑客诱导用户点击链接,打开黑客的网站,然后黑客利用用户目前的登录状态发起跨站请求。 举个例子, 你在某个论坛点击了黑客精心挑选的小姐姐图片,你点击后,进入了一个新的页面。那么恭喜你,被攻击了:) 你可能会比较好奇,怎么突然就被攻击了呢?接下来我们就来拆解一下当你点击了链接之后,黑客在背后做了哪些事情。 可能会做三样事情。列举如下:

自动发 GET 请求

黑客网页里面可能有一段这样的代码:

html 复制代码
<img src="https://xxx.com/info?user=hhh&count=100">

进入页面后自动发送 get 请求,值得注意的是,这个请求会自动带上关于 xxx.com 的 cookie 信息(这里是假定你已经在 xxx.com 中登录过)。 假如服务器端没有相应的验证机制,它可能认为发请求的是一个正常的用户,因为携带了相应的 cookie,然后进行相应的各种操作,可以是转账汇款以及其他的恶意操作。

自动发 POST 请求

黑客可能自己填了一个表单,写了一段自动提交的脚本。

html 复制代码
<form id='hacker-form' action="https://xxx.com/info" method="POST">
  <input type="hidden" name="user" value="hhh" />
  <input type="hidden" name="count" value="100" />
</form>
<script>document.getElementById('hacker-form').submit();</script>

同样也会携带相应的用户 cookie 信息,让服务器误以为是一个正常的用户在操作,让各种恶意的操作变为可能。

诱导点击发送 GET 请求

在黑客的网站上,可能会放上一个链接,驱使你来点击:

html 复制代码
<a href="https://xxx/info?user=hhh&count=100" taget="_blank">点击进入甜蜜恋爱世界</a>

点击后,自动发送 get 请求,接下来和自动发 GET 请求部分同理。 这就是CSRF攻击的原理。和XSS攻击对比,CSRF 攻击并不需要将恶意代码注入用户当前页面的html文档中,而是跳转到新的页面,利用服务器的 **验证漏洞 **和 **用户之前的登录状态 **来模拟用户进行操作。

防范措施

利用Cookie的SameSite属性

CSRF攻击中重要的一环就是自动发送目标站点下的 Cookie,然后就是这一份 Cookie 模拟了用户的身份。因此在Cookie上面下文章是防范的不二之选。 恰好,在 Cookie 当中有一个关键的字段,可以对请求中 Cookie 的携带作一些限制,这个字段就是SameSite。 SameSite可以设置为三个值,Strict、Lax和None。

  • Strict模式下 浏览器完全禁止第三方请求携带Cookie。比如请求baidu.com网站只能在baidu.com域名当中请求才能携带 Cookie,在其他网站请求都不能
  • Lax模式 就宽松一点了,但是只能在 get 方法提交表单况或者a 标签发送 get 请求的情况下可以携带 Cookie,其他情况均不能
  • None模式下 也就是默认模式,请求会自动携带上 Cookie

验证来源站点

这就需要要用到请求头中的两个字段: OriginReferer

  • Origin 只包含域名信息
  • Referer 包含了具体的 URL 路径

当然,这两者都是可以伪造的,通过 Ajax 中自定义请求头即可,安全性略差

使用验证码

关键操作页面加上验证码,后台收到请求后通过判断验证码可以防御CSRF。但这种方法对用户不太友好

在请求地址中添加token并验证

CSRF 攻击之所以能够成功,是因为黑客可以完全伪造用户的请求,该请求中所有的用户验证信息都是存在于cookie中,因此黑客可以在不知道这些验证信息的情况下直接利用用户自己的cookie 来通过安全验证。要抵御 CSRF,关键在于在请求中放入黑客所不能伪造的信息,并且该信息不存在于 cookie 之中。可以在 HTTP 请求中以参数的形式加入一个随机产生的 token,并在服务器端建立一个拦截器来验证这个 token,如果请求中没有token或者 token 内容不正确,则认为可能是 CSRF 攻击而拒绝该请求。这种方法要比检查 Referer 要安全一些,token 可以在用户登陆后产生并放于session之中,然后在每次请求时把token 从 session 中拿出,与请求中的 token 进行比对,但这种方法的难点在于如何把 token 以参数的形式加入请求。对于 GET 请求,token 将附在请求地址之后,这样 URL 就变成

html 复制代码
http://url?csrftoken=tokenvalue

而对于 POST 请求来说,要在 form 的最后加上

html 复制代码
 <input type="hidden" name="csrftoken" value="tokenvalue"/>

这样就把token以参数的形式加入请求了。

CSRF Token (在HTTP 头中自定义属性并验证)

这种方法也是使用 token 并进行验证,和上一种方法不同的是,这里并不是把 token 以参数的形式置于 HTTP 请求之中,而是把它放到 HTTP 头中自定义的属性里。通过 XMLHttpRequest 这个类,可以一次性给所有该类请求加上 csrftoken 这个 HTTP 头属性,并把 token 值放入其中。这样解决了上种方法在请求中加入 token 的不便,同时,通过 XMLHttpRequest 请求的地址不会被记录到浏览器的地址栏,也不用担心 token 会透过 Referer 泄露到其他网站中去 Django作为 Python 的一门后端框架,如果是用它开发过的同学就知道,在它的模板(template)中, 开发表单时,经常会附上这样一行代码:

html 复制代码
{% csrf_token %}

这就是 CSRF Token 的典型应用。那它的原理是怎样的呢? 首先,浏览器向服务器发送请求时,服务器生成一个字符串,将其植入到返回的页面中。 然后浏览器如果要发送请求,就必须带上这个字符串,然后服务器来验证是否合法,如果不合法则不予响应。这个字符串也就是CSRF Token,通常第三方站点无法拿到这个 token, 因此也就是被服务器给拒绝

第三章:什么是 DDoS 攻击

分布式网络攻击通常称为分布式拒绝服务 (DDoS) 攻击。这类攻击利用适用于任何网络资源(比如为公司网站提供支持的基础设施)的特定容量限制。DDoS 攻击会向受攻击的 web 资源发送大量请求,目的是超出该网站的多请求处理能力,从而阻止网站的正常运行。 DDoS 攻击的典型目标包括:

  • 互联网购物网站
  • 在线赌场
  • 任何依赖于提供在线服务开展业务的企业或组织

如何防范DDoS攻击?

  • **使用高带宽:**采用带宽大的服务器能够增加抗攻击能力,能够在遭受DDoS攻击时保有一定的流量冗余,避免网络的拥堵。
  • **高防CDN:**通过部署CDN,除了能够加速网络服务外,还能够隐藏源服务器IP地址,充当高防服务器使用,可以说使用CDN服务器是应对DDoS攻击的最有效手段之一。
  • **使用防火墙:**针对DDoS攻击和黑客入侵而设计的专业级防火墙通过对异常流量的清洗过滤,可对抗SYN/ACK攻击、TCP全连接攻击、刷脚本攻击等流量型DDoS攻击。
  • **安装最新的安全补丁:**大多数的DDoS攻击都是针对的特定软件或硬件漏洞,因此及时安装安全补丁可以有效降低被攻击的风险。
  • **禁用未使用的服务:**服务器运行的应用程序和服务越多,遭受DDoS攻击的可能性和造成的破坏越大。因此禁用所有不需要和未使用的服务和程序,可以提高网络的安全性。

第四章: xhr、ajax、axios、featch的使用详解和区别

:::tips JavaScript 表单、上传、下载类 :::

xhr

在Fetch出现之前我们发送异步请求默认都是通过ajax,底层使用了宿主环境的(XHR)XMLHTTPRequest 对象来实现异步请求。实现代码如下:

javascript 复制代码
var xhr = new XHMHttpRequest()
xhr.open('GET', url, true) //第三个参数为true(异步执行,不等待后台返回)。而为false(同步执行,等待返回后再执行下一步)
xhr.responseType = 'json'
xhr.onload = function () { console.log(xhr.response) }
xhr.setRequestHeader(header, value);
xhr.onreadystatechange = function () {
  if (xhr.readyState === 4 && xhr.stastus === 200) {
    console.log(xhr.responseText);
  }
}
xhr.onerror = function () { console.log() }
xhr.send()

// xhr.overrideMimeType("application/json") 重写mime类型 
// xhr.abort()  请求取消
// xhr.upload.onprogress 上传事件
// xhr.onprogress 下载事件
/// xhr.setRequestHeader(name, headers[name]); //设置请求头

为了方便,自己封装一个简易的ajax xhr 读取JSON文件的数据

jsx 复制代码
function readFileToJSON(filePath, callback) {
  var xhr = new XMLHttpRequest()
  xhr.overrideMimeType("application/json") //重写mime类型 
  xhr.open("GET", filePath, true)
  xhr.onreadystatechange = function () {
    if (xhr.readyState === 4 && xhr.status == "200") {
      callback(xhr.responseText)
    }
  }
  xhr.send(null)
}

// 调用readFileToJSON函数
readFileToJSON("./test.json", function (text) {
  var data = JSON.parse(text)
  console.log(data)
}) 
javascript 复制代码
<script>
	function $_ajax(request) {
		request.type = request.type.toUpperCase()
		var send_data = null, data_arr = []
		for (var key in request.data) { data_arr.push(key + '=' + request.data[key]) }
		var data_str = data_arr.join('&')
		var xhr = window.XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject('Microsoft.XMLHTTP')
		if (request.type === 'GET') {
			request.url += '?' + data_str
		}
		xhr.open(request.type, request.url)
		if (request.type === 'POST') {
			xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded')
			send_data = data_str
		}
		xhr.send(send_data)  //参数的形式是:"key=value&id=123"

		xhr.addEventListener('readystatechange', function () {
			if (this.readyState !== 4) return
			try {
				request.done(JSON.parse(this.responseText))
			} catch (e) {
				request.done(this.responseText)
			}
		})
	}
</script>

<script>
	var sayHi = new Function("a", "b", "alert(a + b)")
	sayHi("Hello", "Word")
	$_ajax({
		type: 'get',
		url: 'jsonp.php',
		data: {
			username: "张三",
			password: '123456789'
		},
		done: function (data) {
			console.log(data)
		}
	})
	$_ajax({
		type: 'Post',
		url: 'jsonp.php',
		data: {
			username: "张三",
			password: '123456789'
		},
		done: function (data) {
			console.log(data)
		}
	})
</script>

使用 Promise 的形式来解决回调地狱

javascript 复制代码
const getJSON = function (url) {
  return new Promise((resolve, reject) => {
    const xhr = XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject('Mscrosoft.XMLHttp');
    xhr.open('GET', url, false);
    xhr.setRequestHeader('Accept', 'application/json');
    xhr.onreadystatechange = function () {
      if (xhr.readyState !== 4) return;
      if (xhr.status === 200 || xhr.status === 304) {
        resolve(xhr.responseText);
      } else {
        reject(new Error(xhr.responseText));
      }
    }
    xhr.send();
  })
}

:::tips 基于 TypeSctipt封装的 XMLHttpRequest 网络请求
Learn-ts-axios-master.zip :::

jsonp

JSONP 核心原理:script 标签不受同源策略约束,所以可以用来进行跨域请求,优点是兼容性好,但是只能用于 GET 请求;

javascript 复制代码
const jsonp = ({ url, params, callbackName }) => {
  const generateUrl = () => {
    let dataSrc = ''
    for (let key in params) {
      if (params.hasOwnProperty(key)) {
        dataSrc += `${key}=${params[key]}&`
      }
    }
    dataSrc += `callback=${callbackName}`
    return `${url}?${dataSrc}`
  }
  return new Promise((resolve, reject) => {
    const scriptEle = document.createElement('script')
    scriptEle.src = generateUrl()
    document.body.appendChild(scriptEle)
    window[callbackName] = data => {
      resolve(data)
      document.removeChild(scriptEle)
    }
  })
}

ajax

当然我们一般会用一些封装过的ajax实现解决传统方式的繁琐写法以及xhr低版本浏览器可能出现的兼容问题, 比如jquery的ajax: :::tips ajax都有哪些优点和缺点? 优点:

  • ajax技术的目的是让javascript发送http请求,与后台通信,获取数据和信息
  • ajax技术的原理是实例化xmlhttp对象与后台通信,调用了异步HTTP请求线程
  • ajax通信的过程不会影响后续javascript的执行,从而实现异步,ajax是局部刷新,从而不会影响页面加载抖动

缺点:

  • 本身是针对MVC的编程,不符合现在前端MVVM的浪潮
  • 基于原生的XHR开发,XHR本身的架构不清晰,已经有了fetch的替代方案
  • JQuery整个项目太大,单纯使用ajax却要引入整个JQuery非常的不合理(采取个性化打包的方案又不能享受CDN服务)

同步和异步? 同步: 客户端必须等待服务器端的响应。在等待的期间客户端不能做其他操作。做完一件事后再做另外一件事 异步: 客户端不需要等待服务器端的响应。在服务器处理请求的过程中,客户端可以进行其他的操作,同时做几件事情

什么是同源策略? ajax请求的页面或资源只能是同一个域下面的资源,不能是其他域的资源 这是在设计ajax时基于安全的考虑。特征报错提示: XMLHttpRequest cannot load www.baidu.com/. No

'Access-Control-Allow-Origin' header is present on the requested resource.

Origin 'null' is therefore not allowed access. :::

javascript 复制代码
url         请求地址
type        请求方式,默认是'GET',常用的还有'POST'
dataType    设置返回的数据格式,常用的是'json'格式,也可以设置为'html',或者jsonp
data        设置发送给服务器的数据
contentType 指明向后端发送的数据格式,通常为contentType:"application/json" 
success     设置请求成功后的回调函数
error       设置请求失败后的回调函数
cache       是否使用缓存
async       设置是否异步,默认值是'true',表示异步
headers     设置一个请求头对象
javascript 复制代码
$.ajaxSettings.async = false             // 设置同步
$.ajax({
  url: "/delete_book",                   // 请求的后端url
  type: "post",                          // 请求方式 get|post|put|delete
  dataType: "json",                      // 指明后端返回的数据格式
  cache:false,                           // 禁止用缓存
  data: JSON.stringify({ name: "张三" }), // 向后端发送的请求体数据
  contentType: "application/json",       // 指明向后端发送的数据格式
  headers: {                             // 请求头
    "X-CSRFToken": getCookie("csrf_token")
  },
  success: function (res) {
    if (res.code == 0) {
      location.href = "/";
      location.reload()
    }
  }
})
$.ajaxSettings.async = true  //设置异步





//类似Promise写法
$.ajax({
  url: '/api/v1/users',
  type: 'post',
  data: JSON.stringify({ name: "张三" }), //转json数据
  dataType: 'json',
  contentType: 'application/json',
  headers: {
    "X-CSRFToken": getCookie("csrf_token") // 请求头,将csrf_token值放到请求中,方便后端csrf进行验证
  }
})
.then(function (res) { })



$.ajaxSettings.async = false  //设置同步
$.post('/cart/update', params).then(function (data) {
  if (data.res == 5) {
    total_count = data.total_count
    sussess_update = true
  }
  else {
    alert(data.errmsg)
    sussess_update = false
  }
  $.ajaxSettings.async = true  //设置异步
})

axios

是一个基于promise网络请求库,作用于node.js和浏览器中,它是 isomorphic 的(即同一套代码可以运行在浏览器和node.js中)

  • 在服务端它使用原生node.js的http模块
  • 而在客户端 (浏览端) 则使用XMLHttpRequests
javascript 复制代码
axios({
  method: 'post',
  url: '/user/12345',
  data: {
    firstName: 'Fred',
    lastName: 'Flintstone'
  }
})
  .then(function (response) {
    console.log(response)
  })
  .catch(function (error) {
    console.log(error)
  })
  • 从 node.js 创建 http 请求
  • 从浏览器中创建 XMLHttpRequest
  • 支持 Promise API
  • 拦截请求和响应
  • 转换请求和响应数据
  • 取消请求
  • 自动转换JSON数据
  • 客户端支持防止CSRF
  • 提供了一些并发请求的接口(重要,方便了很多的操作)

fetch

符合关注分离,没有将输入、输出和用事件来跟踪的状态混杂在一个对象里 更好更方便的写法更加底层,提供的API丰富(request, response)脱离了XHR,是ES规范里新的实现方式

javascript 复制代码
fetch(url, {
  body: JSON.stringify(data), // must match 'Content-Type' header
  cache: 'no-cache',          // *default, no-cache, reload, force-cache, only-if-cached
  credentials: 'same-origin', // include, same-origin, *omit
  headers: {
    'user-agent': 'Mozilla/4.0 MDN Example',
    'content-type': 'application/json'
  },
  method: 'POST',          // *GET, POST, PUT, DELETE, etc.
  mode: 'cors',            // no-cors, cors, *same-origin
  redirect: 'follow',      // manual, *follow, error
  referrer: 'no-referrer', // *client, no-referrer
})
  • fetch的语法简洁,更语义化
  • 基于promise,支持async/await
  • 同构方便,使用isomorphic-fetch
  • Fetch API更加现代
  • Fetch API更底层
  • Fetch API更接近未来
  • 缺点
  • fetch只针对网络错误报错,http状态码错误不报错,对400,500都当做成功的请求,需要封装去处理
  • fetch不支持abort,无法终止
  • fetch不支持超时控制,使用setTimeout和Promise.reject实现的超时控制不能阻止请求过程继续在后台运行,造成了流量的浪费
  • fetch没有原生检测请求进度的方式,XHR可以
  • 默认情况下fetch不发送cookie,除非手动配置,
  • credentials: 'include' // 默认请求是否带上cookie
  • credentials: 'same-origin' //同源的情况下带cookie
  • credentials: 'omit' // 忽略cookie

fetch API更加现代 XHR 和 Fetch API 最显著的区别就是调用方式不同。这一点大家应该都知道吧。 举个例子,下面两端代码完成的是同一功能:

javascript 复制代码
// 用 XHR 发起一个GET请求
var xhr = new XHMHttpRequest()
xhr.open('GET', url, true) //第三个参数为true(异步执行,不等待后台返回)。而为false(同步执行,等待返回后再执行下一步)
xhr.responseType = 'json'
xhr.onload = function () { console.log(xhr.response) }
xhr.setRequestHeader(header, value);
xhr.onreadystatechange = function () {
  if (xhr.readyState === 4 && xhr.stastus === 200) {
    console.log(xhr.responseText);
  }
}
xhr.onerror = function () { console.log() }
xhr.send()

// 用 Fetch 完成同样的请求
fetch(url).then(function (response) {
  return response.json()
}).then(function (jsonData) {
  console.log(jsonData)
}).catch(function () {
  console.log()
})

fetch API更底层 其实,刚才说到的 Fetch API 并不是指仅仅一个 fetch 方法,还包括 Request、 Response、Headers、Body都一系列原生对象。对于传统的XHR而言,你必须使用它的一个实例来发出请求和处理响应。 但是通过Fetch API,我们还能够通过刚才提到的原生对象,明确的配置请求和响应。这些底层的抽象让 Fetch API 比 XHR 更灵活。 举个例子,现在要下载一个很大的 utf-8 格式的 txt 文件,我们通过流式的响应体而不是文本的形式读取,最后显示在一个div中

javascript 复制代码
document.addEventListener('DOMContentLoaded', function (e) {
  var url = 'test.txt'
  var div = document.getElementById('content')

  var progress = 0
  var contentLength = 0

  fetch(url).then(function (response) {
    // 通过响应头获取文件大小
    contentLength = response.headers.get('Content-Length')

    var pump = function (reader) {
      return reader.read().then(function (result) {
        // 如果流中的内容读取完毕,result.done的值会变为true
        if (!result.done) {
          // 获取流中的数据
          var chunk = result.value

          var text = ''
          // 流中的数据是一串字节码,需要做转码
          for (var i = 3; i < chunk.byteLength; i++) {
            text += String.fromCharCode(chunk[i])
          }

          // 添加到页面的div中
          div.innerHTML += text

          // 还可以用流的长度显示当前进度
          progress += chunk.byteLength
          console.log(((progress / contentLength) * 100) + '%')

          // 开始读取下一个流
          return pump(reader)
        }
      })
    }

    // 开始读取流中的信息
    return pump(response.body.getReader())
  }).catch(function (error) {
      console.log(error)
    })
})

在上面的例子中,我们不止使用了流来下载文件,还通过响应头获取了响应的具体信息,显示了下载的进度。虽然使用XHR也能做到使用流来读取文件,不过现在应该只有IE浏览器支持。但是 Fetch API 提供了访问数据的实际字节的方法,而 XHR 的 responseText 只有文本形式,这意味着在某些场景下它的作用可能非常有限。

fetch API更接近未来 当我们在谈论 Fetch API 时,我们在谈论的不止是这些已经胜过 XHR 的地方,更是在谈论 Fetch API未来的可能性。比如未来基于 Fetch 和 Service Worker 的缓存和请求拦截技术。

参考文献

:::info 网络篇 :::

相关推荐
一颗花生米。3 小时前
深入理解JavaScript 的原型继承
java·开发语言·javascript·原型模式
学习使我快乐013 小时前
JS进阶 3——深入面向对象、原型
开发语言·前端·javascript
bobostudio19953 小时前
TypeScript 设计模式之【策略模式】
前端·javascript·设计模式·typescript·策略模式
勿语&4 小时前
Element-UI Plus 暗黑主题切换及自定义主题色
开发语言·javascript·ui
黄尚圈圈4 小时前
Vue 中引入 ECharts 的详细步骤与示例
前端·vue.js·echarts
浮华似水5 小时前
简洁之道 - React Hook Form
前端
正小安7 小时前
如何在微信小程序中实现分包加载和预下载
前端·微信小程序·小程序
小飞猪Jay9 小时前
C++面试速通宝典——13
jvm·c++·面试
_.Switch9 小时前
Python Web 应用中的 API 网关集成与优化
开发语言·前端·后端·python·架构·log4j