什么是 JSONP,为什么创建它?

我了解 JSON,但不了解 JSONP。 维基百科关于 JSON 的文档 是(曾经)JSONP 的顶级搜索结果。 它是这样说的:

JSONP 或“带填充的 JSON”是 JSON 扩展,其中将前缀指定为调用本身的输入参数。

嗯? 什么叫法? 这对我来说没有任何意义。 JSON 是一种数据格式。 没有叫法。

第二个搜索结果来自一个名叫Remy,他写了这篇关于 JSONP 的文章:

JSONP 是脚本标记注入,将来自服务器的响应传递给用户指定的函数。

我可以理解这一点,但它仍然没有任何意义。


那么什么是 JSONP? 为什么创建它(它解决了什么问题)? 我为什么要使用它?


附录:我刚刚在维基百科上创建了JSONP 的新页面 ; 它现在根据 jvenema 的回答对 JSONP 进行了清晰而全面的描述.

JSONP is really a simple trick to overcome the XMLHttpRequest same domain policy. (As you know one cannot send AJAX (XMLHttpRequest) request to a different domain.)

So - instead of using XMLHttpRequest we have to use script HTML tags, the ones you usually use to load js files, in order for js to get data from another domain. Sounds weird?

Thing is - turns out script tags can be used in a fashion similar to XMLHttpRequest! Check this out:

script = document.createElement('script');
script.type = 'text/javascript';
script.src = 'http://www.someWebApiServer.com/some-data';

You will end up with a script segment that looks like this after it loads the data:

<script>
{['some string 1', 'some data', 'whatever data']}
</script>

However this is a bit inconvenient, because we have to fetch this array from script tag. So JSONP creators decided that this will work better(and it is):

script = document.createElement('script');
script.type = 'text/javascript';
script.src = 'http://www.someWebApiServer.com/some-data?callback=my_callback';

Notice the my_callback function over there? So - when JSONP server receives your request and finds callback parameter - instead of returning plain js array it'll return this:

my_callback({['some string 1', 'some data', 'whatever data']});

See where the profit is: now we get automatic callback (my_callback) that'll be triggered once we get the data.
That's all there is to know about JSONP: it's a callback and script tags.

NOTE: these are simple examples of JSONP usage, these are not production ready scripts.

Basic JavaScript example (simple Twitter feed using JSONP)

<html>
    <head>
    </head>
    <body>
        <div id = 'twitterFeed'></div>
        <script>
        function myCallback(dataWeGotViaJsonp){
            var text = '';
            var len = dataWeGotViaJsonp.length;
            for(var i=0;i<len;i++){
                twitterEntry = dataWeGotViaJsonp[i];
                text += '<p><img src = "' + twitterEntry.user.profile_image_url_https +'"/>' + twitterEntry['text'] + '</p>'
            }
            document.getElementById('twitterFeed').innerHTML = text;
        }
        </script>
        <script type="text/javascript" src="http://twitter.com/status/user_timeline/padraicb.json?count=10&callback=myCallback"></script>
    </body>
</html>

Basic jQuery example (simple Twitter feed using JSONP)

<html>
    <head>
        <script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.6.2/jquery.min.js"></script>
        <script>
            $(document).ready(function(){
                $.ajax({
                    url: 'http://twitter.com/status/user_timeline/padraicb.json?count=10',
                    dataType: 'jsonp',
                    success: function(dataWeGotViaJsonp){
                        var text = '';
                        var len = dataWeGotViaJsonp.length;
                        for(var i=0;i<len;i++){
                            twitterEntry = dataWeGotViaJsonp[i];
                            text += '<p><img src = "' + twitterEntry.user.profile_image_url_https +'"/>' + twitterEntry['text'] + '</p>'
                        }
                        $('#twitterFeed').html(text);
                    }
                });
            })
        </script>
    </head>
    <body>
        <div id = 'twitterFeed'></div>
    </body>
</html>


JSONP stands for JSON with Padding. (very poorly named technique as it really has nothing to do with what most people would think of as “padding”.)

JSONP works by constructing a “script” element (either in HTML markup or inserted into the DOM via JavaScript), which requests to a remote data service location. The response is a javascript loaded on to your browser with name of the pre-defined function along with parameter being passed that is tht JSON data being requested. When the script executes, the function is called along with JSON data, allowing the requesting page to receive and process the data.

