关于利用JSONP进行跨域请求

跨域请求据我所知有两种方案,其一是在服务器端PHP页面设置响应头,称为“CORS跨域”(全称cross-origin resource sharing跨域资源共享),其方法就是在PHP页面中添加一行代码,代码如下:

<?php
	header('Access-Control-Allow-Origin:*');
	//这里的*号也可以改成你所授权的需要请求当前域资源的域的路径地址。
	------------
?>

关于第一种方案本文在这里不再赘述,以下主要说明第二种方案-----JSONP。
jsonp 全称是JSON with Padding,是为了解决跨域请求资源而产生的解决方案。很多时候我们需要在客户端获取服务器数据进行操作,一般我们会使用ajax+webservice做此事,但是如果我们希望获取的数据和当前页面并不是一个域,著名的同源策略(不同域的客户端脚本在没明确授权的情况下,不能读写对方的资源)会因为安全原因拒绝请求,也就是我们不能向其它域直接发送请求以获取资源。

JSONP的原理

JSONP 是一种请求一段 JS 脚本,把执行这段脚本的结果当做数据的玩法。

所以,你能 POST 一段通过 script 标签引入的脚本吗?
(如果看过 JSONP 库的源码就知道,常见的实现代码其实就是 document.createElement(‘script’) 生成一个 script 标签,然后插 body 里而已。在这里根本没有设置请求格式的余地)。

所以JSONP的实现原理就是创建一个script标签, 再把需要请求的api地址放到src里. 这个请求只能用GET方法, 不可能是POST。
注:利用jsonp进行跨域请求的本质:动态创建script标签+动态指定回调函数名称
以下是模仿jquery中封装jsonp跨域请求的代码,顺便体会以下jsonp跨域的本质。

    <script>
        function test(result){
            console.log(result);
        }
        // 定义自己的ajax
        var $ ={
            ajax:function(option){
                var url = option.url;
                var callback = option.callback;
                var dataType = option.dataType;
                // 当dataType为jsonp的时候我们需要发起一个跨域请求
                if(dataType == "jsonp"){
                    // 1.创建一个script标签
                    var script = document.createElement("script");
                    // 2.为script标签设置src属性,同时在src中传入之后需要挨靠的函数名称
                    script.src = url +"?callback="+callback;
                    // 3.将生成的script标签添加到页面结构中
                    document.body.appendChild(script);
                }
            }
        };
        // 我也想通过$.ajax的方式发送请求,当我传入dataType:jsonp的时候,希望能够发送一个跨域请求
        $.ajax({
            type:'get',
            url:'http://wpw.com/demo.php',
            callback:'test',
            dataType:"jsonp"
        });
        // 1.主要是利用了script标签的天然的跨域特性来发送请求
        // 2.它的实现方式:在发送请求的时候传递一个函数名称给后台,后台返回数据的时候会返回这个函数的调用形式,并且在()中拼接参数
        // 3.ajax和jsonp的本质不一样。ajax的核心是通过XMLHttpRequest来发送请求,而jsonp是通过script标签来实现请求的发送
    </script>

在localhost域上有一个books.php,里面包含脚本对test.com域的books.php发送get请求,希望获取其book列表资源,这就是一个跨域请求资源

$.ajax({
            type:'get',
            url:'http://test.com/books.php'
        });

页面会报一个这样的错误:
** XMLHttpRequest cannot load http://test.com/books.php. Origin http://localhost is not allowed by Access-Control-Allow-Origin.**
jsonp是为了解决这个问题出现的。

虽然有同源策略的限制,但是并不是HTML上所有资源都必须是同一个域的,我们常见的页面为了节省流量或加载速度采用Google或微软的 jQuery CDN,在页面上我们可以这样写就可以引用jQuery了

<script  type="text/javascript"
    src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js">
</script>

iframe、img、style、script等元素的src属性可以直接向不同域请求资源,jsonp正式利用script标签跨域请求资源的简单实现
localhost的books.php希望获得域test.com的books列表,在域test.com内book列表存储在books.xml中

test.com/books.xml

<?xml version="1.0"?>
<books>
    <book name="JavaScript: The Defiitive Guide" publisher="O'Reilly Media, Inc.">
        <author>David Flanagan</author>
    </book>
    <book name="PHP anf MySQL Web Development" publisher="Perason Education">
        <author>Luke Welling</author>
        <author>Laura Thomson</author>
    </book>
    <book name="HTTP: The Defiitive Guide" publisher="O'Reilly Media, Inc.">
        <author>David Courley</author>
        <author>Brian Totty</author>
    </book>
</books>

明显JavaScript不能直接获取books.xml,在test.com中需要有一个机制将xml转化为json(这也就是为什么叫jsonp,其实和ajax一样,返回的数据不一定是json格式,只是json很好用),并动态拼接一条javascript调用语句返回,这个例子中直接使用php页面拼接

test.com/bookservice.php

<?php
    $path=$_SERVER["DOCUMENT_ROOT"].'/books.xml';
    $json=json_encode(simplexml_load_file($path));

    $callbackFn=$_GET['callback'];
    echo "$callbackFn($json);";
?>

这样首先把xml文件内容转换成一个json对象

