JSONP คืออะไรและทำไมมันถูกสร้างขึ้น


2128

ฉันเข้าใจ JSON แต่ไม่ใช่ JSONP เอกสารของ Wikipedia บน JSONคือ (คือ) ผลการค้นหายอดนิยมสำหรับ JSONP มันบอกว่านี้:

JSONP หรือ "JSON with padding" เป็นส่วนขยาย JSON โดยจะมีการระบุคำนำหน้าเป็นอาร์กิวเมนต์อินพุตของการโทร

ฮะ? สายอะไร นั่นไม่สมเหตุสมผลเลยสำหรับฉัน JSON เป็นรูปแบบข้อมูล ไม่มีการโทร

ผลการค้นหาที่ 2คือจากผู้ชายบางชื่อRemyผู้เขียนเกี่ยวกับเรื่องนี้ JSONP:

JSONP เป็นการฉีดแท็กสคริปต์ผ่านการตอบสนองจากเซิร์ฟเวอร์ไปยังฟังก์ชันที่ผู้ใช้ระบุ

ฉันสามารถเข้าใจได้ แต่ก็ยังไม่มีเหตุผลใด ๆ


ดังนั้น JSONP คืออะไร ทำไมมันถูกสร้างขึ้นมา (มันแก้ปัญหาอะไรได้บ้าง)? และทำไมฉันถึงใช้มัน?


ภาคผนวก : ฉันเพิ่งสร้างหน้าใหม่สำหรับ JSONPบน Wikipedia; ตอนนี้มีคำอธิบายที่ชัดเจนและละเอียดของ JSONP ตามคำตอบของjvenema


29
สำหรับบันทึกอย่าใช้ JSONP ถ้าคุณไม่เชื่อถือเซิร์ฟเวอร์ที่คุณกำลังพูดถึง 100% หากถูกบุกรุกหน้าเว็บของคุณจะถูกบุกรุกเล็กน้อย
ninjagecko

7
โปรดทราบด้วยว่า JSONP สามารถถูกขโมยได้หากไม่ได้ดำเนินการอย่างถูกต้อง
Pacerier

3
ผมอยากจะให้เครดิตกับผู้เขียน JSONP ที่ให้ปรัชญาเบื้องหลังมัน: เก็บบ๊อบ Ippolito บน 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 เมื่อเซิร์ฟเวอร์ได้รับพารามิเตอร์ "callback" มันจะสรุปผลลัพธ์ที่แตกต่างออกไปเล็กน้อยโดยส่งคืนสิ่งนี้:

mycallback({ foo: 'bar' });

อย่างที่คุณเห็นตอนนี้จะเรียกใช้เมธอดที่คุณระบุ ดังนั้นในหน้าของคุณคุณกำหนดฟังก์ชั่นการโทรกลับ:

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

และตอนนี้เมื่อโหลดสคริปต์สคริปต์จะถูกประเมินและฟังก์ชันของคุณจะถูกดำเนินการ Voila ขอข้ามโดเมน!

นอกจากนี้ยังควรคำนึงถึงปัญหาสำคัญอย่างหนึ่งของ JSONP ด้วย: คุณสูญเสียการควบคุมคำขอเป็นจำนวนมาก ตัวอย่างเช่นไม่มีวิธี "ดี" ในการรับรหัสความล้มเหลวที่เหมาะสมกลับมา เป็นผลให้คุณท้ายด้วยการใช้ตัวจับเวลาเพื่อตรวจสอบการร้องขอ ฯลฯ ซึ่งเป็นที่สงสัยอยู่เสมอ ข้อเสนอสำหรับJSONRequestเป็นโซลูชันที่ยอดเยี่ยมสำหรับการอนุญาตให้ใช้สคริปต์ข้ามโดเมนการบำรุงรักษาความปลอดภัยและการอนุญาตการควบคุมคำขอที่เหมาะสม

วันนี้ (2015) CORSเป็นแนวทางที่แนะนำเทียบกับ JSONRequest JSONP ยังคงมีประโยชน์สำหรับการสนับสนุนเบราว์เซอร์รุ่นเก่า แต่ได้รับผลกระทบด้านความปลอดภัยเว้นแต่ว่าคุณไม่มีทางเลือก CORS เป็นตัวเลือกที่ดีกว่า


