Ajax、JSON 及跨域请求

作者:user 发布日期: 浏览量:17

一、Ajax

1、初识 Ajax

Ajax 是什么:
- Ajax 是 Asynchronus JavaScript and XML(异步 JavaScript 和 XML)的简写
- Ajax 其实就是浏览器与服务器之间的一种异步通信方式
- Ajax 中的异步:可以异步的向服务器发送请求,在等待响应的过程中,不会阻塞当前页面,浏览器可以做自己的事情;直到成功获取响应后,浏览器才开始处理响应数据
- XML(可扩展标记语言)是前后端数据通信时的一种数据传输格式(现在主流时 JSON)

2、Ajax 的基本用法

XMLHttpRequest:
- Ajax 想要实现浏览器与服务器之间的异步通信,需要依靠 XMLHttpRequest 创建一个对象,XMLHttpRequest 是一个构造函数

readyState 的值:
- xhr.readyState 这个状态的变化值从 0~4 表示 5 种状态
- 0:请求还未初始化。尚未调用 open()方法
- 1:启动(请求已建立)。已经调用 open()方法,但未调用 send() 方法
- 2:请求已发送。已经调用 send() 方法,但未接收到响应
- 3:接收。开始接收响应数据
- 4:接收完成。已经全部接收完响应数据,可以在浏览器中使用了

status 的值:
- 1xx:收到请求
100:客户必须继续发送请求
101:客户要求服务器根据请求转换HTTP协议版本
- 2xx:表示成功处理请求,如:200
200:成功处理请求
201:提示知道新文件的URL
202:接受和处理、但处理未完成
203:返回信息不确定或不完整
204:请求收到,但返回信息为空
205:服务器完成了请求,用户代理必须复位当前已经浏览过的文件
206:服务器已经完成了部分用户的GET请求
- 3xx:需要重定向,浏览器直接跳转
300:请求的资源可在多处得到
301:永远重定向到该资源
302:临时重定向到该资源
303:建议客户访问其他URL或访问方式
304:资源未改变,如果访问的资源没有改变,服务器返回304,浏览器使用缓存的资源
305——请求的资源必须从服务器指定的地址得到
306——前一版本HTTP中使用的代码,现行版本中不再使用
307——申明请求的资源临时性删除
- 4xx:客户端请求错误
400:错误请求,如语法错误
401:请求授权失败
402:保留有效ChargeTo头响应
403:客户端没有权限
404:请求地址错误或者服务的没有对应接口
- 5xx:服务端错误
500:服务器产生内部错误
501:服务器不支持请求的函数

Ajax 的使用步骤:
- 创建 XMLHttpRequest 的对象

    let xhr = new XMLHttpRequest();

    // 一般使用下面这种方式创建
    let request;
    if (window.XMLHttpRequest) {
        // 这种方式是对于使用这几种浏览器的情况
        // IE7+,Firefox,Chrome,Opera,Safari ...
        request = new XMLHttpRequest(); 
    } else {
        // 这种方式是针对低版本浏览器:IE6,IE5
        request = new ActiveObject("Microsoft.XMLHTTP");
    }
  • 准备发送请求
    // method 是 HTTP 请求方法:GET、POST、PUT、DELETE
    // URL 地址:
    // async 是否使用异步:true 或者 false
    xhr.open(method, url, async);
  • 监听事件,处理响应
    // 当获取到响应后,会触发 xhr 对象的 readystatechange 事件,
    // 可以在该事件中对响应进行处理
    xhr.onreadystatechange = function() {
        // readyState 这个状态的变化值从 0~4 表示 5 种状态
        // 0:请求还未初始化。尚未调用 open()方法
        // 1:启动(请求已建立)。已经调用 open()方法,但未调用 send() 方法
        // 2:请求已发送。已经调用 send() 方法,但未接收到响应
        // 3:接收。开始接收响应数据
        // 4:接收完成。已经全部接收完响应数据,可以在浏览器中使用了
        // status == 200 表示请求成功
        if (xhr.readyState == 4 && xhr.status == 200) {
            //将返回结果以文本(字符串)形式输出
            document.write(xhr.responseText);
            //将返回结果以XML形式输出
            //docunment.write(xhr.responseXML);
            // or lists = xhr.responseText
        }
    }
  • 发送请求:调用 send() 方法正式发送请求
    // send() 的参数是通过请求体携带的数据
    // 如果是 GET 请求,括号里填 null 或者不填
    xhr.send();

