JSONP解决JS跨域问题的实现


Posted in Javascript onMay 25, 2020

现代编程中前后端都已经是分开部署了,前端有自己的域,后端也有自己的域。由于浏览器同源策略的限制,非同源下的请求,就会产生跨域问题。解决跨域问题的方法有很多种,例如CORS(cross orign resources share)和JSONP。这里我就着重介绍一下jsonp的解决方案。

一:基础概念

同源策略:同一协议,同一域名,同一端口号。只要不满足三者其中一种都是属于跨域问题。

举几个简单的例子

1: https://www.a.com:8080到http://www.a.com:8080的请求会出现跨域(域名、端口相同但协议不同)

2: https://www.a.com:8080到https://www.b.com:8080的请求会出现跨域(协议、端口相同但域名不同)

3: https://www.a.com:8080到https://www.a.com:9090的请求会出现跨域(协议、域名相同但端口不同)

跨域:跨域的安全限制都是对浏览器端来说的,服务器端是不存在跨域安全限制的。浏览器的同源策略限制从一个源加载的文档或脚本与来自另一个源的资源进行交互。

二:区别JSON和JSONP

  • JSON的全称为JavaScript Object Notation,是一种轻量级的数据交互格式。它基于 ECMAScript (欧洲计算机协会制定的js规范)的一个子集,采用完全独立于编程语言的文本格式来存储和表示数据。简单来说,json就是一种用来传输数据的数据格式。
  • JSONP是一种非正式传输协议,该协议的一个要点就是允许用户传递一个callback(或者一开始就定义一个回调方法)参数给服务端,然后服务端返回数据时会将这个callback 参数作为函数名来包裹住 JSON 数据,这样客户端就可以随意定制自己的函数来自动处理返回数据了。

三:跳出同源的“舒适圈”

我们发现,在web页面调用js文件是不受是否跨域问题的影响的。而且我们还发现凡是拥有 src 这个属性的标签都拥有跨域的能力,比如img和script。在html页面中我们经常会做引入图片的操作,通过img标签中的src属性,我们就可以请求得到一个静态资源。

JSONP解决JS跨域问题的实现

我们可以看到这本质上就是一个GET请求,同理,link和script里的href和src同样可以通过GET请求去请求资源。

<script src="http://localhost:9090/api"></script>

JSONP解决JS跨域问题的实现

我们可以看到这本质上就是一个GET请求,同理,link和script里的href和src同样可以通过GET请求去请求资源。它们并没有受到同源策略的影响,jsonp的实现原理其实就是利用了这个策略的小“bug”,从而实现跨越请求的。既然是一个GET请求,服务器一定可以收到这个请求并作出响应。下面就让我们来具体实现一下吧!

四:原理及跨域实现

具体流程( 原理 )

jsonp的执行流程其实就是简单的两步。第一,在前端预先定义好一个带参数的回调函数用来接受后端传来的数据。第二,在后端启动一个server服务,将要传的数据以定义好了的回调函数名加上返回结果的方式传给前端。

// 前端部分
<script>
  // 1 callback
  // 2 后端 callbackName(数据)
  function onResponse(posts) {
    console.log(posts);
  }
  // 前端没有调用
</script>
<!-- 后端返回结果 -->
<!-- 调用 -->
<script src="http://localhost:9090/api"></script>
//后端部分
const http = require('http');
http.createServer((req, res) => {
  if (req.url === '/api') {
    let posts = ['js', 'php'];
    res.end(`onResponse(${JSON.stringify(posts)})`);
  }
})
.listen(9090, () => {
  console.log(9090)
})

JSONP解决JS跨域问题的实现 

前端script中的src请求完毕以后,后端会给前端返回一个字符串onResponse(["js","php"]),因为script标签的原因,浏览器会把这一段字符串当做js来执行。这样我们一开始在前端定义好了的回调就会执行,我们就拿到数据了。

封装

以上只是有一个简单的请求,实际项目中肯定会有很多个请求,我们肯定不可以定义一排的script标签和回调函数。这样写出来的代码就太不灵活了。封装的目的之一也就是为了前端可以灵活地修改预定义回调函数的名字,而不是在前后端把回调函数定死。同时,把代码封装以后,我们就不用手动地创建回调函数了,封装后的函数会帮我们自动放src的地址,自动创建回调函数名。

// 后端
const http = require('http');
const url = require('url');
http.createServer((req, res) => {
 // /api?callback=onResponse
 // 解析前端请求url中的callback名
 if(req.url.includes('/api')) {
  let myurl = url.parse(req.url);
  let params = new URLSearchParams(myurl.query)
  let posts = ['js', 'php'];
  let mathodName = params.get('callback');
  res.end(`${mathodName}(${JSON.stringify(posts)})`)
 }
})
.listen(9090, () => {
 console.log(9090);
})
// 前端(代码放在body中执行)
<script>
 function jsonp(url, options) {
  // 超时处理
  const { timeout } = options;
  return new Promise((resolve, reject) => {
   // 防止函数名冲突
   let funcName = `jsonp${Date.now()}`;
   let time = null, scriptNode;
   // 定义callback
   window[funcName] = function(data) {
    if (timeout) clearTimeout(time);
    resolve(data);
    // 很重要的性能优化点
    // 清除本次请求产生的回调函数和script标签
    delete window[funcName];
    document.body.removeChild(scriptNode);
   }
   // 创建script标签
   scriptNode = document.createElement('script');
   // 给script标签添加src属性
   scriptNode.src = `${url}?callback=${funcName}`;
   // 发出请求
   document.body.appendChild(scriptNode);
   time = setTimeout(() => {
    reject('network err, timeout')
   }, timeout)
   // 失败
   scriptNode.onerror = function(err) {
   reject(err);
   }
  })
 }
 jsonp('http://localhost:9090/api', {
  callBack: 'res1',
  // 超时处理
  timeout: 3000
 })
 // 请求成功
 .then(res => {
  console.log('jsonp->', res);
 })
 // 请求失败
 .catch(err => {
   console.log("network err!")
 })
