Що таке JSONP, і чому він був створений?


2128

Я розумію JSON, але не JSONP. Документ Вікіпедії на JSON є (був) головним результатом пошуку JSONP. Це говорить так:

JSONP або "JSON з прокладкою" - це розширення JSON, в якому префікс задається як вхідний аргумент самого виклику.

Так? Який дзвінок? Це не має для мене сенсу. JSON - формат даних. Немає дзвінків.

Результат пошуку другого з якого - то хлопця по імені Ремі , який пише це про JSONP:

JSONP - це введення тегів скрипту, що передає відповідь від сервера до визначеної користувачем функції.

Я можу це зрозуміти, але це все одно не має сенсу.


Отже, що таке JSONP? Для чого він був створений (яку проблему він вирішує)? І навіщо я його використовувати?


Додаток : Я щойно створив нову сторінку для JSONP у Вікіпедії; тепер він має чіткий і ретельний опис JSONP, заснований на відповіді jvenema .


29
Для запису НЕ використовуйте JSONP, якщо ви не довіряєте серверу, на якому ви говорите на 100%. Якщо це порушено, ваша веб-сторінка буде тривільно порушена.
ninjagecko

7
Також зауважте, що JSONP може бути викрадений, якщо він не виконаний правильно.
Pacerier

3
Я хотів би віддати подяку автору JSONP, який наголосив на філософії: архів Боба Іпполіто на JSONP . Він представляє JSONP як "нову стандартну методологію агностики для методу тегів скриптів для отримання даних міждоменних даних".
harshvchawla

Відповіді:


2047

Це насправді не надто складно ...

Скажіть, що ви в домені 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 - кращий вибір.


180
Зауважте, що використання JSONP має певні наслідки для безпеки. Оскільки JSONP насправді javascript, він може робити все інше, що може робити Javascript, тому вам потрібно довіритися постачальнику даних JSONP. Я написав про це повідомлення в блозі тут: erlend.oftedal.no/blog/?blogid=97
Ерленд

72
Чи дійсно є якісь нові наслідки для безпеки в JSONP, які відсутні в тегу <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>

Однак це трохи незручно, оскільки нам потрібно отримати цей масив із тегу скрипту . Тож творці 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 з Padding . (дуже погано названа техніка, оскільки вона насправді не має нічого спільного з тим, що більшість людей вважають «підкладкою».)


34
Дякуємо за пояснення тегу сценарію. Я не зміг зрозуміти, як JSONP обійшла політику безпеки міждоменних мереж. Після пояснення я почуваюсь дурним дурним, щоб пропустити суть ...
Едуард

13
Це дуже хороша додаткова відповідь на відповідь jvenema - я не розумів, чому зворотний виклик був необхідним, поки ви не вказали, що в іншому випадку до даних json потрібно буде отримати доступ через елемент скрипту.
Метт

5
Дякую за таке чітке пояснення. Я хочу, щоб мої підручники в коледжі були написані такими, як ви :)
hashbrown

1
Гарна експлікація, а не попередня. Звичайно - ваш уривок "ті, які ви зазвичай використовуєте для завантаження файлів js, щоб js отримав дані з іншого домену. Звучить дивно?" є також відкриття очей для мене. Приклад коду в дуже показовому.
SIslam

оббивка не повинна бути буквальною. це свого роду метафора. тому це може означати "JSON з деякими" пробілами "". lol
marvinIsSacul

48

JSONP працює, будуючи елемент "скрипт" (або в розмітці 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), зазначена функція може виконувати перевірки перед прийняттям / подальшою обробкою повернених даних.


"додавати префікс" заплутано :)
jub0bs

19

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

Ви можете скористатися послугою b1t.co, щоб побачити, як вона працює. Це безкоштовна послуга JSONP, яка дозволяє вам мінімізувати свої URL-адреси. Ось URL-адреса, яку потрібно використовувати для сервісу:

http://b1t.co/Site/api/External/MakeUrlWithGet?callback= evidenceresultsCallBackSense&url= evidenceescapedUrlToMinify]

Наприклад, дзвінок http://b1t.co/Site/api/External/MakeUrlWithGet?callback=wwhatJavascriptName&url=google.com

повернеться

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

Таким чином, коли ця програма завантажується у ваш 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, на якій можна практикувати, доступні за адресою: ця публікація


2
Дякуємо, що опублікували свою відповідь! Будь ласка, зауважте, що ви повинні розмістити основні частини відповіді тут, на цьому веб-сайті, або ваша публікація ризикує бути видаленою Дивіться поширені запитання, де в ній згадуються відповіді, які ледве перевищують посилання. Ви можете все-таки включити посилання за бажанням, але лише як "посилання". Відповідь має стояти самостійно, не потребуючи посилання.
Taryn

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, але через обмеження безпеки механізм браузера Sandbox відключає виконання міждоменного коду JSON.

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

Отже, коротко, це пояснює, що таке JSONP, яку проблему він вирішує (коли ним користуватися).


4
Я сприйняв це, тому що я не вірю твердженням, що XML був найбільш використовуваним форматом DAT в Інтернеті в грудні '15.
RobbyD

Він досі не визначає, чому замість json використовується jsonp. Звідки беруться всі ці обмеження безпеки? Чому ми можемо використовувати jsonp, але не json для крос-доменів?
Merunas Grincalaitis

5

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>теги не підпадають під обмеження всього цього сервера (походження *)! Ось чому ми можемо завантажувати бібліотеку на зразок jQuery або Google Maps з будь-якого сервера, наприклад CDN, без помилок.

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

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

wtf це {"user":"Smith"}лайно, яке ми завантажили? Це не код. Я не можу обчислити, синтаксична помилка!


Злом JSONP

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

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

що робить його JS-кодом наш браузер буде розбирати без нарікань! Так само, як і в бібліотеці jQuery. Тепер, щоб зробити це таким чином, клієнт "запитує" для нього зручний сервер JSONP, як правило, робиться так:

<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 застосовується і до інших даних (WebFonts, зображення / відео, намальовані малюнком drawImage () ...)
  • Це не дуже елегантно і не читабельно.

Вигода полягає в тому, що зараз не потрібно використовувати його .

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

Ви повинні прочитати про CORS тут , але суть його:

Перехресне походження ресурсів (CORS) - це механізм, який використовує додаткові заголовки HTTP, щоб повідомити браузерам надати веб-додатку, що працює з одним джерелом, доступ до вибраних ресурсів з іншого походження. Веб-додаток виконує запит HTTP-походження, коли він запитує ресурс, який має інше походження (домен, протокол або порт) від власного.



* походження визначається трьома речами: протоколом , портом і хостом . Так, наприклад, https://web.comмає інше походження, ніж http://web.com(різний протокол) і https://web.com:8081(інший порт) і очевидно https://thatotherweb.net(інший хост)


Гей, це забезпечило 100% ясність як виноску до схваленої відповіді! Дякую за це ....
M'Baku

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 з Padding .

Ось сайт, з великими прикладами , з поясненням від найпростішого використання цієї техніки в найбільш передових в площині JavaScript:

w3schools.com / 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)
);
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.