3、GET 请求

携带数据:
- GET 请求不能通过请求体携带数据,但可以通过请求头携带
- 在请求头的 url 后加“?属性=值”的方式添加数据
- 多个“属性=值”之间用“&”分隔

数据编码:
- 如果携带的数据是非英文字母的话,比如汉字,就需要编码后再发送给后端,不然会造成乱码问题
- 可以使用 encodeURIComponent() 编码
- 示例:url = http://localhost:8080/test?username = ${encodeURIComponent("名字") &age=18}

4、POST 请求

携带数据:
- 如果想发送数据,直接将参数写在 send() 方法括号里,一般是字符串
- 不能直接传递对象,需要先将对象转换成字符串的形式

数据编码:
- 可以使用 encodeURIComponent() 编码
- 示例:xhr.send(username=${encodeURIComponent("名字")} & age=18);

5、XHR

(1)XHR 的属性

        if (xhr.readyState == 4 && xhr.status == 200) {
            //将返回结果以文本(字符串)形式输出
            document.write(xhr.responseText);
            //将返回结果以XML形式输出
            //docunment.write(xhr.responseXML);
        }

responseType 和 response 属性:
- 上面 Ajax 响应中的 responseText 是文本形式的响应内容(或字符串形式)
- 当上面的 responseText 变为 response 时,可以在 xhr.open() 方法后面通过 xhr.responseType 设置具体的响应类型
- IE6~9不支持,IE10 才开始支持

    xhr.open(method,url,async);
    // 默认是空字符串
    // xhr.responseType = '';

    // 当设置为‘text’或空字符串时等效于上面设置的 responseText
    xhr.responseType = 'text';

    // 响应将以 JSON 形式输出
    xhr.responseType = 'json';

timeout 属性:
- 用来设置请求的超时时间(单位:ms)
- 当上面的 responseText 变为 response 时,可以在 xhr.open() 方法后面通过 xhr.timeout 设置响应超时时间
- 当超时后会自动触发 timeout() 事件
- IE6~7不支持,IE8 才开始支持

   xhr.open(method,url,async);

   // 设置响应超时时间
   xhr.timeout = 10000;

withCredentials 属性:
- 指定使用 Ajax 发送请求时是否携带 Cookie
- 使用 Ajax 发送请求,默认情况下,同域时,会携带 Cookie;跨域时,不会
- 同样的,当上面的 responseText 变为 response 时,可以在 xhr.open() 方法后面通过 xhr.withCredentials 设置是否携带 Cookie
- 最终能否成功跨域携带 Cookie,还要看服务器是否同意,若服务器同意,设置 Access-Control-Allow-Origin 时不能使用通配符 *,必须指定具体域名
- IE6~9不支持,IE10 才开始支持

   xhr.open(method,url,async);

   // 设置是否携带 Cookie  true or false
   xhr.withCredentials = true;

(2)XHR 的方法

abort() 方法:
- abort() 方法用于终止当前请求
- 一般配合 abort 同名事件一起使用
- 该方法在 xhr.send() 方法后面调用

   xhr.send();

   // 设置终止当前请求
   xhr.abort();

setRequestHeader() 方法:
- setRequestHeader() 方法用于设置请求头信息
- 该方法在 xhr.open() 方法后面调用

   // 此方法针对 POST 请求
   xhr.open('POST', url, true);

   // 设置请求头信息
   // 请求头中的 Content-Type 字段用来告诉服务器,浏览器发送什么格式的数据
   xhr.setRequestHeader(Content-Type, 'application/x-www-form-urlencoded');
   // application/x-www-form-urlencoded 对应如下 表单 数据格式
   xhr.send('username = alex&age=18');

   // application/json 对应如下 json 数据格式
   xhr.send(
       JSON.stringify({
           username = alex,
           age=18
       })
   );

(3)XHR 的事件