{"book":[
{"@attributes":{"name":"JavaScript: The Defiitive Guide","publisher":"O'Reilly Media, Inc."},"author":"David Flanagan"},
{"@attributes":{"name":"PHP anf MySQL Web Development","publisher":"Perason Education"},"author":["Luke Welling","Laura Thomson"]},
{"@attributes":{"name":"HTTP: The Defiitive Guide","publisher":"O'Reilly Media, Inc."},"author":["David Courley","Brian Totty"]}
]}

然后拼接为一条javascript语句交给localhost去处理,当然test.com并不知道应该拼接的方法名叫什么,需要localhost在发送请求的时候在url中传入一个叫callback(这个也随便,两边同步就行)的参数指明。看看localhost怎么发送请求吧

localhost/books.php

<!DOCTYPE html>
<html>
<head>
    <title>Books</title>
    <?php include('/components/headerinclude.php');?></head>
    <style type="text/css">
        .book-title
        {
            font-size: 15px;
            font-weight:bold;
            margin-top:6px;
        }
        .book-info
        {
            color:#ccc;
            font-style:italic;
            border-bottom:dashed 1px #ccc;
        }
    </style>
</head>
<body>
    <div style="margin:20px;">
        <div style="font-size:16px;font-weight:bold;">Books</div>
        <div id="books">
        </div>
    </div>
</body>
</html>

我们希望在id为books的div中展示所有book,先添加一个用以显示book的javascript函数,也就是获取到数据后的回调函数,结合上面拼接的json格式可以这么写

function displayBooks(books){
            var books=books.book;
            var booksContainer=document.getElementById('books');
            for(var i=0;i<books.length;i++){
                var tmp=Array();
                tmp.push('<div class="book-title">'+books[i]['@attributes'].name+'</div>');
                tmp.push('<div class="book-info">');
                tmp.push('<div>Publisher: '+books[i]['@attributes'].publisher+'</div>');
                tmp.push('<div>Author(s): ');
                if(typeof books[i].author=='string'){
                    tmp.push(books[i].author);
                }else{
                    var authors=books[i].author;
                    for(var j=0;j<authors.length;j++){
                        tmp.push(authors[j]+'&emsp;');
                    }
                }
                tmp.push('</div>'); //end of author
                tmp.push('</div>'); //end of book info
                booksContainer.innerHTML+=tmp.join('');
            }
        }

然后是关键的jsonp请求的方法了

function getBooks(){
            var script=document.createElement('script');
            script.setAttribute('type','text/javascript');
            script.setAttribute('src','http://test.com/bookservice.php?callback=displayBooks');
            document.body.appendChild(script);
        }

        getBooks();

在getbooks()方法中动态创建了一个script标签,设置其src为test.com提供的获取数据的service接口并传入回调函数,这样我们可以看看页面的反应,在Chrome控制台下可以看到这条请求

Books 这样我们就可以在localhost下获取test.com的books了

在这里插入图片描述

jquery实现

在jquery中也有对jsonp的封装,不过jquery把其放到了ajax中,不明白为什么,毕竟这东西和ajax不太一样。以下是jQuery版的

function getBooks(){
            $.ajax({
                type:'get',
                url:'http://test.com/bookservice.php',
                dataType:'jsonp',
                jsonp:'callback',
                jsonpCallback:'displayBooks'
            });
        }

看起来完全一样,不过方便了很多,不用自己创建script标签了,指明dataType为jsonp,回调函数不放在url内了,而是使用两个参数分别指明。

JSONP的缺点

虽然JSONP在跨域ajax请求方面有很强的能力,但是它也有一些缺陷。
首先,它没有关于JSONP调用的错误处理,一旦回调函数调用失败,浏览器会以静默失败的方式处理。
其次,它只支持GET请求,这是由于该技术本身的特性所决定的。因此,对于一些需要对安全性有要求的跨域请求,JSONP的使用需要谨慎一点了。
当然使用jsonp会在一定程度上造成安全性问题,如果请求的站点不是信任站点,那么可能会在返回的方法调用中包含一些恶意代码。所以尽量向信任的站点发送请求。另外xss也经常会利用jsonp向站点注入恶意代码。
由于JSONP对于老浏览器兼容性方面比较良好,因此,对于那些对IE8以下仍然需要支持的网站来说,仍然被广泛应用。不过,针对高级浏览器,建议还是使用文章开头介绍的CORS 方法。

JSONP异步的理解

ajax其实用它主要就是因为它的异步,它去服务器端取数据,html页面可以在它取数据期间接着走下面的,等ajax取回数据了再处理;
jsonp也是利用回调函数,去指定服务器上取数据的时候不用等待,等它取回来了直接用回调函数一处理,也达到了异步的效果呀

JSONP异步的例子

你可以试试写个内容如下的a.js

alert('a')

然后

var scn = document.createElement('script');
scn.src = 'a.js';
document.getElementsByTagName('head')[0].appendChild(scn);
alert('b');

看看执行顺序是先a后b ,还是先b后a ,就知道是同步异步
你还可以在以上代码中加入async 来看看是否造成差异
执行结果是先b后a

ajax与jsonp的异同:

1、ajax和jsonp这两种技术在调用方式上”看起来”很像,目的也一样,都是请求一个url,然后把服务器返回的数据进行处理,因此jquery和ext等框架都把jsonp作为ajax的一种形式进行了封装。

2、但ajax和jsonp其实本质上是不同的东西。ajax的核心是通过XmlHttpRequest获取非本页内容,而jsonp的核心则是动态添加

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值