180
โปรดทราบว่าการใช้ JSONP มีความเกี่ยวข้องด้านความปลอดภัย ในฐานะที่เป็น JSONP เป็นจาวาสคริปต์จริงๆมันสามารถทำทุกอย่างที่จาวาสคริปต์สามารถทำได้ดังนั้นคุณต้องเชื่อถือผู้ให้บริการข้อมูล JSONP ฉันได้เขียนบทความบล็อกเกี่ยวกับที่นี่: erlend.oftedal.no/blog/?blogid=97
Erlend

72
มีนัยยะด้านความปลอดภัยใหม่ ๆ ใน JSONP ที่ไม่ปรากฏในแท็ก <script> หรือไม่? ด้วยแท็กสคริปต์เบราว์เซอร์จะเชื่อถือเซิร์ฟเวอร์โดยนัยเพื่อส่งมอบ Javascript ที่ไม่เป็นอันตรายซึ่งเบราว์เซอร์ประเมินสุ่มสี่สุ่มห้า JSONP เปลี่ยนความจริงนั้นหรือไม่? ดูเหมือนว่าจะไม่
Cheeso

23
ไม่มันไม่ได้ คุณเชื่อใจในการส่งมอบจาวาสคริปต์เหมือนกับที่ใช้กับ 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 อย่างไร หลังจากคำอธิบายฉันรู้สึก litte โง่พลาดจุด ...
เอดูอาร์

13
นี่เป็นคำตอบที่สมบูรณ์มากสำหรับคำตอบของ jvenema - ฉันไม่เข้าใจว่าทำไมการโทรกลับจึงจำเป็นจนกว่าคุณจะชี้ให้เห็นว่าข้อมูล json จะต้องเข้าถึงผ่านองค์ประกอบสคริปต์
แมตต์

5
ขอบคุณสำหรับคำอธิบายที่ชัดเจนเช่นนี้ ฉันต้องการตำราเรียนที่วิทยาลัยของฉันถูกเขียนขึ้นโดยคนที่ชอบคุณ :)
hashbrown

1
คำอธิบายที่ดีมากกว่าที่ผ่านมา Ofcourse - ข้อความที่ตัดตอนมาของคุณ "สิ่งที่คุณมักใช้ในการโหลดไฟล์ js เพื่อให้ js รับข้อมูลจากโดเมนอื่นฟังดูแปลก ๆ เหรอ?" นอกจากนี้ยังเปิดตาสำหรับฉัน โค้ดตัวอย่างมีชื่อเสียงมาก
SIslam

การขยายไม่จำเป็นต้องเป็นตัวอักษร มันเป็นอุปมาชนิดหนึ่ง ดังนั้นจึงอาจหมายถึง "JSON ด้วย" ช่องว่าง "บางส่วน lol
marvinIsSacul

48

