浅谈 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 视图——表现 ...
随机推荐
- vue基于d2-admin的RBAC权限管理解决方案
前两篇关于vue权限路由文章的填坑,说了一堆理论,是时候操作一波了. vue权限路由实现方式总结 vue权限路由实现方式总结二 选择d2-admin是因为element-ui的相关开源项目里,d2-a ...
- HTML的map-area的使用
使用背景 在把设置图转成页面的时候,时常会遇到这种情况:一张小图片上有好多个可以点击的小图标,按常规的处理方法是把这一个一个的小图切出来,然后每个加个a标签进行跳转,但是这样会非常的浪费时间,而且会增 ...
- AVFoundation-视频录制以及拍照
一般如果UI和UE在设计时只要求功能,对相机界面没什么要求的话,个人觉得调用系统相机(UIImagePickerController)就可以满足我们的需求比如照相或者录制视频,但是考虑界面美观性,有时 ...
- Dijkstra Java
https://leetcode.com/problems/network-delay-time/ /* Java program to find a Pair which has maximum s ...
- 解决:java 读取 resources 下面的 json 文件
前言:java 读取 工程下的配置文件,文件类型为 json(*.json),记录一下始终读取不到 json 文件的坑.maven项目 直接上工具类代码 package com.yule.compon ...
- SVN提交小结
在我们用VS进行项目合作开发的过程中,SVN的提交控制是至关重要的,由于版本冲突造成的各种麻烦咱们已经遇到的够多了.所以,总结他们的经验教训,给我们也给其他人做个提醒.下面的第一部分是需要在正式开发之 ...
- HDU2181(KB2-C)
哈密顿绕行世界问题 Time Limit: 3000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total S ...
- 【学习笔记】--- 老男孩学Python,day13 生成器,生成器函数,各种推倒式和生成器表达式
1. 生成器和生成器函数 生成器的本质就是迭代器 生成器的三种创建办法: 1.通过生成器函数 2.通过生成器表达式创建生成器 3.通过数据转换 2. 生成器函数: 函数中包含了yield的就是生成 ...
- 用hmac验证客户端的合法性
服务器端程序 import os import hmac import socket def auth(conn): secret_key = b'rock' rand_b = os.urandom( ...
- 高级功能:很有用的javascript自定义事件
之前写了篇文章<原生javascript实现类似jquery on方法的行为监听>比较浅显,能够简单的使用场景. 这里的自定义事件指的是区别javascript默认的与DOM交互的事件,比 ...