load 事件:
- 响应数据可用时触发
- 可用 onload 事件替换 onreadystatechange 事件
- IE6~8 不支持,IE9 及以上版本才支持 onload 事件

    // 响应数据可用时 readyState 已经等于4
    // xhr.onload = function() {

        // if (xhr.readyState == 4 && xhr.status == 200) {
            //将返回结果以文本(字符串)形式输出
            // document.write(xhr.responseText);
            //将返回结果以XML形式输出
            //docunment.write(xhr.responseXML);
        // }
    // }

    // load 事件配上 addEventListener
    xhr.addEventListener('load', ()=>{
        if (xhr.readyState == 4 && xhr.status == 200) {
            //将返回结果以文本(字符串)形式输出
            document.write(xhr.responseText);
            //将返回结果以XML形式输出
            //docunment.write(xhr.responseXML);
        }
    }, false); 

error 事件:
- 请求发生错误时触发(是请求时错误,要与响应时错误区分开)
- IE10 及以上版本才支持

    // load 事件配上 addEventListener
    xhr.addEventListener('load', ()=>{
        if (xhr.readyState == 4 && xhr.status == 200) {
            //将返回结果以文本(字符串)形式输出
            document.write(xhr.responseText);
            //将返回结果以XML形式输出
            //docunment.write(xhr.responseXML);
        }
    }, false); 
    xhr.addEventListener('error', ()=>{
        console.log('error');
    });

abort 事件:
- 调用 abort() 方法终止请求时触发
- IE10 及以上版本才支持

    // load 事件配上 addEventListener
    xhr.addEventListener('load', ()=>{
        if (xhr.readyState == 4 && xhr.status == 200) {
            //将返回结果以文本(字符串)形式输出
            document.write(xhr.responseText);
            //将返回结果以XML形式输出
            //docunment.write(xhr.responseXML);
        }
    }, false); 
    xhr.addEventListener('abort', ()=>{
        console.log('触发事件后想执行的逻辑');
    });

timeout 事件:
- 请求超时后触发
- IE8 及以上版本才支持

    // load 事件配上 addEventListener
    xhr.addEventListener('load', ()=>{
        if (xhr.readyState == 4 && xhr.status == 200) {
            //将返回结果以文本(字符串)形式输出
            document.write(xhr.responseText);
            //将返回结果以XML形式输出
            //docunment.write(xhr.responseXML);
        }
    }, false); 
    xhr.addEventListener('timeout', ()=>{
        console.log('触发事件后想执行的逻辑');
    });

6、Ajax 进阶之 FormData

  • IE10 及以上版本才支持 FormData
  • 使用 Ajax 提交表单,可以阻止提交后跳转
    // 表单
    <form id="login" action="" method="POST">
    ......
    <input id="submit" type="submit" value="登陆" />
    </form>

    // 解构获取表单的参数和值
    const {username, password} = login;

    const btn = document.getElementById('submit');

    // 点击提交触发事件
    btn.addEventListener('click', e =>{
        // 阻止表单自动提交
        e.preventDefault();

        // 表单数据验证

        // 发送 Ajax 请求
        let xhr;
        if (window.XMLHttpRequest) {
            xhr = new XMLHttpRequest();
        } else {
            xhr = new ActiveObject('Mircosoft.XMLHTTP');
        }

        xhr.onreadystatechange =function() {
            if (xhr.readyState == 4 && xhr.status == 200) {
                console.log(xhr.response);
            }
        };

        // 准备发送
        xhr.open("POST", url, true);
        // 组装数据
        let data = `username=${username.value}&password=${password.value}`;
        // 设置发送格式
        xhr.setRequestHeader(Content-Type, 'application/x-www-form-urlencoded');
        // 发送
        xhr.send(data);
    },false);

FormData 的基本用法:
- 通过 HTML 表单元素创建 FromData 对象,然后直接将表单元素(login)传过去
- 也可以使用 append() 方法追加数据。例:data.append(“age”, 18);
- 使用 formData 的作用就是替换自己组装数据的过程,并且不用自己设置请求头的数据发送格式

    // 表单
    <form id="login" action="" method="POST">
    ......
    <input id="submit" type="submit" value="登陆" />
    </form>

    // 解构获取表单的参数和值
    const {username, password} = login;

    const btn = document.getElementById('submit');

    // 点击提交触发事件
    btn.addEventListener('click', e =>{
        // 阻止表单自动提交
        e.preventDefault();

        // 表单数据验证

        // 发送 Ajax 请求
        let xhr;
        if (window.XMLHttpRequest) {
            xhr = new XMLHttpRequest();
        } else {
            xhr = new ActiveObject('Mircosoft.XMLHTTP');
        }

        xhr.onreadystatechange =function() {
            if (xhr.readyState == 4 && xhr.status == 200) {
                console.log(xhr.response);
            }
        };

        // 准备发送
        xhr.open("POST", url, true);
        // 组装数据
        const data = new FormData(login);        
        // 发送
        xhr.send(data);
    },false);

