Что такое JSONP и почему он был создан?

2131

Я понимаю JSON, но не JSONP. Документ Википедии о JSON является (был) лучшим результатом поиска для JSONP. Это говорит это:

JSONP или «JSON with padding» - это расширение JSON, префикс которого указан в качестве входного аргумента самого вызова.

А? Какой звонок? Это не имеет никакого смысла для меня. JSON - это формат данных. Там нет звонка.

Результат поиска второго из какого - то парня по имени Реми , который пишет это о JSONP:

JSONP - это инъекция тега сценария, передающая ответ от сервера в указанную пользователем функцию.

Я могу это понять, но это все еще не имеет никакого смысла.


Так что же такое JSONP? Почему он был создан (какую проблему он решает)? И зачем мне это использовать?


Приложение : Я только что создал новую страницу для JSONP в Википедии; теперь он содержит четкое и подробное описание JSONP, основанное на ответе jvenema .

Cheeso
источник
29
Для записи НЕ используйте JSONP, если вы не доверяете серверу, с которым говорите на 100%. Если это скомпрометировано, ваша веб-страница будет скомпрометирована.
ninjagecko
7
Также обратите внимание, что JSONP может быть взломан, если он не реализован правильно.
Pacerier
3
Я хотел бы поблагодарить автора JSONP, который изложил философию, стоящую за ним: архив Боба Ипполито на JSONP . Он представляет JSONP как «новую методологию, независимую от технологии, для метода тегов сценария для междоменной выборки данных».
harshvchawla

Ответы:

2048

Это на самом деле не слишком сложно ...

Допустим, вы находитесь на домене example.comи хотите сделать запрос на домен example.net. Для этого вам нужно пересечь доменные границы, нет-нет в большинстве браузерных стран.

Единственный элемент, который обходит это ограничение - это <script>теги. Когда вы используете тег сценария, ограничение домена игнорируется, но при нормальных обстоятельствах вы ничего не можете сделать с результатами, сценарий просто оценивается.

Введите JSONP. Когда вы отправляете запрос на сервер с поддержкой JSONP, вы передаете специальный параметр, который немного сообщает серверу о вашей странице. Таким образом, сервер может красиво обернуть свой ответ так, как может справиться ваша страница.

Например, скажем, сервер ожидает параметр, вызываемый callbackдля включения его возможностей JSONP. Тогда ваш запрос будет выглядеть так:

http://www.example.net/sample.aspx?callback=mycallback

Без JSONP это может вернуть некоторый базовый объект JavaScript, например так:

{ foo: 'bar' }

Однако в JSONP, когда сервер получает параметр «обратного вызова», он оборачивает результат немного по-другому, возвращая что-то вроде этого:

mycallback({ foo: 'bar' });

Как видите, теперь он будет вызывать указанный вами метод. Итак, на вашей странице вы определяете функцию обратного вызова:

mycallback = function(data){
  alert(data.foo);
};

И теперь, когда скрипт загружен, он будет оценен, и ваша функция будет выполнена. Вуаля, междоменные запросы!

Стоит также отметить одну важную проблему с JSONP: вы теряете контроль над запросом. Например, нет «хорошего» способа вернуть правильные коды ошибок. В результате вы используете таймеры для мониторинга запроса и т. Д., Что всегда немного подозрительно. Предложение для JSONRequest является отличным решением, позволяющим создавать междоменные сценарии, поддерживать безопасность и обеспечивать надлежащий контроль над запросом.

В эти дни (2015) CORS является рекомендуемым подходом против JSONRequest. JSONP по-прежнему полезен для поддержки старых браузеров, но с учетом последствий для безопасности, если у вас нет выбора, CORS - лучший выбор.