For Further Reading Visit: https://blogs.sap.com/2013/07/15/secret-behind-jsonp/

client side snippet of code

    <!DOCTYPE html>
    <html lang="en">
    <head>
     <title>AvLabz - CORS : The Secrets Behind JSONP </title>
     <meta charset="UTF-8" />
    </head>
    <body>
      <input type="text" id="username" placeholder="Enter Your Name"/>
      <button type="submit" onclick="sendRequest()"> Send Request to Server </button>
    <script>
    "use strict";
    //Construct the script tag at Runtime
    function requestServerCall(url) {
      var head = document.head;
      var script = document.createElement("script");
  script.setAttribute("src", url);
  head.appendChild(script);
  head.removeChild(script);
}

//Predefined callback function    
function jsonpCallback(data) {
  alert(data.message); // Response data from the server
}

//Reference to the input field
var username = document.getElementById("username");

//Send Request to Server
function sendRequest() {
  // Edit with your Web Service URL
  requestServerCall("http://localhost/PHP_Series/CORS/myService.php?callback=jsonpCallback&amp;message="+username.value+"");
}    

</script>
</body>
</html>

Server side piece of PHP code

<?php
    header("Content-Type: application/javascript");
    $callback = $_GET["callback"];
    $message = $_GET["message"]." you got a response from server yipeee!!!";
    $jsonResponse = "{\"message\":\"" . $message . "\"}";
    echo $callback . "(" . $jsonResponse . ")";
?>
</div>

JSONP is a great away to get around cross-domain scripting errors. You can consume a JSONP service purely with JS without having to implement a AJAX proxy on the server side.

You can use the b1t.co service to see how it works. This is a free JSONP service that alllows you to minify your URLs. Here is the url to use for the service:

http://b1t.co/Site/api/External/MakeUrlWithGet?callback=[resultsCallBack]&url=[escapedUrlToMinify]

For example the call, http://b1t.co/Site/api/External/MakeUrlWithGet?callback=whateverJavascriptName&url=google.com

would return

whateverJavascriptName({"success":true,"url":"http://google.com","shortUrl":"http://b1t.co/54"});

And thus when that get's loaded in your js as a src, it will automatically run whateverJavascriptName which you should implement as your callback function:

function minifyResultsCallBack(data)
{
    document.getElementById("results").innerHTML = JSON.stringify(data);
}

To actually make the JSONP call, you can do it about several ways (including using jQuery) but here is a pure JS example:

function minify(urlToMinify)
{
   url = escape(urlToMinify);
   var s = document.createElement('script');
   s.id = 'dynScript';
   s.type='text/javascript';
   s.src = "http://b1t.co/Site/api/External/MakeUrlWithGet?callback=resultsCallBack&url=" + url;
   document.getElementsByTagName('head')[0].appendChild(s);
}

A step by step example and a jsonp web service to practice on is available at: this post

A simple example for the usage of JSONP.

client.html

    <html>
    <head>
   </head>
     body>
&lt;input type="button" id="001" onclick=gO("getCompany") value="Company"  /&gt;
&lt;input type="button" id="002" onclick=gO("getPosition") value="Position"/&gt;
&lt;h3&gt;
&lt;div id="101"&gt;

&lt;/div&gt;
&lt;/h3&gt;

&lt;script type="text/javascript"&gt;

var elem=document.getElementById("101");

function gO(callback){

script = document.createElement('script');
script.type = 'text/javascript';
script.src = 'http://localhost/test/server.php?callback='+callback;
elem.appendChild(script);
elem.removeChild(script);


}

function getCompany(data){

var message="The company you work for is "+data.company +"&lt;img src='"+data.image+"'/   &gt;";
elem.innerHTML=message;

}

function getPosition(data){
var message="The position you are offered is "+data.position;
elem.innerHTML=message;
}
&lt;/script&gt;
&lt;/body&gt;
&lt;/html&gt;

server.php

  <?php
$callback=$_GET["callback"];
echo $callback;

if($callback=='getCompany')
$response="({\"company\":\"Google\",\"image\":\"xyz.jpg\"})";

else
$response="({\"position\":\"Development Intern\"})";
echo $response;

?&gt;    

</div>