7、Ajax 扩展之 Fetch

Fetch 是什么:
- Fetch 也是前后端通信的一种方式
- Fetch 是 Ajax(XMLHttpRequest)的一种替代方案,它是基于 Promise 的

Fetch 的基本用法:

二、Ajax 扩展之 axios

1、axios 介绍

axios 是什么:
- 第三方的 Ajax 库,是基于 Promise
- axios 的中文官方文档:http://www.axios-js.com/zh-cn/docs

axios 请求方法主要有:get、post、put、patch、delete
- get 方法主要用于获取数据
- post 方法主要用于提交数据(表单提交或文件上传)
- put 方法主要用于更新数据(将所有数据推送到后端)
- patch 方法也用于更新数据(只将修改的数据推送到后端)
- delete 方法主要用于删除数据

2、axios 请求方法

(1)axios 请求方法之 get

  • get 方法主要用于获取数据
  • get 方法有两种写法
// 请求到URL为:http://localhost:8080/hello?id=1
// 第一种形式
axios.get('/hello', {
    params: {
        id: 1
    }
}).then((res) => {
    console.log(res)
})
// 第二种形式
axios({
    method: 'get',
    url: '/hello',
    params: {
        id: 1
    }
}).then((res) => {
    console.log(res)
})

(2)axios 请求方法之 post

  • post 方法主要用于提交数据
  • post 方法有两种写法
// 请求到URL为:http://localhost:8080/post

// json 形式数据
let data = {
    id: 2
};
// 第一种形式:别名形式
axios.post('/post', data).then((res) => {
    console.log(res)
})
// 第二种形式
axios({
    method: 'post',
    url: '/post',
    data: data
}).then((res) => {
    console.log(res)
})

// form-data 形式数据
let formData = new FormData()
for(let key in data) {
    formData.append(key, data[key])
}
axios.post('post', formData).then((res) => {
    console.log(res)
})

(3)axios 请求方法之 put 和 patch

  • put 和 patch 这两种方法请求的形式和 post 方法非常相似,只有请求方法不同,其它都相同
let data = {
    id: 2
};
// put:别名形式
// 请求到URL为:http://localhost:8080/put
axios.put('/put', data).then((res) => {
    console.log(res)
})
// patch:别名形式
// 请求到URL为:http://localhost:8080/patch
axios.patch('/patch', data).then((res) => {
    console.log(res)
})

(4)axios 请求方法之 delete

  • delete 方法主要用于删除数据
// 第一种形式:别名形式

// 如果需要在请求URL上加参数
// 请求到URL为:http://localhost:8080/delete?id=1
axios.delete('/delete', {
    params: {
        id: 1
    }
}).then((res) => {
    console.log(res)
})
// 不需要在请求URL上加参数
// 请求到URL为:http://localhost:8080/delete
axios.delete('/delete', {
    data: {
        id: 1
    }
}).then((res) => {
    console.log(res)
})

// 第二种形式:不使用别名

// 如果需要在请求URL上加参数
// 请求到URL为:http://localhost:8080/delete?id=1
axios({
    method: 'delete',
    url: '/delete',
    params: {
        id: 1
    }
}).then((res) => {
    console.log(res)
})
// 不需要在请求URL上加参数
// 请求到URL为:http://localhost:8080/delete
axios({
    method: 'delete',
    url: '/delete',
    data: {
        id: 1
    }
}).then((res) => {
    console.log(res)
})

3、axios 并发请求

  • axios.all() 的参数是一个数组,数组里面是一个一个的 axios 请求
  • axios.spread() 的作用是在 axios.all() 请求完成之后将它的返回数据封装处理;axios.spread() 只有一个回调函数作为参数,回调函数参数的个数由 axios.all() 内的请求个数决定
axios.all([
    axios.get('hello'),
    axios.get('world')
]).then(
    axios.spread((helloRes, worldRes) => {
        // 这里面可以进行多个请求数据的处理
        console.log(helloRes, worldRes)
    })
)