jvenema
источник
180
Обратите внимание, что использование JSONP имеет некоторые последствия для безопасности. Поскольку JSONP действительно является javascript, он может делать все остальное, что может делать javascript, поэтому вам нужно доверять поставщику данных JSONP. Я написал сообщение в блоге об этом здесь: erlend.oftedal.no/blog/?blogid=97
Erlend
72
Действительно ли в JSONP есть какие-то новые аспекты безопасности, которых нет в теге <script>? С помощью тега script браузер неявно доверяет серверу доставку неопасного Javascript, который браузер слепо оценивает. JSONP меняет этот факт? Кажется, нет.
Cheeso
23
Нет, это не так. Если вы доверяете Javascript, то же самое относится и к JSONP.
Jvenema
15
Стоит отметить, что вы можете немного повысить безопасность, изменив способ возврата данных. Если вы возвращаете сценарий в истинном формате JSON, например, mycallback ('{"foo": "bar"}') (обратите внимание, что параметр теперь является строкой), то вы можете самостоятельно проанализировать данные вручную, чтобы "очистить" их перед оценка.
Jvenema
8
CURL - это решение на стороне сервера, а не на стороне клиента. Они служат двум различным целям.
Jvenema
712

JSONP - это действительно простая уловка для преодоления политики домена XMLHttpRequest . (Как вы знаете, никто не может отправить AJAX (XMLHttpRequest) запрос в другой домен.)

Итак, вместо использования XMLHttpRequest мы должны использовать HTML-теги сценариев , которые вы обычно используете для загрузки файлов js, чтобы js мог получать данные из другого домена. Звучит странно?

Дело в том, что теги сценариев можно использовать аналогично XMLHttpRequest ! Проверь это:

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

Вы получите сегмент скрипта, который выглядит следующим образом после загрузки данных:

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

Однако это немного неудобно, потому что мы должны извлечь этот массив из тега script . Поэтому создатели JSONP решили, что это будет работать лучше (и это так):

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

Заметьте функцию my_callback там? Итак, когда JSONP- сервер получит ваш запрос и найдет параметр обратного вызова, вместо возврата обычного массива js он вернет следующее:

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

Посмотрите, где прибыль: теперь мы получаем автоматический обратный вызов (my_callback), который будет запущен, как только мы получим данные.
Это все, что нужно знать о JSONP : это обратный вызов и теги сценария.

ПРИМЕЧАНИЕ: это простые примеры использования JSONP, это не готовые к работе скрипты.

Базовый пример JavaScript (простой канал Twitter с использованием 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>

Базовый пример jQuery (простой канал Twitter с использованием 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 означает JSON с отступом . (очень плохо названная техника, поскольку она на самом деле не имеет ничего общего с тем, что большинство людей считает «заполнением».)

Тот парень
источник
34
Спасибо за объяснение тега сценария. Я не смог понять, как JSONP обошел политику междоменной безопасности. После объяснения я чувствую себя немного глупо, чтобы упустить момент ...
Эдуард
13
Это очень хороший дополнительный ответ на ответ jvenema - я не понимал, почему обратный вызов был необходим, пока вы не указали, что в противном случае к данным json нужно будет обращаться через элемент script.
Мэтт
5
Спасибо за такое ясное объяснение. Я хотел бы, чтобы мои учебники для колледжа были написаны такими людьми, как вы :)
hashbrown
1
Хорошая экспликация, а не предыдущая. Конечно, ваш отрывок "те, которые вы обычно используете для загрузки файлов js, чтобы js мог получить данные из другого домена. Звучит странно?" это также сенсационное сообщение для меня. Пример кода в очень выдающемся.
SIslam
отступы не должны быть буквальными. это своего рода метафора. так что это может означать «JSON с некоторыми пробелами». LOL
marvinIsSacul
48

JSONP работает путем создания элемента «script» (либо в разметке HTML, либо вставляется в DOM через JavaScript), который запрашивает расположение удаленной службы данных. Ответ представляет собой JavaScript, загруженный в ваш браузер с именем предопределенной функции и передаваемым параметром, который запрашивает данные JSON. Когда скрипт выполняется, функция вызывается вместе с данными JSON, что позволяет запрашивающей странице получать и обрабатывать данные.

Для дальнейшего чтения посетите: https://blogs.sap.com/2013/07/15/secret-behind-jsonp/

фрагмент кода на стороне клиента

    <!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&message="+username.value+"");
    }    

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

Серверная часть кода PHP

