Làm cách nào để tạo một yêu cầu JSONP từ Javascript mà không cần JQuery?


122

Tôi có thể thực hiện yêu cầu JSONP miền chéo trong JavaScript mà không sử dụng jQuery hoặc thư viện bên ngoài khác không? Tôi muốn sử dụng chính JavaScript và sau đó phân tích cú pháp dữ liệu và biến nó thành một đối tượng để tôi có thể sử dụng nó. Tôi có phải sử dụng thư viện bên ngoài không? Nếu không, tôi có thể làm như thế nào?


Câu trả lời:


151
function foo(data)
{
    // do stuff with JSON
}

var script = document.createElement('script');
script.src = '//example.com/path/to/jsonp?callback=foo'

document.getElementsByTagName('head')[0].appendChild(script);
// or document.head.appendChild(script) in modern browsers

2
Đây là một JSBin có thể được sử dụng để làm quen với JSONP từ Wikipedia. Nó đã được tham chiếu trong câu trả lời này .
rkagerer

1
Tôi nghĩ cần chỉ ra rằng phản hồi phải có dạng foo(payload_of_json_data):, ý tưởng là khi nó được tải vào thẻ script, nó sẽ gọi hàm foo với tải trọng đã là một đối tượng javascript và không cần phân tích cú pháp.
Octopus,

@WillMunn Tôi không nghĩ điều đó có thể làm được với JSONP. Đó là một cuộc tấn công từ những ngày trước CORS. Bạn cần đặt tiêu đề để làm gì? Máy chủ đặc biệt cần chấp nhận các yêu cầu JSONP vì vậy nó phải được thiết lập để phân phát một cách lành mạnh.
Matt Ball

bạn nói đúng, tôi đã đọc nhầm một số tài liệu api, có một tham số truy vấn đặc biệt để thực hiện những gì tôi muốn khi sử dụng appologies jsonp.
Will Munn

@WillMunn đừng lo lắng. Rất vui vì bạn đã có thể sắp xếp nó ra!
Matt Ball

37

Ví dụ về trọng lượng nhẹ (có hỗ trợ onSuccess và onTimeout). Bạn cần chuyển tên gọi lại trong URL nếu bạn cần.

var $jsonp = (function(){
  var that = {};

  that.send = function(src, options) {
    var callback_name = options.callbackName || 'callback',
      on_success = options.onSuccess || function(){},
      on_timeout = options.onTimeout || function(){},
      timeout = options.timeout || 10; // sec

    var timeout_trigger = window.setTimeout(function(){
      window[callback_name] = function(){};
      on_timeout();
    }, timeout * 1000);

    window[callback_name] = function(data){
      window.clearTimeout(timeout_trigger);
      on_success(data);
    }

    var script = document.createElement('script');
    script.type = 'text/javascript';
    script.async = true;
    script.src = src;

    document.getElementsByTagName('head')[0].appendChild(script);
  }

  return that;
})();

Sử dụng mẫu:

$jsonp.send('some_url?callback=handleStuff', {
    callbackName: 'handleStuff',
    onSuccess: function(json){
        console.log('success!', json);
    },
    onTimeout: function(){
        console.log('timeout!');
    },
    timeout: 5
});

Tại GitHub: https://github.com/sobstel/jsonp.js/blob/master/jsonp.js


29

JSONP là gì?

Điều quan trọng cần nhớ với jsonp là nó không thực sự là một giao thức hoặc kiểu dữ liệu. Nó chỉ là một cách tải tập lệnh nhanh chóng và xử lý tập lệnh được đưa vào trang. Theo tinh thần của JSONP, điều này có nghĩa là giới thiệu một đối tượng javascript mới từ máy chủ vào ứng dụng / tập lệnh máy khách.

Khi nào thì cần JSONP?

Đây là 1 phương pháp cho phép một miền truy cập / xử lý dữ liệu từ một miền khác trong cùng một trang một cách không đồng bộ. Về cơ bản, nó được sử dụng để ghi đè các hạn chế CORS (Chia sẻ tài nguyên nguồn gốc chéo) sẽ xảy ra với yêu cầu XHR (ajax). Tải tập lệnh không phải tuân theo các hạn chế của CORS.

Nó được thực hiện như thế nào

