ל-JavaScript (ולשפות צד לקוח אחרות כגון Action Script) יש תכונה שנקראת Same Domain Policy שאוסרת על ייבוא משאבים מדומיין אחר. אך בעולם המודרני יש צורך לעתים ל-AJAX זה או אחר לתקשר עם דומיין אחר.
במאמר הקודם על פתרונות ל-Same Domain Policy עם פרוקסי הראיתי איך ניתן לעקוף את המגבלה הזו עם פרוקסי מבוסס PHP.
דרך אחרת היא שימוש בפלאש לשם התקשורת. במאמר על flXHR יש הסבר מקיף כיצד לעשות זאת.
JSONP היא דרך נוספת שאותה אסקור כאן. דרך זו הרבה יותר טבעית. במיוחד עבור API ו-WebServices שעובדים מול AJAXים אחרים.
העקרון מאחורי JSONP
העקרון הוא פשוט מאד. נכון שאנו לא יכולים לייבא אובייקטים או משאבים מדומיין אחר, אך אנו כן יכולים לייבא סקריפטים מדומיין אחר. כלומר להשתמש ב-src:
<script type="text/javascript" src="whatever.com/myscript.php" ></script>
וכמובן שאני גם יכול להעביר פרמטרים באמצעות אותו טריק:
<script type="text/javascript" src="whatever.com/myscript.php?parm1=a¶m2=b" ></script>
אם בקצה השני יהיה לי דף PHP (או כל דף צד שרת לצורך העניין) שידע לקבל את הפרמטרים האלו ולהחזיר לי אובייקט JSON הכולל מידע שאני רוצה. אז הכל יעבוד למרות שהדף שמחזיר לי מידע נמצא בדומיין אחר. זהו העקרון שלפיו אנו עובדים ועוקפים את ה-Same Domain Policy.
מקובל גם (לפחות בחלק מהשירותים) להעביר פונקצית callback שתתבצע אליה קריאה לאחר שהסקריפט ייטען. נושא זה יובהר בדוגמא.
ראשית אנו נציב script ב-head שלנו ואיתו אנו נבצע את התקשורת. הדבר היחידי שיש בתגית הזו ואין בדרך כלל הוא id. ה-id הזה יאפשר לי לשנות את ה-src למה שמתאים לי. הנה ה-script ששמתי ב-head.
<script type="text/javascript" id="myJSONPCall" src=""></script>
עכשיו כל מה שאני צריך לעשות זה לשנות את ה-src. ב-script אחר, שיימצא מתחת ל-script שיצרתי זה עתה. אני אכניס את השורות הבאות:
<script type="text/javascript">
var script = document.getElementById('myJSONPCall');
script.setAttribute('src', 'http://www.numenore.com/test/script.php?jsonCallback=myCallback');
</script>
מה עשיתי? ראשית יצרתי רפרנס ל-script הראשון באמצעות getElementById. שנית עם מתודת .setAttribute שיניתי את ה-src למקור שאני רוצה. העברתי גם פרמטר של jsonCallback שמפרט את ה-Callback שירוץ ברגע שהסקריפט ירד. עכשיו נעבור לצד השרת – כלומר ל-'http://www.numenore.com/test/script.php
כמובן שהדוגמא היא ב-PHP אבל היא יכולה להיות בכל שפת צד שרת שהיא.
ה-PHP שלי הוא מאד פשוט, הוא ייצור אובייקט JSON כלשהו (הארד טקסט), ויכניס אותו לתוך פונקצית ה-callback.
<?php
$myObject = array(
"text" => "Hello, I am data from the remote server.",
"created_at" => "Thu May 07 21:36:12 +0000 2009"
);
$myJSONObject = json_encode($myObject);
$myJSONCallback = filter_var($_REQUEST['jsonCallback'], FILTER_SANITIZE_STRING);
print "$myJSONCallback($myJSONObject)"
מה ה-PHP החביב הזה עושה? ראשית הוא יוצר לנו אובייקט חביב ומעביר אותו דרך json_encode על מנת ליצור string של JSON שאותו נוכל להעביר ב-script. אחרי כן הוא מבצע סניטציה לפרמטר jsonCallback ומדפיס אותו כך שיתקבל הדבר הבא:
myCallback({"text":"Hello, I am data from the remote server.","created_at":"Thu May 07 21:36:12 +0000 2009"})
וכאשר הדבר הזה נמצא בתוך script, הוא הופך להיות קריאה לפונקציית myCallback עם אובייקט JSON בתוכה כפרמטר. כל מה שנותר לי לעשות, זה לכתוב את פונקצית myCallback בקוד שלי.
function myCallback(obj) {
alert(obj.text);
}
ועכשיו להדגמה – ליחצו על הקישור החביב הזה וצפו ב-alert שמביא את המידע מהשרת המרוחק. ככה עוקפים Same Domain Policy.
JSONP עם jQuery
JSONP הופך להיות מאד מאד פשוט עם jQuery. למי שלא מכיר, jQuery הוא פריימוורק נהדר ל-JavaScript שמפשט מאד את כל העבודה. jQuery תומך ב-JSONP החל מגרסה 1.2. כדי לעבוד עם JSONP כל מה שאנו צריכים לעשות זה להשתמש בפונקצית jquery.ajax שבאופן אוטומטי יוצרת JSONP אם מדובר ב-url שהוא לא הדומיין שלנו. איך עושים את זה? אם נרצה לחקות את הדוגמא שלנו, אז אנחנו צריכים ליצור את הפונקציה הבאה:
$.ajax({
dataType: 'jsonp',
data: null,
jsonp: 'jsonCallback',
url: 'http://www.numenore.com/test/script.php',
success: function (response) {
alert(response.text)
},
});
jquery.ajax היא מתודה שאמורה להיות מוכרת לכל מי שעבד עם jQuery ו-AJAX (ולמי שלא מכיר – הנה מדריך בעברית ל-AJAX ו-jQuery שילמד אתכם איך). דרך המתודה עובר אובייקט שמגדיר את קריאת ה-AJAX. ה-dataType שלנו הוא jsonp (מנדטורי במקרה של JSONP). ה-data הוא הנתונים שאנו מעבירים – יכול להיות כל string שהוא. jsonp גם הוא מנדטורי והוא צריך להכיל את פונקצית ה-callback שלכם. גם אם לא צריך אחת כזו, ייתכן שהשרת דורש אותה. כמו במקרה שלנו. success היא מתודה שמעבירה את המידע המתקבל מהשרת. במקרה הזה כל מה שהיא עושה הוא להקפיץ alert.
והנה הדוגמא. לקישור הזה הצמדתי אירוע שיוצר קריאת AJAX. נסו ותהנו
מדובר כמובן בקריאת AJAX לכל דבר והאובייקט שאנו מעבירים דרך jquery.ajax מכיל את התכונות הרגילות שכל אובייקט AJAX ב-jQuery יכול לקבל.
עד כאן בנוגע ל-JSONP. דרך הרבה יותר אלגנטית ליצירת תקשורת בין דומיינים שונים.