<?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 . ")";
?>
Аджайн Вивек
источник
3
ссылка вверху всего 404с сейчас
Кевин Бил
Содержимое этой ссылки теперь доступно по адресу http://scn.sap.com/community/developer-center/front-end/blog/2013/07/15/secret-behind-jsonp .
ᴠɪɴᴄᴇɴᴛ
42

Потому что вы можете попросить сервер добавить префикс к возвращенному объекту JSON. Например

function_prefix(json_object);

для того, чтобы браузер eval «встроил» строку JSON в качестве выражения. Этот трюк позволяет серверу «вставлять» код JavaScript непосредственно в браузер клиента, что позволяет обойти ограничения «одного источника».

Другими словами, вы можете добиться междоменного обмена данными .


Обычно XMLHttpRequestне разрешает междоменный обмен данными напрямую (нужно пройти через сервер в одном домене), тогда как:

<script src="some_other_domain/some_data.js&prefix=function_prefix> `можно получить доступ к данным из домена, отличного от источника.


Также стоит отметить: несмотря на то, что сервер должен рассматриваться как «доверенный» перед попыткой такого рода «уловки», побочные эффекты возможного изменения формата объекта и т. Д. Могут быть ограничены. Если function_prefixдля получения объекта JSON используется (т. Е. Правильная функция js), упомянутая функция может выполнить проверки перед принятием / дальнейшей обработкой возвращенных данных.

jldupont
источник
«добавить префикс» сбивает с толку :)
jub0bs
19

JSONP отлично подходит для обхода ошибок междоменного скриптинга. Вы можете использовать службу JSONP исключительно с JS без необходимости в реализации прокси AJAX на стороне сервера.

Вы можете использовать сервис b1t.co, чтобы увидеть, как он работает. Это бесплатный сервис JSONP, который позволяет минимизировать ваши URL. Вот URL для использования в сервисе:

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

Например, вызов http://b1t.co/Site/api/External/MakeUrlWithGet?callback=whwhatJavascriptName&url=google.com.

вернется

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

И, таким образом, когда этот get загружается в ваш js как src, он автоматически запускает независимо от JavascriptName, которое вы должны реализовать в качестве функции обратного вызова:

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

Чтобы фактически сделать вызов JSONP, вы можете сделать это несколькими способами (включая использование jQuery), но вот чистый пример JS:

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);
}

Пошаговый пример и веб-сервис jsonp для практики доступны по адресу: этот пост

dardawk
источник
2
Спасибо за публикацию вашего ответа! Обратите внимание, что вы должны опубликовать основные части ответа здесь, на этом сайте, иначе ваша публикация может быть удалена. См. FAQ, где упоминаются ответы, которые «едва ли больше, чем ссылка». Вы по-прежнему можете включить ссылку, если хотите, но только в качестве «ссылки». Ответ должен стоять самостоятельно, без необходимости ссылки.
Тарын
14

Простой пример использования JSONP.

client.html

    <html>
    <head>
   </head>
     body>


    <input type="button" id="001" onclick=gO("getCompany") value="Company"  />
    <input type="button" id="002" onclick=gO("getPosition") value="Position"/>
    <h3>
    <div id="101">

    </div>
    </h3>

    <script type="text/javascript">

    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 +"<img src='"+data.image+"'/   >";
    elem.innerHTML=message;
}

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

server.php

  <?php

    $callback=$_GET["callback"];
    echo $callback;

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

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

    ?>    
Сарат Джозеф
источник
8

Прежде чем понимать JSONP, вам необходимо знать формат JSON и XML. В настоящее время наиболее часто используемым форматом данных в Интернете является XML, но XML очень сложен. Это делает пользователей неудобными для обработки встроенных в веб-страницах.

Чтобы JavaScript мог легко обмениваться данными, даже в качестве программы обработки данных, мы используем формулировку в соответствии с объектами JavaScript и разработали простой формат обмена данными, который является JSON. JSON можно использовать как данные или как программу JavaScript.

JSON может быть непосредственно встроен в JavaScript, используя их, вы можете напрямую выполнять определенную программу JSON, но из-за ограничений безопасности механизм песочницы браузера отключает междоменное выполнение кода JSON.