Việc giới thiệu một đối tượng javascript mới từ máy chủ có thể được thực hiện theo nhiều cách, nhưng thực tế phổ biến nhất là máy chủ thực hiện việc thực thi một hàm 'gọi lại', với đối tượng bắt buộc được truyền vào nó. Hàm gọi lại chỉ là một hàm bạn đã thiết lập trên máy khách mà tập lệnh bạn tải sẽ gọi vào thời điểm tập lệnh tải để xử lý dữ liệu được truyền vào nó.

Thí dụ:

Tôi có một ứng dụng ghi lại tất cả các mục trong nhà của ai đó. Ứng dụng của tôi đã được thiết lập và bây giờ tôi muốn lấy tất cả các vật dụng trong phòng ngủ chính.

Ứng dụng của tôi đang bật app.home.com. Apis tôi cần để tải dữ liệu từ đó đang bật api.home.com.

Trừ khi máy chủ được thiết lập rõ ràng để cho phép, tôi không thể sử dụng ajax để tải dữ liệu này, vì ngay cả các trang trên các miền phụ riêng biệt cũng phải tuân theo các hạn chế của XHR CORS.

Tốt nhất, hãy thiết lập mọi thứ để cho phép XHR miền x

Lý tưởng nhất là vì api và ứng dụng nằm trên cùng một miền, tôi có thể có quyền truy cập để thiết lập tiêu đề trên api.home.com. Nếu tôi làm vậy, tôi có thể thêm một Access-Control-Allow-Origin: mục tiêu đề cấp quyền truy cập vào app.home.com. Giả sử tiêu đề được thiết lập như sau:, Access-Control-Allow-Origin: "http://app.home.com"điều này an toàn hơn nhiều so với thiết lập JSONP. Điều này là do app.home.comcó thể có được mọi thứ nó muốn api.home.commà không cần api.home.comcấp cho CORS quyền truy cập vào toàn bộ internet.

Giải pháp XHR ở trên không thể thực hiện được. Thiết lập JSONP Trên tập lệnh máy khách của tôi: Tôi thiết lập một hàm để xử lý phản hồi từ máy chủ khi tôi thực hiện lệnh gọi JSONP. :

function processJSONPResponse(data) {
    var dataFromServer = data;
}

Máy chủ sẽ cần được thiết lập để trả về một tập lệnh nhỏ trông giống như "processJSONPResponse('{"room":"main bedroom","items":["bed","chest of drawers"]}');"Nó có thể được thiết kế để trả về một chuỗi như vậy nếu một cái gì đó tương tự //api.home.com?getdata=room&room=main_bedroomđược gọi.

Sau đó, khách hàng thiết lập một thẻ script như sau:

var script = document.createElement('script');
script.src = '//api.home.com?getdata=room&room=main_bedroom';

document.querySelector('head').appendChild(script);

Thao tác này tải tập lệnh và ngay lập tức gọi window.processJSONPResponse()dưới dạng được máy chủ viết / echo / in ra. Dữ liệu được chuyển vào dưới dạng tham số cho hàm hiện được lưu trữ trong dataFromServerbiến cục bộ và bạn có thể làm với nó bất cứ điều gì bạn cần.

Dọn dẹp

Một khi khách hàng có dữ liệu, tức là. ngay sau khi tập lệnh được thêm vào DOM, phần tử tập lệnh có thể bị xóa khỏi DOM:

script.parentNode.removeChild(script);

2
Cảm ơn rất nhiều, điều này đã giúp tôi rất nhiều với dự án của mình. Một vấn đề nhỏ: Tôi đã nhận được SyntaxError: JSON.parse: unexpected character at line 1 column 2 of the JSON data. Sau khi thêm dấu nháy đơn vào các dữ liệu, mọi thứ hoạt động tốt, vì vậy:"processJSONPResponse('{"room":"main bedroom","items":["bed","chest of drawers"]}');"
Hein van Dyke

17

Sự hiểu biết của tôi là bạn thực sự sử dụng các thẻ script với JSONP, sooo ...

Bước đầu tiên là tạo hàm của bạn sẽ xử lý JSON:

function hooray(json) {
    // dealin wit teh jsonz
}

Đảm bảo rằng chức năng này có thể truy cập được ở cấp độ toàn cầu.

Tiếp theo, thêm một phần tử tập lệnh vào DOM:

var script = document.createElement('script');
script.src = 'http://domain.com/?function=hooray';
document.body.appendChild(script);

Tập lệnh sẽ tải JavaScript mà nhà cung cấp API xây dựng và thực thi nó.


2
Cảm ơn mọi người. Có nó bắn ra trên internet để tìm kiếm một số dữ liệu và sau đó tôi làm điều gì đó với nó. Tôi đã sử dụng eval () trên dữ liệu phản hồi đã giúp tôi xử lý đối tượng - mảng mà nó là (tôi nghĩ). {Đó là một con gấu tìm ra phân tích cú pháp với trí tuệ hạn chế của tôi nhưng cuối cùng tôi đã nhận được giá trị từ nó}. Tuyệt diệu.
Dave

@ Dave @ Matt Có lẽ tôi mờ trên JSONP, nhưng bạn không cần evalhoặc parsehoặc bất cứ điều gì. Bạn sẽ nhận được JavaScript mà trình duyệt có thể thực thi, phải không?
sdleihssirhc

Tôi xấu, xin lỗi vì sự nhầm lẫn. Việc cố gắng lấy thứ (giá trị? Thuộc tính?) Ra khỏi mảng khiến tôi quay cuồng (Việc lồng, gọi lại, mảng, phần tử, đối tượng, chuỗi, giá trị, dấu ngoặc nhọn, dấu ngoặc nhọn ...). Tôi đã xóa việc sử dụng eval và vẫn nhận được thuộc tính (giá trị?) Từ mảng (đối tượng? Phần tử?) Mà tôi muốn.
Dave

10

cách tôi sử dụng jsonp như bên dưới:

function jsonp(uri) {
    return new Promise(function(resolve, reject) {
        var id = '_' + Math.round(10000 * Math.random());
        var callbackName = 'jsonp_callback_' + id;
        window[callbackName] = function(data) {
            delete window[callbackName];
            var ele = document.getElementById(id);
            ele.parentNode.removeChild(ele);
            resolve(data);
        }

        var src = uri + '&callback=' + callbackName;
        var script = document.createElement('script');
        script.src = src;
        script.id = id;
        script.addEventListener('error', reject);
        (document.getElementsByTagName('head')[0] || document.body || document.documentElement).appendChild(script)
    });
}

sau đó sử dụng phương thức 'jsonp' như sau:

jsonp('http://xxx/cors').then(function(data){
    console.log(data);
});

tài liệu tham khảo:

JavaScript XMLHttpRequest sử dụng JsonP

http://www.w3ctech.com/topic/721 (nói về cách sử dụng Promise)


1
chấm dứt các nhiệm vụ script.src = src; thêm dấu ';' đến hết tất cả các bài tập
chdev77

6

Tôi có một thư viện javascript thuần để làm điều đó https://github.com/robertodecurnex/J50Npi/blob/master/J50Npi.js

Hãy xem nó và cho tôi biết nếu bạn cần bất kỳ trợ giúp nào khi sử dụng hoặc hiểu mã.

Btw, bạn có ví dụ sử dụng đơn giản ở đây: http://robertodecurnex.github.com/J50Npi/


6
Giải pháp của bạn hơi quá đơn giản đối với trường hợp sử dụng của tôi, vì vậy tôi đã thêm nhiều yêu cầu hỗ trợ gist.github.com/1431613
Chad Scira

5
/**
 * Loads data asynchronously via JSONP.
 */
const load = (() => {
  let index = 0;
  const timeout = 5000;

  return url => new Promise((resolve, reject) => {
    const callback = '__callback' + index++;
    const timeoutID = window.setTimeout(() => {
      reject(new Error('Request timeout.'));
    }, timeout);

    window[callback] = response => {
      window.clearTimeout(timeoutID);
      resolve(response.data);
    };

    const script = document.createElement('script');
    script.type = 'text/javascript';
    script.async = true;
    script.src = url + (url.indexOf('?') === -1 ? '?' : '&') + 'callback=' + callback;
    document.getElementsByTagName('head')[0].appendChild(script);
  });
})();

Mẫu sử dụng:

const data = await load('http://api.github.com/orgs/kriasoft');

1
Đừng quên window[callback] = nullcho phép chức năng được thu gom rác.
Sukima

3