4、创建 axios 实例及使用

  • 为什么要创建实例:如果有多个后端接口地址且超时时长不一样的话,使用默认的 axios 调用请求方法就会使用同一个实例(如果不嫌麻烦也可以在请求方法的 url 上写完整的 URL 和 配置参数)
  • 创建 axios 实例,参数就是该实例的配置信息
  • 创建好实例后就可以用实例调用请求方法了
  • 实例的配置信息 baseURL 会自动拼接在请求方法里相对路径 url 的前面
import axios from 'axios'

const instance = axios.create({
    baseURL: 'http://localhost:8080',
    timeout: 1000
});
instance.get('/hello').then((response) => {
    console.log(response)
})

5、axios 基本配置参数

  • baseURL:请求的域名,基本地址
  • timeout:请求超时时长(单位:毫秒(ms))
  • url:请求的路径
  • method:请求方法(get、post、put、patch、delete)
  • headers:设置请求头信息
headers: {
    token: '',  // 一般用来识别登陆人的信息
    Content-Type: application/json   // 请求数据格式
}
  • params:请求参数(会将请求参数拼接在 URL 上)
  • data:请求参数(会将请求参数放在请求体)
  • 配置优先级:请求配置 > 实例配置 > 全局配置

axios 全局配置:

axios.defaults.baseURL = 'http://localhost:8080'
axios.defaults.timeout = 1000

axios 实例配置:

let instance = axios.create()
instance.defaults.baseURL = 'http://localhost:8080'
instance.defaults.timeout = 1000

axios 请求配置:

let instance = axios.create()
instance.get(url, {
    baseURL = 'http://localhost:8080',
    timeout = 1000
})

6、axios 拦截器

  • 拦截器:在请求或响应被处理前拦截它们
  • 拦截器分为:请求拦截器和响应拦截器
// 请求拦截器
axios.interceptors.request.use(
(config) => {
    // 在请求前可以做一些事,例如:修改配置,修改内容
    console.log(config)
    // 完成操作后一定要将 config 返回
    return config
}, (err) => {
    // 在请求错误的时候可以做一些事
    // 返回的是一个 Promise对象
    return Promise.reject(err)
})

// 响应拦截器
axios.interceptors.response.use(
(res) => {
    // 请求成功后对响应数据做处理
    console.log(res)
    // 完成操作后一定要将 response 返回
    // 返回的数据会到 axios.get或post().then(res => {}) 的 then方法里
    return res
}, (err) => {
    // 在响应错误的时候可以做一些事
    // 返回的是一个 Promise对象
    // 返回的数据会到 axios.get或post().then(res => {}).catch(err => {}) 的 then方法的第二个回调函数或者 catch 方法里
    return Promise.reject(err)
})

三、JSON

1、初识 JSON

JSON 是什么:
- JSON 全称是(JavaScript Object Notation)JavaScript的对象表示法
- JSON 是发送和接收数据的一种格式

为什么需要 JSON:
- JSON 有3种形式,每种形式和写法都和 JS 中的数据类型很像,可以很轻松的和 JS 中的数据类型互相转换

2、JSON 的 3 种形式

简单值形式:
- JSON 的简单值形式对应着 JS 中的基础数据类型
- 简单值形式有:数字、字符串、布尔值、null

// JSON 文件以 .json 结尾
{
    12,
    "这是字符串",
    true,
    null
}

注意事项:
- JSON 中没有 undefined 值
- JSON 中的字符串必须使用双引号
- JSON 文件中不能有注释

对象形式:
- JSON 中对象的属性名必须用双引号,属性值如果是字符串也必须用双引号
- JSON 中只要涉及字符串的地方一定要加双引号
- 不支持 undefined
- JSON 对象形式里面可以嵌套数组、对象、JS 基本数据类型等

// JSON 文件以 .json 结尾
{
    "username" : "zhangsan",
    "age" : 18,
    "hobby" : ["看书","玩游戏","踢足球"]
    family : {
        "father" : "张爸",
        "mother" : "张妈"
    }
}

数组形式:
- JSON 数组形式里面也可以嵌套数组、对象、JS 基本数据类型
- 不支持 undefined

