JSONP: Что это, зачем создали и почему его вытеснил CORS

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

JSONP: Секретное оружие фронтендера прошлого

Представьте, что вы — веб-разработчик в 2010 году. Вам нужно получить данные с чужого сервера, например, прогноз погоды или курс валют. Вы пишете простой fetch запрос, но в консоли браузера появляется красная ошибка: «No 'Access-Control-Allow-Origin' header is present». Знакомо?

Это Same-Origin Policy (политика одинакового источника) не пускает ваш JavaScript за пределы родного домена. Но как же тогда работали виджеты погоды, кнопки «Поделиться» и счётчики посещений? Ответ прост: они использовали JSONP — хитрый трюк, о котором мы сегодня и поговорим.

В этой статье мы ответим на популярный вопрос с Stack Overflow: «What is JSONP, and why was it created?» — простым языком, с примерами кода и без лишней теории.

1. Проблема: Почему браузер блокирует запросы?

Браузер — параноик. Он защищает пользователя от XSS-атак и кражи данных. Same-Origin Policy разрешает JavaScript читать ответы только с того же домена, протокола и порта.

Пример блокировки:

// Этот код вызовет ошибку CORS
fetch('https://api.distant-server.com/data')
  .then(response => response.json())
  .then(data => console.log(data));

Но есть одно исключение: <script> теги. Браузер спокойно загружает скрипты с любых доменов! Именно эту лазейку и использует JSONP.

2. Решение: JSONP — JSON с Padding'ом

JSONP (JSON with Padding) — это не технология и не формат данных, а паттерн (костыль), который обходит Same-Origin Policy через тег <script>.

Идея проста: вместо того чтобы делать запрос через XMLHttpRequest, мы динамически создаём <script> элемент, указываем URL сервера, и сервер возвращает не JSON, а JavaScript-код, который вызывает нашу функцию с данными.

Как выглядит обычный JSON:

// Ответ сервера (обычный JSON)
{"name": "PythonLib", "topic": "JSONP"}

Как выглядит JSONP:

// Ответ сервера (JSONP)
myCallback({"name": "PythonLib", "topic": "JSONP"});

Разница колоссальная: во втором случае сервер оборачивает данные в вызов функции myCallback. Это и есть тот самый padding (обёртка).

3. Практика: Пишем JSONP запрос своими руками

Давайте реализуем JSONP-клиент на чистом JavaScript. Это поможет понять магию.

function jsonp(url, callbackName) {
  // 1. Создаём глобальную функцию (колбэк)
  window[callbackName] = function(data) {
    console.log('Данные получены:', data);
    delete window[callbackName]; // Чистим за собой
    document.body.removeChild(script);
  };

  // 2. Создаём тег script
  const script = document.createElement('script');
  script.src = `${url}?callback=${callbackName}`; // Передаём имя колбэка
  script.onerror = function() {
    console.error('Ошибка загрузки JSONP');
    delete window[callbackName];
    document.body.removeChild(script);
  };

  // 3. Добавляем в DOM — браузер сразу загружает и исполняет скрипт
  document.body.appendChild(script);
}

// Используем:
jsonp('https://api.example.com/data', 'handleData');

Как это работает по шагам:

  • Мы создаём глобальную функцию handleData.
  • Добавляем тег <script> с URL сервера. В URL мы передаём параметр ?callback=handleData.
  • Сервер, поддерживающий JSONP, видит параметр callback и возвращает не JSON, а JavaScript: handleData({...}).
  • Браузер выполняет этот скрипт, вызывается наша функция, и мы получаем данные.

4. Почему JSONP — это зло? (Недостатки)

Если вы подумали: «Круто, буду везде использовать JSONP!» — не спешите. У этого метода есть фатальные недостатки:

  • Только GET-запросы. Тег <script> может делать только GET. Никаких POST, PUT или DELETE.
  • Нет обработки ошибок. Вы не узнаете HTTP-статус (404, 500). Скрипт либо загрузился, либо нет.
  • Угроза безопасности. Вы доверяете чужому серверу выполнить любой JavaScript в вашем контексте. Если сервер взломают — прощай, данные пользователей.
  • Глобальное загрязнение. Колбэки живут в глобальном объекте window, что может вызвать конфликты.

5. Зачем JSONP создали, если он такой плохой?

Ответ — исторический контекст. JSONP появился в эпоху, когда CORS (Cross-Origin Resource Sharing) ещё не был стандартом. Первые черновики CORS появились в 2006 году, но браузеры начали массово поддерживать его только к 2012-2014 годам.

Разработчикам нужно было решение «здесь и сейчас». JSONP был единственным способом делать кросс-доменные запросы без серверного прокси. Его использовали:

  • Google APIs (карты, поиск)
  • Twitter (виджеты твитов)
  • jQuery (метод $.ajax с типом данных jsonp)
  • Счётчики аналитики (Google Analytics)

6. JSONP vs CORS: Кто победил?

Сегодня JSONP считается устаревшим. Единственный правильный способ делать кросс-доменные запросы — это CORS. Сервер просто добавляет заголовок:

Access-Control-Allow-Origin: *

И браузер перестаёт блокировать запросы. CORS поддерживает любые HTTP-методы, передаёт заголовки, позволяет обрабатывать ошибки и безопаснее.

Однако, если вы работаете с legacy-системой или API, которое до сих пор не поддерживает CORS, знание JSONP может спасти проект. Но для нового кода — забудьте про JSONP как про страшный сон.

7. Вопросы с собеседований (и Stack Overflow)

Чтобы закрепить материал, вот 3 вопроса, которые часто задают на собеседованиях:

Вопрос 1: Чем JSONP отличается от JSON?

Ответ: JSON — это формат данных. JSONP — это техника доставки этих данных, при которой они оборачиваются в вызов функции. JSONP не является форматом, это паттерн.

Вопрос 2: Можно ли использовать JSONP с fetch или XMLHttpRequest?

Блоги

Рекомендаций по книгам