实现跨域访问
一. 跨域访问
-
URL地址:
URL地址:网络协议 + 域名(IP地址) + 端口号 + 资源路径
- 同源:网络协议相同、域名相同以及端口相同
- 不同源:
- 域名不相同:完全跨域
- 域名相同,端口号不一样:跨子域
- 互联网默认原则(同源策略)不允许跨域访问
-
通源策略
1995年,同源政策由Netscape公司引入游览器。现在所有游览器都实行这个政策。
同源指“三个相同”:协议相同、域名相同和接口相同
- 同源策略的作用:
- 保证用户信息安全
- 防止恶意的网站窃取数据
- 如果不是同源的话,会有三种行为受限:
- Cookie、LocalStorage和IndexDB无法读取
- DOM无法获得
- Ajax请求不能发送
-
跨域
一个资源从资源本身所在服务器不同的域或端口请求一个资源时,资源会发起一个跨域HTTP请求。
游览器限制从脚本发起的跨域HTTP请求,所以有些API的Web应用程序只能从加载应用程序的一个域请求HTTP资源
- 常见跨域类型:
- 完全跨域:一个顶级域名方向另一个停机域名
- 跨子域:相同顶级域名下的两个子级域名互相通信
- 跨域元素
- HTML页面中一些允许指定路径的元素具有跨域特性
- link
- script
- img
- iframe
二. JSONP解决跨域
-
JSONP(JSON with Padding)是JSON的一种“使用模式”,可用于解决游览器的跨域问题
JSONP的使用模式就是利用
<script>
元素的开放策略,使游览器可以获得其他来源动态产生的JSON资料- 数据必须是JSON格式
-
过程
游览器:
- 创建一个函数,定义形参,用于接收服务器返回的数据
<script> // 定义发送服务器端 function fn(data){ console.log(data) } </script>
- 利用script元素的开放策略,向指定服务器地址发送跨域访问请求,并携带回调函数的名称
<script src="http://127.0.0.1:8088?callback=fn"></script>
服务器:
- 接收游览器页面发送的跨域访问请求
- 将响应数据构建成JSON格式,并将数据放置在回调函数中进行传递
res.end('fn({"msg":"HELLO WORLD"})');
先编写回调函数的调用语法,然后将数据作为回调函数的实参
- 完整代码
const http = require('http'); // 引入HTTP模块 // 创建一个服务 const server = http.createServer((req,res)=>{ res.statusCode = 200; res.setHeader('Content-Type','text/plain'); // 响应页面请求 res.end('fn({"msg":"HELLO WORLD"})'); }) // 开启服务器 server.listen(8088,()=>{ console.log('>>>服务器已开启,端口号8088') })
三. jQuery实现跨域访问
-
jQuery中
$.getJSON()
方法可以通过使用JSON形式的回调函数来加载其他网域的JSON数据$.getJSON()
第一个参数为URL,要在后面添加’callback=?’, jQuery会自动把?改为正确的函数名
-
客户端
$.getJSON('http://127.0.0.1:8088?callback=?',function(data){ console.log(data) });
-
服务器
const http = require('http'); // 引入HTTP模块 // 创建一个服务 /* req---表示请求 res---表示响应 */ const server = http.createServer((req,res)=>{ var url = req.url; var urlObj = require('url').parse(url); var functionName = urlObj.query.split('&')[0].split('=')[1]; res.statusCode = 200; res.setHeader('Content-Type','text/plain'); /** * 响应页面请求 * - 响应的数据必须是JSON格式 * - 将响应数据放在回调函数中 */ // res.end('fn({"msg":"HELLO WORLD"})'); res.end(functionName + '({"msg":"HELLO WORLD"})'); }) // 开启服务器 server.listen(8088,()=>{ console.log('>>>服务器已开启,端口号8088') })
Axios库
一. axios是继承语法
Axios是一个类库,基于PROMISE管理的AJAX库
提供了对应的请求方法如: GET\POST\HEAD\PUT\DELETE…
axios.get() 向服务器发送一个请求,基于GET方法
axios.post() 向服务器发送一个请求,基于POST方法
- 基于GET或者POST方法请求,返回的结果都是PROMISE实例
安装Axios:
- 直接
npm install axios --save
安装 - 或者先安装yarn
npm i -g yarn
然后yarn add axios
例子:
- get方法
axios.get('http://127.0.0.1:8088/',{
params:{
name:'admin',
age:26
}
});
- post方法
axios.post('http://127.0.0.1:8088/add', {
params: {
name: 'admin',
age: 26
}
});
- 解决回调地狱
axios.get('http://127.0.0.1:8088/',{
params:{
name:26
}
}).then(result =>{
let {data} = result;
return axios.post('http://127.0.0.1:8088/')
}).then(result => {
let {data} = result;
console.log(data);
})
二. axios请求合并
- 两个请求都执行成功后才执行
- 普通写法
let result = null; axios.get("A").then(resultA =>{ result = resultA; // 把A的结果给全局的result return axios.get('B'); }).then(resultB => { // A和B都执行完毕后才执行 })
- axios的all方法
// 保存了Ajax请求实例的数组 let sendAry=[ axios.get('http://127.0.0.1:8088/'), axios.post('http://127.0.0.1:8088/add') ] axios的all()方法 axios.all(sendAry).then(result =>{ console.log(result); // 分别存了两个请求的结果 })
三. axios常用配置项
-
初始化路径
axios.defaults.baseURL = 'http://127.0.0.1:8088';
-
设置响应拦截器:分别在响应成功和失败的时候做一些拦截处理
//axios.interceptors.response.use(function success(result){ //return result.data //} // 箭头函数写法 axios.interceptors.response.use(result => result.data)
-
自定义请求成功失败规则
// axios.defaults.validateStatus = function validateStatus(status){ // return /^(2|3)\d{2}$/.test(status); // } // 箭头函数写法 axios.defaults.validateStatus = status => /^(2|3)\d{2}$/.test(status);
-
设置在post请中向服务器发送内容的格式,默认RAW,常用为URL-ENCODEED格式
axios.defaults.headers['Content-Type'] = 'appliction/x-www-form-urlencoded'; axios.defaults.transformRequest = data =>{ let str = ''; for (let attr in data){ if(data.hasOwnProperty(attr)){ str += '${attr}=${data[attr]}&'; } } return str.substring(0, str.length - 1); }
-
不适合设置成全局的配置项
// 设置时间 axios.defaults.timeout = 3000; // 自定义请求头 axios.defaults.headers ={ name:'admin' } // get传参 axios.defaults.params = {}; // post传参 axios.defaults.data= {};
四.Fetch
Fetch不是Ajax,是为了代替Ajax而存在的,是JS里内置的API。可以通过Fetch实现客户端和服务端的信息通信。
- ES2018规范中新增的Fetch,游览器支持度可能不好,可以基于BABEL的最新语法解析包进行解析
- 例子
// get方法
fetch('http://127.0.0.1:8088/').then(result=>{
let {status} = result;
if(/^(4|5)\d{2}$/.test(status)){
throw new Error('query data is error')
return;
}
return result.json();
}).then(result =>{
console.log(result);
}).catch(msg =>{
console.log(msg);
})
五. 基于promise创建ajax库
-
首先设置好默认配置项
let _default = { method: 'GET', url: '', baseURL: '', headers: {}, dataType: 'JSON', data: null, // POST系列请求基于请求主体传递给服务器的内容 params: null, // GET系列请求基于问号传参传递给服务器的内容 cache: true }; // 把默认配置暴露出去,使用户可以自己修改值 ajaxPromise.defaults = _default;
-
要用到的方法
// 把对象变为URLENCODE格式的字符串 ajaxPromise.formatData = function formatData(obj){ let str = ""; for(let attr in obj){ if(obj.hasOwnProperty(attr)){ str += `${attr}=${obj[attr]}&` } } return str.substring(0,str.length - 1); }; ajaxPromise.check = function check(url){ return url.indexOf('?') > -1 ? '&' : '?'; };
-
ajax请求相关
let ajaxPromise = function ajaxPromise(options) { // options中融合了默认配置信息、用户基于DEFAULTS修改的信息、用户执行GET\POST方法时传递的配置信息 let { url, baseURL, method, data, dataType, headers, cache, params } = options; // 把参数进一步处理 if(/^(GET|DELETE|HEAD|OPTIONS)$/i.test(method)){ // get系列 if(params){ url += `${ajaxPromise.check(url)}${ajaxPromise.formatData(params)}`; } if(cache === false){ url += `${ajaxPromise.check(url)}_=${+(new Date())}`; } data = null; // get请求主体里没内容 }else{ // post if(data){ data = ajaxPromise.formatData(data); }; }; // 基于promise发送AJAX return new Promise((resolve, reject) => { let xhr = new XMLHttpRequest; xhr.open(method, '${baseURL}${url}'); // 如果HEADERS存在,设置请求头 if (headers !== null && typeof headers === 'object') { for (let attr in headers) { if (headers.hasOwnProperty(attr)) { let val = headers[attr]; if (/[\u4e00-\u9fa5]/.test(val)) { // val中有中文,用encodeURIComponent进行编码 val = encodeURIComponent(val); }; xhr.setRequestHeader(attr, headers[attr]); }; }; }; // 设置请求成功条件 xhr.onreadystatechange = () => { if (xhr.readyState === 4) { if (/^(2|3)\d{2}&/.test(xhr.status)) { let result = xhr.responseText; dataType = dataType.toUpperCase(); dataType === 'JSON' ? result = JSON.parse(result) : (dataType === 'XML' ? result = xhr.responseXML : null); resolve(result,xhr); return; }; reject(xhr.statusText, xhr); }; }; xhr.send(data); }); };
-
请求方法
// get请求 ['get', 'delete', 'head', 'options'].forEach(item =>{ ajaxPromise[item] = function anonymous(url, options = {}){ options={ ..._default, // 默认值或default修改的值 ...options, // 用户调取方法传递的配置项 url:url, // 请求的url地址 method: item.toUpperCase() // 以ajaxPromise.method执行 }; return ajaxPromise(options); }; }); // post请求 3个参数 ['post', 'put', 'patch'].forEach(item => { ajaxPromise[item] = function anonymous(url,data = {},options = {}) { options = { ..._default, // 默认值或default修改的值 ...options, // 用户调取方法传递的配置项 url: url, // 请求的url地址 method: item.toUpperCase(), // 以AjaxPromise.method执行 data:data }; return ajaxPromise(options); }; });