</script>

JSONP解决JS跨域问题的实现 

五:总结

1: 优点

  • 它不像XMLHttpRequest对象实现的Ajax请求那样受到同源策略的限制
  • 它的兼容性更好,在更加古老的浏览器中都可以运行,不需要XMLHttpRequest或ActiveX的支持
  • 并且在请求完毕后可以通过调用callback的方式回传结果

2: 缺点

只支持GET请求 而不支持POST等其它类型的HTTP请求

它只支持跨域HTTP请求这种情况,不能解决不同域的两个页面之间如何进行JavaScript调用的问题

到此这篇关于JSONP解决JS跨域问题的实现的文章就介绍到这了,更多相关JSONP解决JS跨域内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章希望大家以后多多支持三水点靠木!

Javascript 相关文章推荐
Javascript 继承机制的实现
Aug 12 Javascript
jquery改变tr背景色的示例代码
Dec 28 Javascript
使用js判断当前时区TimeZone是否是夏令时
Feb 23 Javascript
绑定回车enter事件代码
May 18 Javascript
javascript中的作用域和闭包详解
Jan 13 Javascript
Bootstrap学习笔记之css组件(3)
Jun 07 Javascript
bootstrap table实例详解
Jan 06 Javascript
Vue.js实现模拟微信朋友圈开发demo
Apr 20 Javascript
elementUI select组件使用及注意事项详解
May 29 Javascript
vue 实现Web端的定位功能 获取经纬度
Aug 08 Javascript
selenium 反爬虫之跳过淘宝滑块验证功能的实现代码
Aug 27 Javascript
JS如何操作DOM基于表格动态展示数据
Oct 15 Javascript
JS实现时间校验的代码
May 25 #Javascript
使用Typescript和ES模块发布Node模块的方法
May 25 #Javascript
js 动态校验开始结束时间的实现代码
May 25 #Javascript
使用 Opentype.js 生成字体子集的实例代码详解
May 25 #Javascript
Node.js API详解之 repl模块用法实例分析
May 25 #Javascript
微信小程序仿抖音视频之整屏上下切换功能的实现代码
May 24 #Javascript
如何使用vue slot创建一个模态框的实例代码
May 24 #Javascript
You might like
PHP实现GIF图片验证码
2015/11/04 PHP
关于php支持的协议与封装协议总结(推荐)
2017/11/17 PHP
PHP call_user_func和call_user_func_array函数的简单理解与应用分析
2019/11/25 PHP
详解阿里云视频直播PHP-SDK接入教程
2020/07/09 PHP
js 新浪的一个图片播放图片轮换效果代码
2008/07/15 Javascript
jquery特效 幻灯片效果示例代码
2013/07/16 Javascript
jQuery动画效果-fadeIn fadeOut淡入浅出示例代码
2013/08/28 Javascript
ajax请求乱码的解决方法(中文乱码)
2014/04/10 Javascript
Js操作树节点自动折叠展开的几种方法
2014/05/05 Javascript
jquery插件推荐浏览器嗅探userAgent
2014/11/09 Javascript
jQuery实现TAB风格的全国省份城市滑动切换效果代码
2015/08/24 Javascript
Javascript removeChild()删除节点及删除子节点的方法
2015/12/27 Javascript
js判断图片加载完成后获取图片实际宽高的方法
2016/02/25 Javascript
浅析js绑定事件的常用方法
2016/05/15 Javascript
Move.js入门
2017/02/08 Javascript
JS去掉字符串前后空格或去掉所有空格的用法
2017/03/25 Javascript
Angularjs2不同组件间的通信实例代码
2017/05/06 Javascript
jQuery选择器_动力节点Java学院整理
2017/07/05 jQuery
vue基于element-ui的三级CheckBox复选框功能的实现代码
2018/10/15 Javascript
微信小程序用户位置权限的获取方法(拒绝后提醒)
2018/11/15 Javascript
jQuery实现动态加载(按需加载)javascript文件的方法分析
2019/05/31 jQuery
Vue 数组和对象更新,但是页面没有刷新的解决方式
2019/11/09 Javascript
微信小程序实现音乐播放器
2019/11/20 Javascript
[01:28]一分钟告诉你DOTA2 TI9不朽宝藏Ⅱ中有什么!
2019/07/09 DOTA
Python删除指定目录下过期文件的2个脚本分享
2014/04/10 Python
跟老齐学Python之不要红头文件(2)
2014/09/28 Python
python使用fileinput模块实现逐行读取文件的方法
2015/04/29 Python
Python编程求解二叉树中和为某一值的路径代码示例
2018/01/04 Python
简单实现python聊天程序
2018/04/01 Python
pandas的相关系数与协方差实例
2019/12/27 Python
python爬虫利用selenium实现自动翻页爬取某鱼数据的思路详解
2020/12/22 Python
Lookfantastic美国/加拿大:英国知名美妆购物网站
2019/03/27 全球购物
函授毕业生的自我鉴定
2013/11/26 职场文书
开业庆典活动策划方案
2014/09/21 职场文书
2014年学校工会工作总结
2014/12/06 职场文书
个人廉政承诺书
2015/04/28 职场文书