Чтобы JSON можно было передать после выполнения, мы разработали JSONP. JSONP обходит пределы безопасности браузера с помощью функции обратного вызова JavaScript и тега <script>.

Вкратце, он объясняет, что такое JSONP, какую проблему он решает (когда его использовать).

Маркус Торнтон
источник
4
Я опроверг это, потому что не верю утверждению, что XML был наиболее часто используемым форматом данных в сети в декабре 15 года.
RobbyD
Это все еще не спрашивает, почему jsonp используется вместо json. Откуда все эти ограничения безопасности? Почему мы можем использовать jsonp, но не json для междоменных доменов?
Мерунас Гринкалайтис
6

TL; DR

JSONP - это старый прием, изобретенный для обхода ограничения безопасности, которое запрещает нам получать данные JSON с другого сервера (другого источника * ).

Трюк работает с использованием <script>тега, который запрашивает JSON из этого места, например:, { "user":"Smith" }но обернут в функцию, фактический JSONP («JSON with Padding»):

peopleDataJSONP({"user":"Smith"})

Получение этого в этой форме позволяет нам использовать данные в нашей peopleDataJSONPфункции. JSONP - плохая практика , не используйте ее (читайте ниже)


Проблема

Скажем, мы находимся ourweb.comи хотим получать данные JSON (или любые необработанные данные) anotherweb.com. Если бы мы использовали запрос GET (например XMLHttpRequest, fetchвызов $.ajaxи т. Д.), Наш браузер сказал бы, что это не разрешено с этой ужасной ошибкой:

Ошибка консоли Chrome CORS

Как получить данные, которые мы хотим? Ну, <script>теги не подчиняются всему этому ограничению сервера (origin *)! Вот почему мы можем загрузить библиотеку, такую ​​как jQuery или Google Maps, с любого сервера, например, CDN, без каких-либо ошибок.

Важный момент : если вы подумаете об этом, эти библиотеки - это реально работающий JS-код (обычно это огромная функция со всей логикой внутри). Но необработанные данные? Данные JSON не являются кодом . Там нет ничего, чтобы бежать; это просто простые данные.

Таким образом, мы не можем обрабатывать или манипулировать нашими ценными данными. Браузер загрузит данные, на которые указывает наш <script>тег, и при обработке будет по праву жаловаться:

WTF это {"user":"Smith"}дерьмо мы загрузили? Это не код Я не могу вычислить, синтаксическая ошибка!


Взлом JSONP

Старый / хакерский способ использовать эти данные? Нам нужен этот сервер для отправки его с некоторой логикой, поэтому, когда он будет загружен, ваш код в браузере сможет использовать эти данные. Таким образом, сторонний сервер отправляет нам данные JSON внутри функции JS. Сами данные устанавливаются как входные данные этой функции. Это выглядит так:

peopleDataJSONP({"user":"Smith"})

что делает его JS-кодом, наш браузер будет анализировать без жалоб! Точно так же, как это происходит с библиотекой jQuery. Теперь, чтобы получить это, клиент «запрашивает» JSON-дружественный сервер для него, обычно это делается так:

<script src="https://anotherweb.com/api/data-from-people.json?myCallback=peopleDataJSONP"></script>

Наш браузер получит JSONP с таким именем функции, поэтому нам нужна функция с таким же именем в нашем коде, например:

const peopleDataJSONP = function(data){
  alert(data.user); // "Smith"
}

Или вот так, тот же результат:

function peopleDataJSONP(data){
  alert(data.user); // "Smith"
}

Браузер загрузит JSONP и запустит его, который вызывает нашу функцию , где аргументом dataбудет наш JSON. Теперь мы можем делать с нашими данными все, что захотим.


Не используйте JSONP, используйте CORS

