最近忙着面试,在面试过程中发现了自己怎么突然对ajax这些http请求有点生疏,赶紧补一课,记录一下。
一:XMLHttpRequest
什么是Ajax? 这是一个可以向服务器请求额外的数据而不需要卸载页面的技术。
Ajax的核心内容就是XMLHttpRequest对象。通过XMLHttpRequest发送一次请求需要哪些步骤?
var xhr = new XMLHttpRequest(); // new 一个XMLHttpRequest对象用于创建连接使用
xhr.open(type, url, boolean);
type: 请求的方法get/ post/ put ...
url: 请求的地址
boolean: 布尔值,true为异步, false为同步。
在open方法中,只是等待建立的过程,并没有发送请求。
xhr.send(data); // 开始请求,send接受内容。如果不需要传值设置为null
xhr.readyState属性:用来判断请求进行的步骤
xhr.readyState === 0 // 尚未调用open
xhr.readyState === 1 // open调用, send没有
xhr.readyState === 2 // send调用,没有收到响应
xhr.readyState === 3 // 收到响应,响应没有完全
xhr.readyState === 4 // 响应完全
只要readyState的属性变化,就会触发readystatechange事件.
xhr.status属性: http状态码。 200 成功, 302协商缓存等。
ok.手写一个简单的ajax:
function myAjax (type, url, data) {
var xhr = XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject();
let newData = '';
// 处理一下data
if (typeof data === 'Object') {
let str = '';
for (let key in data) {
if (Object.prototype.hasOwnProperty.call(data, key)) {
str += `${key}=${data[key}&`;
}
};
newData = str.slice(0, str.length - 1);
}
if(type.toUpperCase() == 'GET') {
if (newData) {
xhr.open("GET", `${url}?${newData}`, true);
} else {
xhr.open("GET", url, ture);
};
xhr.send(null);
}
else if (type.toUpperCase() == 'POST') {
xhr.open("POST", url, true);
xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
if (data) {
xhr.send(data);
} else {
xhr.sennd(null);
}
};
xhr.onreadyStatechange = () => {
if (xhr.reayState === 4 && xhr.status === 200) {
const request = xhr.responseText;
} else {
// error 处理
}
}
简单的实现了一下post和get方法。
需要判断一下浏览器,调用不同的创建xhr方法,在ie下可以使用ActiveXObject()方法.
由于get请求需要把参数拼接到url上,这里需要遍历一下data中的key,然后拿到value,拼接到url上。
在post请求中不需要,直接可以通过send方法传data。
在onreadystatechange方法中通过xhr.status和xhr.readystate 判断结果。
二: XHR的其他属性介绍
鉴于 XHR 已经得到广泛接受,成为了事实标准,W3C 也着手制定相应的标准以规范其行为。
XMLHttpRequest 1 级只是把已有的 XHR 对象的实现细节描述了出来。而 XMLHttpRequest 2 级则进一步发展了 XHR。
(1): formData类型
var form = document.getElementById("user-info");
xhr.send(new FormData(form));
使用 FormData 的方便之处体现在不必明确地在 XHR 对象上设置请求头部。XHR 对象能够识别传
入的数据类型是 FormData 的实例,并配置适当的头部信息。
(2): 超时设定
timeout: 设置请求超时的时间,如果超过这个时间就会停止请求,触发ontimeout方法
xhr.timeout = 1000;
xhr.ontimeout = () => {
alert('error');
}
(3): 其他
1. 进度事件:
这些事件最早其实只针对 XHR 操作,但目前也被其他 API 借鉴。有以下 6 个进度事件。
loadstart // 接受数据第一个字节开始触发
progress // 在接收响应期间持续不断地触发
error // 发生错误时触发
abort // 调用about方法触发
load // 在接收到完整的响应数据时触发
loadend // 在通信完成或者触发 error、abort 或 load 事件后触发
三: CORS
通过 XHR 实现 Ajax 通信的一个主要限制,来源于跨域安全策略。默认情况下,XHR 对象只能访问与包含它的页面位于同一个域中的资源。这种安全策略可以预防某些恶意行为。但是,实现合理的跨域请求对开发某些浏览器应用程序也是至关重要的。
1:XDR
XDR其实和XHR差不多,就是在设计的时候考虑到了安全性问题。
xdr:不会携带cookies,头部只能设置content-type字段, 不能访问头部信息, 只能支持get和post。
XDR 对象的安全机制部分实现了 W3C 的 CORS 规范。
2:简单请求和非简单请求
什么是简单请求?
请求方法只能是post和get,请求头部:
content-type: application/x-www-form-urlencoded / text/plain / multipart/form-data
除了简单请求其他的都是非简单请求。
非简单请求的cors请求,是发送请求之后先在服务端 "预检" 一遍,预检发送的请求方法是 OPTIONS ,看看服务端是否允许跨域。
非简单请求的步骤:
cors 有一个预检测(向服务器发送一个Prefight请求)就是options预检
origin: '' // 域名来源
Access-Control-Request-Methods: '' // 自身请求的方法
Acdess-Control-Request-Headers: '' // 可选 定义头部
服务器端决定是否允许请求:
*** 通过一下响应字段返回给浏览器
* Access-Control-Allow-Origin: ''
* Access-Control-Allow-Methods: 允许请求的方法
* Access-Control-Allow-Headers: 允许请求头部
* Access-Control-Max-Age: 请求缓存多长时间
3: 带凭证的请求
默认情况下,跨源请求不提供凭据(cookie、HTTP 认证及客户端 SSL 证明 等 )。
withCredentials:true 可以指定某个请求应该发送凭据
服务器端: 会响应一下http头部:
Access-Control-Allow-Credentials: true
四: 其他跨域技术
1: img标签
图像 Ping 是与服务器进行简单、单向的跨域通信的一种方式。请求的数据是通过查询字符串形式发送的,而响应可以是任意内容,但通常是像素图或 204 响应。通过图像 Ping,浏览器得不到任何具体的数据,但通过侦听 load 和 error 事件,它能知道响应是什么时候接收到的。
let img = new Image();
img.onload = () => {
console.log('加载成功');
}
img.onerror = ()=> {
console.log('加载失败');
}
经常容易忘!
2: JSONP
什么是jsonp?
jsonp是包含在函数中的json 例如: callback(json);
一个常见的jsonp请求的格式:
http://mroal.zip/json/callback=handleFunc
原理就是script标签不受同源协议限制,在script标签上url加入jsonp的地址.
实现一个jsonp:
const handleFunc = (url, params, callBackNA) {
// 处理params
const getUrl = () => {
let dataSrc = '';
for (let key in params) {
if (Object.prototype.hasOwnProperty.call(params, key)) {
dataSrc += `${key}=${params[key]}&`;
}
};
dataSrc += `callback=${callBackNA}`;
return `${url}?${dataSrc}`;
};
return new Promise((resolve, reject) => {
const scriptEle = document.createElement('script');
scriptEle.src = getUrl();
document.body.appendChild(scriptEle);
window[callBackNA] = data => {
resolve(data);
document.removeChild(scriptEle);
}
}
通过promise来实现异步。初始化的时候定义一个函数处理params,然后动态创建一个script标签,把url赋值给创建新的script的url上。最后在调用callBackNA中移除标签。同时获取data,传入resolve中。
大概就到这里了,其实还有其他的一些跨域的解决方法。比如nignx代理。这里就不多说了,主要是介绍一下jsonp和ajax。