WEBAPI-AJAX阶段

175 阅读5分钟

AJAX即“Asynchronous JavaScript and XML”,javascript异步处理网络请求是现在更好理解的说法。

XMLHttpRequest XHR

XMLHttpRequest 运行步骤

  1. 创建XHLHttpRequest
  2. 使用xhr.open()初始化该xhr
  3. xhr.send()发送请求
  4. 监听xhr事件,通常是onload,onerror,onprogress
xhr发起get/post请求的一段代码
// 1. 创建一个 new XMLHttpRequest 对象
let xhr = new XMLHttpRequest();

// 2. 配置它:从 URL /article/.../load GET-request
xhr.open('GET', '/article/xmlhttprequest/example/load');

// 3. 通过网络发送请求
xhr.send();

// 4. 当接收到响应后,将调用此函数
xhr.onload = function() {
  if (xhr.status != 200) { // 分析响应的 HTTP 状态
    alert(`Error ${xhr.status}: ${xhr.statusText}`); // 例如 404: Not Found
  } else { // 显示结果
    alert(`Done, got ${xhr.response.length} bytes`); // response 是服务器响应
  }
};

xhr.onprogress = function(event) {
  if (event.lengthComputable) {
    alert(`Received ${event.loaded} of ${event.total} bytes`);
  } else {
    alert(`Received ${event.loaded} bytes`); // 没有 Content-Length
  }

};

xhr.onerror = function() {
  alert("Request failed");
};

属性

xhr.status HTTP 状态码(一个数字):200,404,403 等,如果出现非 HTTP 错误,则为 0。

xhr.statusText HTTP 状态消息(一个字符串):状态码为 200 对应于 OK,404 对应于 Not Found,403 对应于 Forbidden。

xhr.response(旧脚本可能用的是 responseText)服务器的 response body。

xhr.timeout 超时时间,当在给定timeout时间中未请求成功将触发timeout事件。

xhr.readyState XMLHttpRequest 的状态(state)会随着它的处理进度变化而变化。可以通过 xhr.readyState 来了解当前状态。

XMLHttpRequest 对象以 0 → 1 → 2 → 3 → … → 3 → 4 的顺序在它们之间转变。每当通过网络接收到一个数据包,就会重复一次状态 3。
UNSENT = 0; // 初始状态
OPENED = 1; // open 被调用
HEADERS_RECEIVED = 2; // 接收到 response header
LOADING = 3; // 响应正在被加载(接收到一个数据包)
DONE = 4; // 请求完成

可以使用onreadystatechange来监听,现在被onload等替代
xhr.onreadystatechange = function() {
  if (xhr.readyState == 3) {
    // 加载中
  }
  if (xhr.readyState == 4) {
    // 请求完成
  }
};

方法

xhr.open(method, URL, [async, user, password])

method —— HTTP 方法。通常是 "GET" 或 "POST"。
URL —— 要请求的 URL,通常是一个字符串,也可以是 URL 对象。
async —— 默认为true,如果显式地设置为 false,那么请求将会以同步的方式处理。同步处理xhr请求可能导致页面卡死,具体样子类似在代码段中间使用alert函数。
user,password —— HTTP 基本身份验证(如果需要的话)的登录名和密码。

xhr.send([body]) 该参数用于post传递request body

xhr.abort() 中断请求,触发abort事件,xhr.status变为0

http-header:

**setRequestHeader(name, value) ** 设置头部

getResponseHeader(name) 获取具有给定 name 的 header(Set-Cookie 和 Set-Cookie2 除外)。

getAllResponseHeaders() 返回除 Set-Cookie 和 Set-Cookie2 外的所有 response header。

POST: 使用js内建对象FormData或者使用json,来构造post的request body

<form name="person">
  <input name="name" value="John">
  <input name="surname" value="Smith">
</form>

<script>
  // 从表单预填充 FormData
  let formData = new FormData(document.forms.person);

  // 附加一个字段
  formData.append("middle", "Lee");

  // 将其发送出去
  let xhr = new XMLHttpRequest();
  xhr.open("POST", "/article/xmlhttprequest/post/user");
  xhr.send(formData);

  xhr.onload = () => alert(xhr.response);
