浅谈 JSONP
说起跨域的解决方案,总是会说到 JSONP,但是很多时候都没有仔细去了解过 JSONP,可能是因为现在 JSONP 用的不是很多(多数时候都是配置响应头实现跨域),也可能是因为用 JSONP 的场景一般都是用 jQuery 来实现,所以对 JSONP 知之甚少。
JSONP 的本意是 JSON with Padding,即填充式 JSON。为什么叫填充式呢?因为服务端不会直接返回 JSON 格式数据给客户端,它会拼接成一个字符串,这个字符串被拿到客户端执行。这是对于 JSON 的一种应用。
JSONP 的原理是什么?
发明 JSONP 的老头子们发现虽然同源策略(CORS)限制了 ajax 对于其它服务器的访问,但是并不能限制 HTML 的资源请求。
比如在 HTML 中,img、script、link 等标签完全可以访问任何地址的资源。而其中的 script 标签为跨域请求提供了一种新的思路,因为 script 请求的是一段可执行的 JavaScript 代码。我们可以把之前直接从服务器返回的数据封装到 JavaScript 代码中,然后在前端再使用这些数据。这就是 JSONP 的实现原理。
为什么 JSONP 只能使用 GET 方法?
使用 jQuery 的 $.ajax
进行 JSONP 请求时,type 属性总是选择为 GET
,如果填为 POST
,就会报错,这是为什么呢?其实理解 JSONP 的原理之后,这就很好理解了。原因就是 script 标签的资源请求只能是 GET
类型,目前为止我还没有见过 POST 类型的资源请求~
如何实现 JSONP?
前端小白不理解 JSONP 的另一个原因就在于 JSONP 不只是前端这一块的任务,只靠前端是无法实现的,后端也必须做相应处理。
前端请求一个专用的接口获取数据,请求的数据会以 JavaScript 代码的形式返回,假如数据如下:
{
"name": "russ",
"age": 20
}
那要构造成什么样的 JavaScript 代码才能被前端使用到呢?最容易想到的就是把数据赋值给一个全局变量,或者把数据扔到函数里面。扔给全局变量的话会导致一些问题(全局暴露、命名冲突、数据处理逻辑分散……),所以把数据扔给一个专门处理数据的函数比较合适。这也是 JSONP 所采用的方案。所以拼接出来的字符串(也是后端返回的 js 代码)基本如下:
callback({
"name": "russ",
"age": 20
})
那么前端就需要在 script 请求返回之前定义好 callback 这个函数,以便在 script 返回之后可以顺利加载执行。这里需要前后端约定好回调函数的名称。当然可以前端传递回调的名称给后端,后端根据前端传递的名称进行 JavaScript 代码拼接,jQuery.ajax 就有这种实现,允许前端自定义回调的名称。
先讲讲后端实现,因为后端实现起来比较简单~
后端处理
以下实现均使用 Nodejs。
Nodejs 实现代码如下:
const http = require('http');
const { parse } = require('url');
// 假设这是在数据库中找到的数据~
const data = {
name: 'russ',
age: 20,
gender: 'male'
};
const server = http.createServer((req, res) => {
const url = parse(req.url, true); // 解析 url
// 只有路由为 `/user` 的 GET 请求会被响应
if (req.method === 'GET' && url.pathname === '/user') {
const { callback } = url.query; // 获取 callback 回调名称
if (callback) // 如果传递了 callback 参数,说明是 JSONP 请求
return res.end(`${callback}(${JSON.stringify(data)})`);
else // 没传递 callback,直接当做不跨域的 GET 请求返回数据
return res.end(JSON.stringify(data));
}
return res.end('Not Found'); // 不匹配的路由返回错误
});
server.listen(3000, () => {
console.log('Server listening at port 3000...');
});
可以直接在浏览器中请求查看结果~
前端处理
伴随着很多功能强大的 api 的出现,我们在很多场景下都可以直接弃用 jQuery(参考nefe/You-Dont-Need-jQuery)。而如果我们的页面没有使用 jQuery 的时候,我们就需要手动实现 JSONP 了~
前面说过 JSONP 的原理是 script 标签的资源请求,所以前端的处理就是构造 script 标签发起请求。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>JSONP</title>
</head>
<body>
<!-- 点击的时候调用 fetchJSON -->
<button onclick="fetchJSON()">Fetch</button>
</body>
<script>
// 定义了 fetchJSON 函数。
function fetchJSON() {
// 内部调用 jsonp 函数实现接口的 jsonp 访问。
jsonp('http://localhost:3000/user?').then(data => {
console.log(data);
}).catch(err => {
console.log(err);
})
}
function jsonp(url) {
let $script = document.createElement('script'), // 先构造一个 script 元素
callbackName = `callback_${Date.now()}`; // 先定义回调名称,加时间戳防止缓存
// 返回 promise 对象,方便后续处理
return new Promise((resolve, reject) => {
// 在发起请求之前,先定义好回调函数
window[callbackName] = (res) => {
// 请求结束之后清除全局变量
window[callbackName] = undefined;
// 移除之前挂载的 script 元素
document.head.removeChild($script);
// 清空 $script
$script = undefined;
resolve(res);
};
// 绑定 src,及请求地址
$script.src = `${url}callback=${callbackName}`;
// 绑定 error 处理函数
$script.onerror = err => {
reject(err);
};
// 挂在 script 元素到 head,此时才开始发起请求~
document.head.appendChild($script); // 开始请求
});
}
</script>
</html>
当然上面的代码没有做兼容性处理,在低级浏览器使用时需要做一下处理,但是其他原理是一样的。
JSONP 有什么缺点呢?
大致有两点:
一、安全性问题
JSONP 会从其它域加载 JavaScript 脚本并直接执行,如果 JavaScript 脚本中包含恶意攻击代码,那我们的网站将会受到威胁。所以当我们访问非自己维护的服务器的 JSONP 接口时,需要留心。
二、错误处理
script 标签的 onerror 函数在 HTML5 才定义,并且即使我们定义了 onerror 处理函数,我们也不容易捕捉到错误发生的原因。所以这也是一大缺点,至于具体表现可以单独运行上面的前端代码试试,看看错误发生时(后端服务未启动),前端控制台打印出来的错误对象是什么样的。
浅谈 JSONP的更多相关文章
- 浅谈jsonp
要谈jsonp,首先要弄明白jsonp是什么,它是用来干嘛的.jsonp其实就是我们常用的script标签,用来解决跨域的,只不过这个标签是动态创建的,为啥要动态创建涅. 举个小栗子: 假如我们远程文 ...
- 浅谈JSONP (vue-jsonp组件 XXXtoken:报错处理)
由于同源策略的存在,特别是前后端两个项目存在的情况下,客户端访问服务端必然存在跨域的情况,而使用jsonp,则不存在这个问题. 主要是因为jsonp是在页面中插入一段js代码,而请求返回的也是一段js ...
- 浅谈JSONP 的本质工作原理
json 是一种数据格式jsonp 是一种数据调用的方式. 你可以简单的理解为 带callback的json就是jsonp 话说我们访问一个页面的时候 需要像另一个网站获取部分信息, 这就是所谓的跨域 ...
- 浅谈JSONP 的工作原理
小编最近在工作中经常用到 jsonp 这个东西, 表示之前从来没用过 最近稍微研究了下 当然很多内容来源于网上 收集整理 你懂的 ~~~ 话说我们访问一个页面的时候 需要像另一个网站获取部分信息, ...
- 浅谈Hybrid技术的设计与实现
前言 浅谈Hybrid技术的设计与实现 浅谈Hybrid技术的设计与实现第二弹 浅谈Hybrid技术的设计与实现第三弹——落地篇 随着移动浪潮的兴起,各种APP层出不穷,极速的业务扩展提升了团队对开发 ...
- 浅谈前后端分离与实践 之 nodejs 中间层服务(二)
一.背景 书接上文,浅谈前后端分离与实践(一) 我们用mock服务器搭建起来了自己的前端数据模拟服务,前后端开发过程中只需定义好接口规范,便可以相互进行各自的开发任务.联调的时候,按照之前定义的开发规 ...
- (转)浅谈Hybrid技术的设计与实现
转载地址:https://www.cnblogs.com/yexiaochai/p/4921635.html 前言 浅谈Hybrid技术的设计与实现 浅谈Hybrid技术的设计与实现第二弹 浅谈Hyb ...
- $.ajax()方法详解 ajax之async属性 【原创】详细案例解剖——浅谈Redis缓存的常用5种方式(String,Hash,List,set,SetSorted )
$.ajax()方法详解 jquery中的ajax方法参数总是记不住,这里记录一下. 1.url: 要求为String类型的参数,(默认为当前页地址)发送请求的地址. 2.type: 要求为Str ...
- 浅谈MVC、MVP、MVVM
MVC M: Model 模型——数据 (对于前台而言例如:ajax.jsonp等从后台获取数据的) V: View 视图——表现 ...
随机推荐
- Node.js构建可扩展的Web应用1
<Practical Node.js:Building Real-World Scalable Web Apps>[美]Azat Mardan(电子工业出版社) 安装node.js和NPM ...
- easyUI-textbox回车获取不到正确的textbox值问题
//要先给文本框手动赋值之后才可以获取正确的文本框内容 var t = $('#tt'); t.textbox('textbox').bind('keydown', function(e){ if ( ...
- sql count执行速度测试
要对数据库里面的数据数量进行统计使用,数据库的大概有2000w多的数据.数据库是mysql5.6 用的是远程连接测试 ELECT COUNT(*) 执行语句: select count( *) fro ...
- ASP.NET MVC传递Model到视图的多种方式总结(二)__关于ViewBag、ViewData和TempData的实现机制与区别
在ASP.NET MVC中,视图数据可以通过ViewBag.ViewData.TempData来访问,其中ViewBag 是动态类型(Dynamic),ViewData 是一个字典型的(Diction ...
- centos文件基本操作
centos彻底删除文件夹.文件命令(centos 新建.删除.移动.复制等命令: 1.新建文件夹 mkdir 文件名 新建一个名为test的文件夹在home下 view source1 mkdir ...
- [微信小程序] 微信小程序获取用户定位信息并加载对应城市信息,wx.getLocation,腾讯地图小程序api,微信小程序经纬度逆解析地理信息
因为需要在小程序加个定位并加载对应城市信息 然而小程序自带api目前只能获取经纬度不能逆解析,虽然自己解析方式,但是同时也要调用地图,难道用户每次进小程序还要强行打开地图选择地址才定位吗?多麻烦也不利 ...
- H5新增属性classList
H5新增属性classList h5中新增了一个classList,原生js可以通过它来判断获取dom节点有无某个class. classList是html元素对象的成员,它的使用非常简单,比如 co ...
- ubuntu命令行编译opencv c++项目
ubuntu终端编译opencv c++项目: g++ test.cpp `pkg-config opencv --libs --cflags opencv` -o test
- demo.testfire.net 靶场测试流程记录
demo.testfire.net span::selection, .CodeMirror-line > span > span::selection { background: #d7 ...
- android入门学习-天气预报app(一)
引言 学习<android第一行代码>根据书本开发的天气预报app,主要用于熟练操作android开发(android studio3.0平台). 今天主要分享一下从服务器上获取天气信息, ...