// JSON 文件以 .json 结尾
[
    1,
    "我是字符串",
    ["嵌套数组",2,"字符串",null],
    {
        "username" : "zhangsan",
        "age" : 18
    }
]

3、JSON 的常用方法

JSON.parse():
- 将JSON 格式的字符串转解析为 JS 对应的数据类型

JSON.stringify():
- 是将 JS 对应的数据类型转为对应的 JSON 格式的数据

使用 JSON.parse() 和 JSON.stringify() 封装 localStorage:

// js/storage.js

const storage = window.localStorage;
// 改为 sessionStorage 的方法同理
// const session = window.sessionStorage;

// 设置值
const set = (key, value) =>{
    storage.setItem(key, JSON.stringify(value));
};

// 获取值
const get = key => {
    return storage.getItem(key);
};

// 移除
const remove = () => {
    storage.removeitem(key);
};

// 清空
const clear = () => {
    storage.clear();
};

// 向外暴露函数
export {set, get, remove, clear};
// 在 HTML 中引入
<script type = "module">
    import {get, set, remove, clear} from 'js/storage.js';

    // 示例
    set('userInfo',{
        name: '张三',
        age: 18
    });
</script>

四、跨域

1、初识跨域

跨域是什么:
- 当一个请求url的协议、域名、端口三者之间任意一个与当前页面url不同即为跨域
- 不同域之间的请求就是跨域请求

跨域请求为什么会被阻止:
- 阻止跨域请求的原因是浏览器本身的一种策略----同源策略
- 其它客户端或者服务器不存在跨域被阻止的问题,跨域只存在浏览器

跨域的解决方案:
- CORS 跨域资源共享
- JSONP
- 优先使用 CORS 跨域资源共享,如果浏览器不支持 CORS 的话,在使用 JSONP

URI 和 URL的区别:
- URI 是 Uniform Resource Identifier,统一资源标识符,用来唯一的标识一个资源。
- URL是 Uniform Resource Locator,统一资源定位器,它是一种具体的URI,即URL可以用来标识一个资源。
- 简单区别就是 URL 比 URI 包含信息更多

2、跨域的解决之 CORS 跨域资源共享

CORS 是什么:
- CORS 是 跨域资源共享
- CORS 是由后端采取的措施
- 后端在响应中设置 Access-Control-Allow-Origin: * 表明允许所有的域名来跨域请求它,* 是通配符,表示没有任何限制
- 设置只允许指定域名的跨域请求,只要在 Access-Control-Allow-Origin: 后设置指定的域名即可。例如:Access-Control-Allow-Origin: http://127.0.0.1:8080

使用 CORS 跨域的过程:
- 浏览器向服务器端发送请求
- 如果服务器允许跨域,则后端服务需要在响应头中添加 Access-Control-Allow-Origin 头信息
- 浏览器接收到响应
- 如果是同域的请求,浏览器不会额外做什么,本次前后端通信完成
- 如果是跨域请求,浏览器会从响应头中查找是否允许跨域访问
- 如果是允许跨域,则通信完成
- 如果没找到或不包含想要跨域的域名,就丢弃响应的结果

CORS 的兼容性:
- IE10 及以上版本的浏览器可以正常使用 CORS
- 老版本识别不了响应头中的 Access-Control-Allow-Origin 字段
- 关于兼容性,可以参考该网站:https://caniuse.com/

3、跨域的解决之 JSONP

JSONP 的原理:
- script 标签跨域不会被浏览器阻止
- JSONP 主要就是利用 script 标签加载跨域文件

使用 JSONP 实现跨域:
- 服务器端准备好 JSONP 接口。例如:https://www.test.com/jsonp?callback=response
- 手动使用 script 标签加载 JSONP 接口,
- 或者动态加载 JSONP 接口

    const script = document.createElement('script');
    script.src = 'https://www.test.com/jsonp?callback=response';
    document.body.appendChild(script);
  • 声明函数,例:const handleResponse = data =>{ console.log(“就可以使用handleResponse里面的data数据了”)};

4、跨域的解决之 document.domain

  • 设置 document.domain 解决跨域原理:浏览器是通过 document.domain 属性来检查两个页面是否同源,因此只要通过设置相同的document.domain,页面间就可以共享 Cookie

5、跨域的解决之 window.postMessage()

  • 调用 window.postMessage() 方法实现不同窗口之间发消息

总结