Tôi đã viết một thư viện để xử lý việc này, càng đơn giản càng tốt. Không cần phải làm cho nó bên ngoài, nó chỉ là một chức năng. Không giống như một số tùy chọn khác, tập lệnh này tự dọn dẹp và được tổng quát hóa để thực hiện các yêu cầu khác trong thời gian chạy.

https://github.com/Fresheyeball/micro-jsonp

function jsonp(url, key, callback) {

    var appendParam = function(url, key, param){
            return url
                + (url.indexOf("?") > 0 ? "&" : "?")
                + key + "=" + param;
        },

        createScript = function(url, callback){
            var doc = document,
                head = doc.head,
                script = doc.createElement("script");

            script
            .setAttribute("src", url);

            head
            .appendChild(script);

            callback(function(){
                setTimeout(function(){
                    head
                    .removeChild(script);
                }, 0);
            });
        },

        q =
            "q" + Math.round(Math.random() * Date.now());

    createScript(
        appendParam(url, key, q), function(remove){
            window[q] =
                function(json){
                    window[q] = undefined;
                    remove();
                    callback(json);
                };
        });
}

2

Vui lòng xem JavaScriptví dụ dưới đây để thực hiện JSONPcuộc gọi mà không cần JQuery:

Ngoài ra, bạn có thể tham khảo GitHubkho của tôi để tham khảo.

https://github.com/shedagemayur/JavaScriptCode/tree/master/jsonp

window.onload = function(){
    var callbackMethod = 'callback_' + new Date().getTime();

    var script = document.createElement('script');
    script.src = 'https://jsonplaceholder.typicode.com/users/1?callback='+callbackMethod;

    document.body.appendChild(script);

    window[callbackMethod] = function(data){
        delete window[callbackMethod];
        document.body.removeChild(script);
        console.log(data);
    }
}


0
/**
 * Get JSONP data for cross-domain AJAX requests
 * @private
 * @link http://cameronspear.com/blog/exactly-what-is-jsonp/
 * @param  {String} url      The URL of the JSON request
 * @param  {String} callback The name of the callback to run on load
 */
var loadJSONP = function ( url, callback ) {

    // Create script with url and callback (if specified)
    var ref = window.document.getElementsByTagName( 'script' )[ 0 ];
    var script = window.document.createElement( 'script' );
    script.src = url + (url.indexOf( '?' ) + 1 ? '&' : '?') + 'callback=' + callback;

    // Insert script tag into the DOM (append to <head>)
    ref.parentNode.insertBefore( script, ref );

    // After the script is loaded (and executed), remove it
    script.onload = function () {
        this.remove();
    };

};

/** 
 * Example
 */

// Function to run on success
var logAPI = function ( data ) {
    console.log( data );
}

// Run request
loadJSONP( 'http://api.petfinder.com/shelter.getPets?format=json&key=12345&shelter=AA11', 'logAPI' );

Tại sao window.document.getElementsByTagName('script')[0];và không document.body.appendChild(…)?
Sukima

Bạn không logAPInên đặt thành nullkhi nào hoàn thành để có thể thực hiện thu gom rác trên đó?
Sukima

0

Nếu bạn đang sử dụng ES6 với NPM, bạn có thể thử mô-đun nút "fetch-jsonp". API tìm nạp Cung cấp hỗ trợ để thực hiện cuộc gọi JsonP như một cuộc gọi XHR thông thường.

Điều kiện tiên quyết: bạn nên sử dụng isomorphic-fetchmô-đun nút trong ngăn xếp của mình.


0

Chỉ cần dán phiên bản ES6 của câu trả lời hay ho của sobstel:

send(someUrl + 'error?d=' + encodeURI(JSON.stringify(json)) + '&callback=c', 'c', 5)
    .then((json) => console.log(json))
    .catch((err) => console.log(err))

function send(url, callback, timeout) {
    return new Promise((resolve, reject) => {
        let script = document.createElement('script')
        let timeout_trigger = window.setTimeout(() => {
            window[callback] = () => {}
            script.parentNode.removeChild(script)
            reject('No response')
        }, timeout * 1000)

        window[callback] = (data) => {
            window.clearTimeout(timeout_trigger)
            script.parentNode.removeChild(script)
            resolve(data)
        }

        script.type = 'text/javascript'
        script.async = true
        script.src = url

        document.getElementsByTagName('head')[0].appendChild(script)
    })
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.