JSONP - это хакерский сайт с несколькими недостатками:

  • Мы можем только выполнять запросы GET
  • Поскольку это запрос GET, запускаемый простым тегом скрипта, мы не получаем полезных ошибок или информации о ходе выполнения.
  • Есть также некоторые проблемы с безопасностью, такие как запуск JS-кода вашего клиента, который может быть изменен на вредоносную полезную нагрузку.
  • Это только решает проблему с данными JSON, но политика безопасности Same-Origin применяется к другим данным (веб-шрифты, изображения / видео, нарисованные с помощью drawImage () ...)
  • Это не очень элегантно и не читабельно.

Вывод заключается в том, что в настоящее время нет необходимости использовать его .

JSONP - это хитрость для получения данных JSON с другого сервера, но мы будем нарушать тот же принцип безопасности (Same-Origin), если нам понадобятся другие виды межсайтового контента.

Вы должны прочитать о CORS здесь , но суть этого:

Распределение ресурсов между источниками (CORS) - это механизм, который использует дополнительные заголовки HTTP, чтобы дать браузерам указание предоставить веб-приложению, работающему с одним источником, доступ к выбранным ресурсам из другого источника. Веб-приложение выполняет HTTP-запрос перекрестного источника, когда оно запрашивает ресурс, который имеет другой источник (домен, протокол или порт) от своего собственного.



* происхождение определяется 3 вещами: протокол , порт и хост . Так, например, https://web.comисточник отличается от http://web.com(другой протокол) и https://web.com:8081(другой порт) и, очевидно, https://thatotherweb.net(другой хост)

Карлес Алколея
источник
1
Эй, чувак, это обеспечило 100% ясность в качестве сноски к утвержденному ответу! Спасибо за это ....
М'Баку
4

Хорошие ответы уже были даны, мне просто нужно дать свою часть в виде блоков кода в javascript (я также включу более современное и лучшее решение для запросов между источниками: CORS с заголовками HTTP):

JSONP:

1.client_jsonp.js

$.ajax({
    url: "http://api_test_server.proudlygeek.c9.io/?callback=?",
    dataType: "jsonp",
    success: function(data) {
        console.log(data);    
    }
});​​​​​​​​​​​​​​​​​​

2.server_jsonp.js

var http = require("http"),
    url  = require("url");

var server = http.createServer(function(req, res) {

    var callback = url.parse(req.url, true).query.callback || "myCallback";
    console.log(url.parse(req.url, true).query.callback);

    var data = {
        'name': "Gianpiero",
        'last': "Fiorelli",
        'age': 37
    };

    data = callback + '(' + JSON.stringify(data) + ');';

    res.writeHead(200, {'Content-Type': 'application/json'});
    res.end(data);
});

server.listen(process.env.PORT, process.env.IP);

console.log('Server running at '  + process.env.PORT + ':' + process.env.IP);

CORS :

3.client_cors.js

$.ajax({
    url: "http://api_test_server.proudlygeek.c9.io/",
    success: function(data) {
        console.log(data);    
    }
});​

4.server_cors.js

var http = require("http"),
    url  = require("url");

var server = http.createServer(function(req, res) {
    console.log(req.headers);

    var data = {
        'name': "Gianpiero",
        'last': "Fiorelli",
        'age': 37
    };

    res.writeHead(200, {
        'Content-Type': 'application/json',
        'Access-Control-Allow-Origin': '*'
    });

    res.end(JSON.stringify(data));
});

server.listen(process.env.PORT, process.env.IP);

console.log('Server running at '  + process.env.PORT + ':' + process.env.IP);
Хумоюн Ахмад
источник
1

JSONP означает JSON с отступом .

Вот сайт с отличными примерами , с объяснениями от простейшего использования этой техники до самого продвинутого в плоскости JavaScript:

4PDA / JSONP

Один из моих самых любимых методов, описанных выше, - это динамический результат JSON , который позволяет отправлять JSON в файл PHP с параметром URL , а также позволяет файлу PHP возвращать объект JSON на основе получаемой информации .

Такие инструменты, как jQuery, также имеют возможности использовать JSONP :

jQuery.ajax({
  url: "https://data.acgov.org/resource/k9se-aps6.json?city=Berkeley",
  jsonp: "callbackName",
  dataType: "jsonp"
}).done(
  response => console.log(response)
);
simhumileco
источник