JSONP ทำงานโดยการสร้างองค์ประกอบ "สคริปต์" (ในมาร์กอัป HTML หรือแทรกลงใน DOM ผ่าน 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
ลิงค์ด้านบนสุดเพียง 404s ตอนนี้
Kevin Beal

เนื้อหาของการเชื่อมโยงว่าขณะนี้สามารถใช้ได้ที่http://scn.sap.com/community/developer-center/front-end/blog/2013/07/15/secret-behind-jsonp
ᴠɪɴᴄᴇɴᴛ

42

เนื่องจากคุณสามารถขอให้เซิร์ฟเวอร์เติมคำนำหน้าให้กับวัตถุ JSON ที่ส่งคืน เช่น

function_prefix(json_object);

เพื่อให้เบราว์เซอร์eval"inline" สตริง JSON เป็นนิพจน์ เคล็ดลับนี้ทำให้เซิร์ฟเวอร์สามารถ "ฉีด" รหัสจาวาสคริปต์โดยตรงในเบราว์เซอร์ไคลเอ็นต์และสิ่งนี้ด้วยการข้ามข้อ จำกัด "แหล่งกำเนิดเดียวกัน"

ในคำอื่น ๆ ที่คุณสามารถบรรลุการแลกเปลี่ยนข้อมูลข้ามโดเมน


โดยทั่วไปXMLHttpRequestไม่อนุญาตให้แลกเปลี่ยนข้อมูลข้ามโดเมนโดยตรง (ต้องผ่านเซิร์ฟเวอร์ในโดเมนเดียวกัน) โดยที่:

<script src="some_other_domain/some_data.js&prefix=function_prefix> `หนึ่งสามารถเข้าถึงข้อมูลจากโดเมนที่แตกต่างจากที่มา


นอกจากนี้ยังควรคำนึงถึง: ถึงแม้ว่าเซิร์ฟเวอร์ควรได้รับการพิจารณาว่าเป็น "เชื่อถือได้" ก่อนที่จะลองใช้ "เคล็ดลับ" ดังกล่าวผลข้างเคียงของการเปลี่ยนแปลงที่เป็นไปได้ในรูปแบบวัตถุ ฯลฯ อาจมีอยู่ หากใช้function_prefix(คือฟังก์ชัน js ที่เหมาะสม) เพื่อรับวัตถุ JSON ฟังก์ชันดังกล่าวสามารถทำการตรวจสอบก่อนที่จะยอมรับ / ประมวลผลข้อมูลที่ส่งคืนเพิ่มเติม


"ผนวกคำนำหน้า" ทำให้เกิดความสับสน :)
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=wheverJavascriptName&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 เป็นรูปแบบข้อมูลที่ใช้มากที่สุดในเว็บในเดือนธันวาคม '15
RobbyD

มันยังไม่น่าแปลกใจว่าทำไม jsonp จึงถูกใช้แทน json ข้อ จำกัด ด้านความปลอดภัยเหล่านั้นมาจากไหน? เหตุใดเราสามารถใช้ 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>แท็กของเราและเมื่อประมวลผลข้อมูลจะบ่นอย่างถูกต้อง:

{"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 () ... )
  • มันไม่ได้สวยงามและอ่านง่าย

Takeaway ที่เป็นว่ามีความจำเป็นต้องใช้มันในปัจจุบัน

JSONP เป็นเคล็ดลับในการรับข้อมูล JSON จากเซิร์ฟเวอร์อื่น แต่เราจะละเมิดหลักการความปลอดภัยเดียวกัน (Same-Origin) หากเราต้องการสิ่งข้ามไซต์ชนิดอื่น

คุณควรอ่านเกี่ยวกับ CORS ที่นี่แต่ส่วนสำคัญของมันคือ:

การแบ่งปันทรัพยากรข้ามแหล่งกำเนิด (CORS) เป็นกลไกที่ใช้ส่วนหัว HTTP เพิ่มเติมเพื่อบอกเบราว์เซอร์เพื่อให้เว็บแอปพลิเคชันทำงานที่แหล่งกำเนิดเดียวเข้าถึงทรัพยากรที่เลือกจากแหล่งกำเนิดที่แตกต่างกัน เว็บแอ็พพลิเคชันเรียกใช้การร้องขอ HTTP ข้ามต้นทางเมื่อร้องขอทรัพยากรที่มีต้นกำเนิดที่แตกต่างกัน (โดเมนโปรโตคอลหรือพอร์ต) จากของตนเอง



* ต้นกำเนิดจะถูกกำหนดโดย 3 สิ่ง: โพรโทคอ , พอร์ตและโฮสต์ ตัวอย่างเช่นhttps://web.comเป็นแหล่งกำเนิดที่แตกต่างจากhttp://web.com(โปรโตคอลที่แตกต่าง) และhttps://web.com:8081(พอร์ตที่แตกต่าง) และเห็นได้ชัดhttps://thatotherweb.net(โฮสต์ที่แตกต่างกัน)


1
เฮ้คนนี้ให้ความคมชัด 100% เป็นเชิงอรรถสำหรับคำตอบที่ได้รับการอนุมัติ! ขอบคุณสำหรับสิ่งนี้ ....
M'Baku

4

คำตอบที่ยอดเยี่ยมได้รับแล้วฉันต้องให้ชิ้นส่วนของฉันในรูปแบบของรหัสบล็อกในจาวาสคริปต์ (ฉันจะรวมถึงวิธีการที่ทันสมัยและดีกว่าสำหรับคำขอข้ามที่มา: 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)
);
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.