</script>
/**********************************************/
let xhr = new XMLHttpRequest();

let json = JSON.stringify({
  name: "John",
  surname: "Smith"
});

xhr.open("POST", '/submit')
xhr.setRequestHeader('Content-type', 'application/json; charset=utf-8');

xhr.send(json);

事件

按生命周期排序:

  • loadstart —— 请求开始。
  • progress —— 一个响应数据包到达,此时整个 response body 都在 response 中。
  • abort —— 调用 xhr.abort() 取消了请求。
  • error —— 发生连接错误,例如,域错误。不会发生诸如 404 这类的 HTTP 错误。
  • load —— 请求成功完成。
  • timeout —— 由于请求超时而取消了该请求(仅发生在设置了 timeout 的情况下)。
  • loadend —— 在 load,error,timeout 或 abort 之后触发。
  • 旧 readystatechange error,abort,timeout 和 load 事件是互斥的。其中只有一种可能发生。

上传进度 xhr.upload对象

跨域:同源策略

同源:是指 url 的 域名、协议、端口 相同。

同源策略,有以下两种:

  1. DOM 同源策略:禁止对不同源页面 DOM 进行操作。这里主要场景是 iframe 跨域的情况,不同域名的 iframe 是限制互相访问的。
  2. XMLHttpRequest 同源策略:禁止使用 XHR 对象向不同源的服务器地址发起 HTTP 请求。

为什么要针对这两种进行防范? 1.

如果没有 DOM 同源策略,也就是说不同域的 iframe 之间可以相互访问,那么黑客可以这样进行攻击:

做一个假网站,里面用 iframe 嵌套一个银行网站 http://mybank.com。iframe 宽高啥的调整到页面全部,这样用户进来除了域名,别的部分和银行的网站没有任何差别。
这时如果用户输入账号密码,我们的主网站可以跨域访问到 http://mybank.com 的 dom 节点,就可以拿到用户的账户密码了。
如果没有 XMLHttpRequest 同源策略,那么黑客可以进行 CSRF(跨站请求伪造) 攻击:

用户登录了自己的银行页面 http://mybank.com,http://mybank.com 向用户的 cookie 中添加用户标识。
用户浏览了恶意页面 http://evil.com,执行了页面中的恶意 AJAX 请求代码。
http://evil.com 向 http://mybank.com 发起 AJAX HTTP 请求,请求会默认把 http://mybank.com 对应 cookie 也同时发送过去。
银行页面从发送的 cookie 中提取用户标识,验证用户无误,response 中返回请求数据。此时数据就泄露了。
而且由于 Ajax 在后台执行,用户无法感知这一过程。

跨域方法

  1. cors 跨域资源共享(主要在服务器端进行配置),缺点是兼容性不好

  2. 跨域方法jsonp(JSON with Padding)

其中jsonp是由前端来进行操作的,jsonp原理:加载图片 css js 可无视同源策略

根据这一原理,可以构造一个img/js的dom,使其src指向外域,从而根据响应进行操作。

  • img.src可以用于统计打点,可使用第三方统计服务(统计多少人访问了本网页),由于这是浏览器至服务器单向的,所以达到请求数据。

  • 通过script.src可实现访问外域,并获得返回数据。

<img src="外域url"> 通过发起向第三方统计打点网站的请求,可以记录访问网站的人次

<script src="外域url">
譬如
// 1. 定义一个 回调函数 handleResponse 用来接收返回的数据
function handleResponse(data) {
    console.log(data);
};

// 2. 动态创建一个 script 标签,并且告诉后端回调函数名叫 handleResponse
var body = document.getElementsByTagName('body')[0];
var script = document.gerElement('script');
script.src = 'http://www.laixiangran.cn/json?callback=handleResponse';
body.appendChild(script);

// 3. 通过 script.src 请求 `http://www.laixiangran.cn/json?callback=handleResponse`,
// 4. 后端能够识别这样的 URL 格式并处理该请求,然后返回 handleResponse({"name": "laixiangran"}) 给浏览器
// 5. 浏览器在接收到 handleResponse({"name": "laixiangran"}) 之后立即执行 ,也就是执行 handleResponse 方法,获得后端返回的数据,这样就完成一次跨域请求了。