How to implement JSONP to download XML resource
According to the same-origin security policy, web page can freely share images, stylesheets, scripts… but web fonts and AJAX requests are only accessible from the same domain.
Sometimes this is not desired behaviour, such as when getting a json / xml file from a different domain, using AJAX. In such cases, it all comes down to two solutions :
1- The source sets its header to allow specific domain cross-origin resource sharing (CORS). In PHP, this is done with header
function :
2- Use JSONP.
JSONP is short for JSON padding, a well-known “workaround” to solve CORS. It only works with GET.
To send AJAX requests using JSONP, the server must first knows how to respond to JSONP requests. It usually detects JSONP requests using callback
parameter.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
<?php
// compute result
[...]
// output result
JsonpHelper::outputXML($result);
// json helper class - credits : @andibraeu
class JsonpHelper {
private static function is_valid_callback($subject)
{
$identifier_syntax
= '/^[$_\p{L}][$_\p{L}\p{Mn}\p{Mc}\p{Nd}\p{Pc}\x{200C}\x{200D}]*+$/u';
$reserved_words = array('break', 'do', 'instanceof', 'typeof', 'case',
'else', 'new', 'var', 'catch', 'finally', 'return', 'void', 'continue',
'for', 'switch', 'while', 'debugger', 'function', 'this', 'with',
'default', 'if', 'throw', 'delete', 'in', 'try', 'class', 'enum',
'extends', 'super', 'const', 'export', 'import', 'implements', 'let',
'private', 'public', 'yield', 'interface', 'package', 'protected',
'static', 'null', 'true', 'false');
return preg_match($identifier_syntax, $subject)
&& ! in_array(mb_strtolower($subject, 'UTF-8'), $reserved_words);
}
public static function outputXML($string) {
# JSON if no callback
if( ! isset($_GET['callback']) )
exit($string);
$string = str_replace("\n", " ", str_replace("'", '"', $string));
# JSONP if valid callback
if(JsonpHelper::is_valid_callback($_GET['callback']))
exit("{$_GET['callback']}('$string')");
# Otherwise, bad request
header('status: 400 Bad Request', true, 400);
}
}
Line 29 is dedicated to replace all single-quote characters to double-quote, and new lines to simple space. This is to be able to pass xml document as valid javasript string parameter.
Then, the client can use jQuery to get the document from a different domain (it works on same domain as well, which is convenient for testing & development) :
1
2
3
4
5
6
7
8
9
$.ajax({
url : source_url,
dataType : 'jsonp', // if dataType is set to 'jsonp'
// a 'callback' parameter will be appended to the request url
success: function(data) {
$data = $($.parseXML(data));
items = $data.find('item');
}
});
Something worth noticing on the client side is on line 5 : the data needs to be converted to xml first, with $.parseXML
before converting to jQuery object. Converting the result string directly to jQuery won’t work : $data = $(data)
;.
That’s about it ! To know more about JSONP, please refer to its wiki https://en.wikipedia.org/wiki/JSONP.
References :