• JSONP浅析


    DEMO : JSONP示例

    为什么使用JSONP

    JSONP和JSON是不一样的。JSON(JavaScript Object Notation)是一种基于文本的数据交换方式,或者叫做数据描述格式。而JSONP(JSON with Padding)是一种方式或者说非强制性协议。它是为了解决某个难题而产生的一种技术方式。

    为什么会用到JSONP呢?

    我们平时在用ajax请求服务端数据时,一般是这么写的:

    $.ajax({
    	type: "get",
    	url: "getData.php",
    	dataType: "json",
    	success: function (data, textStatus, jqXHR) {
    		console.log(data);
    	},
    	error: function (XMLHttpRequest, textStatus, errorThrown) {
    		alert('fail');
    	}
    });
    

    这是一段很普通的基于jQuery的AJAX请求,不会有什么问题。注意到:url里是getData.php,说明这个文件url是基于当前服务器的,例如可能是localhost,也就是前端发出的请求来源是localhost,后端肯定也是localhost。他们俩是在同一个域名下。当然,平时我们也不会特别注意。

    这时候,假如这个url变成其它服务器上的地址,例如:'http://apis.juhe.cn/mobile/get?phone=13429667914&key=',我们再把请求发送出去,会发现出问题了。大家可以手动写个示例看看。

    DEMO: 为什么使用jsonp?

    出什么问题了?被限制请求了!

    XMLHttpRequest cannot load http://apis.juhe.cn/mobile/get?phone=13429667914&key=. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://demo.52fhy.com' is therefore not allowed access.
    

    也就是请求来源与服务器不是同一个域名,不允许访问。这就是浏览器同源策略

    所谓"同源"指的是"三个相同":
    
    协议相同
    域名相同
    端口相同
    
    举例来说,http://www.example.com/dir/page.html这个网址,协议是http://,域名是www.example.com,端口是80(默认端口可以省略)。它的同源情况如下。
    http://www.example.com/dir2/other.html:同源
    http://example.com/dir/other.html:不同源(域名不同)
    http://v2.www.example.com/dir/other.html:不同源(域名不同)
    http://www.example.com:81/dir/other.html:不同源(端口不同)
    

    同源政策规定,AJAX请求只能发给同源的网址,否则就报错。

    那么,这时候该怎么办呢?我就是想通过js请求对方服务器上的资源!

    除了架设服务器代理(浏览器请求同源服务器,再由后者请求外部服务),有三种方法规避这个限制。

    • JSONP
    • WebSocket
    • CORS

    本文只对JSONP作介绍。

    如何使用JSONP

    首先前端这边代码得改改,假设先不用jQuery:

    <script type="text/javascript">
    //跨域发送HTTP请求,从服务端获取数据,callback指定回调函数名称
    var url = 'http://demo.52fhy.com/jsonp/handJsonp.php?callback=handler';
    
    function getHello() {
        var script = document.createElement('script');
        script.setAttribute('src', url);
        document.querySelector("head").appendChild(script);
    }
    // 处理函数
    function handler(data) {
        console.log(data);
        // our code here...
    }
    </script>
    
    <input type="button" value="发送跨域HTTP请求,获取数据" onclick="getHello()" />
    

    后端服务器也要改改。

    例如,在PHP语言中,后端服务器在API里返回JSON数据,一般是这么写的:

    $data = array('name' => '52fhy', 'age' => '22');
    echo json_encode($data);
    exit;
    

    这里需要改成:

    $data = array('name' => '52fhy', 'age' => '22');
    handJsonp($data);
    
    //处理jsonp
    function handJsonp($data){
    	$callback = $_GET['callback'] ? : 'callback'; //默认使用callback
    	echo sprintf("%s(%s)", $callback, json_encode($data));
    	exit;
    }
    

    一旦请求成功,服务端输出了:

    handler({name: "52fhy", age: "22"});
    

    这时候浏览器就要响应了,找到handler()方法并执行,恰好,我们提前定义好了handler()方法。

    这里为什么服务端没有限制访问呢?原因是我们通过动态添加了个:

    <script src="http://demo.52fhy.com/jsonp/handJsonp.php?callback=handler"></script>
    

    它的基本思想是,网页通过添加一个<script>元素,向服务器请求JSON数据,这种做法不受同源政策限制;服务器收到请求后,将数据放在一个指定名字的回调函数里传回来。

    这种方法就是JSONP。

    使用jQuery/Zepto

    如果使用jQuery/Zepto,JSONP的方式将会和发送非跨域请求那样简单。
    示例:

    $.ajax({
    	type: "get",
    	async: false,
    	url: "http://demo.52fhy.com/jsonp/handJsonp.php",
    	dataType: "jsonp",
    	success: function (data, textStatus, jqXHR) {
    		console.log(data);
    	},
    	error: function (XMLHttpRequest, textStatus, errorThrown) {
    		alert('fail');
    		console.log(XMLHttpRequest, textStatus, errorThrown);
    	}
    });
    

    只需要将dataType设置成jsonp就可以进行跨域请求了。在success里我们能接收到来自服务端的响应:

    {name: "52fhy", age: "22"}
    

    DEMO: Zepto jsonp demo

    通过console控制台,可以看到请求信息:

    Request URL:http://demo.52fhy.com/jsonp/handJsonp.php?_=1460899828609&callback=jsonp1
    Request Method:GET
    Status Code:200 OK
    

    jQuery/Zepto为我们封装好了回调函数,一般情况下不需要我们单独去写,如果你不想在success中处理,想单独写处理函数,那么可以通过设置这2个参数来实现:

    jsonp: "callback",//传递给服务端的回调函数名称参数,如果不设置此项,则默认是"callback"
    
    jsonpCallback: "handler",//传递给服务端的回调函数名称,如果不设置此项,jQuery默认是形如"jQuery18308539637457579374_1460898291266"的由jQuery自动生成的函数名称,Zepto默认是`jsonp1`
    

    如果是zepto,可以在success回调里面使用console.log(jsonp1),发现jsonp1()方法是存在的。

    需要注意的是:

    1.JSONP虽然看起来很像一般的ajax请求,但其原理不同,JSONP是通过<script>标签的动态加载来实现的跨域请求,而一般的ajax请求是通过XMLHttpRequest对象进行;
    2.JSONP不是一种标准协议,其安全性和稳定性都不如 W3C 推荐的 CORS;
    3.JSONP不支持POST请求,即使把请求类型设置为post,其本质上仍然是一个get请求。

    参考

    1、浏览器同源政策及其规避方法 - 阮一峰的网络日志
    http://www.ruanyifeng.com/blog/2016/04/same-origin-policy.html
    2、跨域资源共享 CORS 详解 - 阮一峰的网络日志
    http://www.ruanyifeng.com/blog/2016/04/cors.html
    3、说说JSON和JSONP,也许你会豁然开朗_知识库_博客园
    http://kb.cnblogs.com/page/139725/
    4、跨域解决方案二:使用JSONP实现跨域 - choon - 博客园
    http://www.cnblogs.com/choon/p/5393682.html
    5、JSON-P: Safer cross-domain Ajax with JSON-P/JSONP
    http://json-p.org/

  • 相关阅读:
    浏览器 cookie
    c# 委托
    并不对劲的loj3106:p5339:[TJOI2019]唱、跳、rap 和篮球
    并不对劲的loj3095:p5329:[SNOI2019]字符串
    并不对劲的CF1365D&E&F: Solve The Maximum Subsequence Again
    并不对劲的loj3123:p5404[CTS2019]重复
    并不对劲的loj3046:p5327:[ZJOI2019]语言
    并不对劲的loj3115:p5362:[SDOI2019]连续子序列
    并不对劲的loj3113:p5360:[SDOI2019]热闹的聚会与尴尬的聚会
    并不对劲的bzoj2521:p5039:[SHOI2010]最小生成树
  • 原文地址:https://www.cnblogs.com/52fhy/p/5402210.html
Copyright © 2020-2023  润新知