how can JSONP (JSON + callback) format be enabled?

Comments

abritez’s picture

I am having issues with this myself. Have you gotten it to work on your end? Not sure if i did something wrong on my end, but the JSON/P patch didn't work for me.

valkum’s picture

Here are my JSONP changes:

works on this way:

http://example.com/path_to_view?jsonp=callback_name

--- Kopie von views_plugin_style_json.inc       2010-01-24 20:50:08.000000000 +0100
+++ views_plugin_style_json.inc 2010-01-24 12:31:18.000000000 +0100
@@ -28,7 +28,7 @@
     $form['format'] = array(
       '#type' => 'radios',
       '#title' => t('JSON data format'),
-      '#options' => array('Simple' => t('Simple'), 'Exhibit' => t('MIT Simile/Exhibit')),
+      '#options' => array('Simple' => t('Simple'), 'Exhibit' => t('MIT Simile/Exhibit'), 'JSONP' => t('JSONP'),),
       '#default_value' => $this->options['format'],
     );
   }
--- Kopie von views-view-json.tpl.php   2010-01-24 20:50:27.000000000 +0100
+++ views-view-json.tpl.php     2010-01-24 21:04:13.000000000 +0100
@@ -16,7 +16,7 @@

 if ($options['format'] == 'Simple') json_simple_render($view);
 if ($options['format'] == 'Exhibit') json_exhibit_render($view);
-
+if ($options['format'] == 'JSONP') json_JSONP_render($view);

 function json_simple_render($view) {
   define('EXHIBIT_DATE_FORMAT', '%Y-%m-%d %H:%M:%S');
@@ -100,3 +100,46 @@


 }
+
+function json_JSONP_render($view) {
+
+
+  define('EXHIBIT_DATE_FORMAT', '%Y-%m-%d %H:%M:%S');
+       $json = "{\n".'  "nodes":'."\n".str_repeat(" ", 4)."[\n";
+       $total_view_result_count = count((array)($view->result)); //cast the result object to an array so we can count how many properties in itt;
+       $view_result_count = 0;
+       foreach ($view->result as $node) {
+               $json.= str_repeat(" ", 6)."{\n";
+               $total_field_count = count((array)$node); //cast the node object to an array so we can count how many properties in itt
+               $field_count = 0;
+               foreach($node as $field_label => $field_value) {
+                 $label = trim(views_json_strip_illegal_chars(views_json_encode_special_chars($field_label)));
+          $value = views_json_encode_special_chars(trim(views_json_is_date($field_value)));
+          if ((is_null($value)) || ($value == '')) continue;
+//          if (preg_match('/\d/', $value)) {
+//            if (strtotime($value))
+//              $value = gmstrftime(EXHIBIT_DATE_FORMAT, strtotime($value));
+//          }
+          $label = str_replace('_value', '', str_replace("profile_values_profile_", '', $label)); //strip out Profile: from profile fields
+          $json.=str_repeat(" ", 8).'"'.$label.'"'. " ".": ".'"'.$value.'"'.((++$field_count == $total_field_count) ? "":",")."\n";
+               }
+               $json.=str_repeat(" ", 6)."}".((++$view_result_count == $total_view_result_count) ? "":",")."\n";
+       }
+       $json.=str_repeat(" ", 4)."]\n}";
+
+       if($_GET['jsonp'])
+          $jsonp = views_json_strip_illegal_chars(views_json_encode_special_chars($_GET['jsonp'])).'('.$json.')';
+        else
+          $jsonp = 'jsonp('.$json.')';
+  if ($view->override_path) { //inside a live preview so just output the text
+    print $jsonp;
+  }
+  else { //real deal so switch the content type and stop further processing of the page
+    drupal_set_header('Content-Type: text/javascript');
+    print $jsonp;
+    module_invoke_all('exit');
+    exit;
+ }
+
+}
+

allisterbeharry’s picture

Thanks so much for the patch. I'll commit it and enable the JSONP format in the next release which should be out today(26th).

valkum’s picture

i think it is better to name the jsonp variable callback and add jsonp as a true/false variable to standart json output. so if you call http://exmaple.com/test?jsonp=true&callback=test it return test(data)
the user can activate jsonp support for normal json extra.

In my code you shuld change the contenttyp i dont now if you have it done in dev release. ;)

i have write some code for dev. i will post it later. with secure callback function.

edit:

--- views-view-json.tpl.php2    2010-01-26 06:25:18.000000000 +0100
+++ views-view-json.tpl.php     2010-01-26 18:40:41.000000000 +0100
@@ -95,10 +95,26 @@
       print $json;
     }
     else {
-      // We want to send the JSON as a server response so switch the content
-      // type and stop further processing of the page.
-      drupal_set_header('Content-Type: application/json; charset=utf-8');
-      print $json;
+      if ( ($options['allow_jsonp']) && $_GET['jsonp'] == true ) {
+        // We want to send the JSON as a server response so switch the content
+        // type and stop further processing of the page.
+        //++
+        //Add $_GET['callback'] to json output
+        //Security for $_GET['callback']
+        if(!preg_match('/^[\w]+$/',$_GET['callback'])) {
+         drupal_set_header('HTTP/1.1 404 Not Found');
+          drupal_set_header('Status: 404 Not Found');
+        } else {
+          drupal_set_header('Content-Type: application/json; charset=utf-8');
+          print $_GET['callback']. '(' .$json. ');';
+        }
+      } else {
+        // We want to send the JSON as a server response so switch the content
+        // type and stop further processing of the page.
+        drupal_set_header('Content-Type: application/json; charset=utf-8');
+        print $json;
+
+      }
       module_invoke_all('exit');
       exit;
     }
--- views_plugin_style_json.inc2        2010-01-26 06:25:18.000000000 +0100
+++ views_plugin_style_json.inc 2010-01-26 18:18:55.000000000 +0100
@@ -40,5 +40,11 @@
       '#default_value'  => $this->options['using_views_api_mode'],
       '#description'    => t('Not using View API mode means the JSON gets output directly and the server ceases normal page processing.  Using it means the server does not cease processing after outputting the JSON.  This allows the Views API to be used with the view without having to prematurely terminate page processing.'),
     );
+    $form['allow_jsonp'] = array(
+      '#type'           => 'checkbox',
+      '#title'          => t('Allow use of JSONP'),
+      '#default_value'  => $this->options['allow_jsonp'],
+      '#description'    => t('Allow using of JSONP. When ?jsonp=true is given callback=? is added to json output.'),
+    );
   }
 }


Description:

you call the site with params jsonp and callback if jsonp is true you get jsonp output. if callback contains a-zA-Z0-9 you get the output else you get an 404 error. because of javascript hacking.

valkum’s picture

do you put this into the module ?

allisterbeharry’s picture

Assigned: Unassigned » allisterbeharry

Sorry, I haven't added it as yet - it will be in the beta1 release.

GloryFish’s picture

Has the patch been tested with 6.x-1.x-dev ?

I applied the patch from #4 to our install of 6.x-1.x-dev. I checked "Allow use of JSONP" and hit the path using jsonp=true&callback=mycallback

I get JSON data back from that, but it isn't wrapped in the callback function. Upon closer inspection it appears that the views_json_render() function in view-view-json.tpl.php file is never called. Am I missing an option or is there an issue with the patch. I'm guessing the former is more likely, but I wanted to be sure.

I've tested with the following options:

- Field output: both normal and raw
- Plaintext output: checked
- JSON Data Format: Simple
- Content type: both "application/json" and "text/json"
- Views API mode: unchecked
- Allow use of JSONP: checked

kostajh’s picture

subscribing

jarchowk’s picture

#4 patch Worked for me just fine

cap60552’s picture

Version: 6.x-1.0-alpha3 » 6.x-1.x-dev
StatusFileSize
new927 bytes
new3.75 KB

#4 patch would not apply to the current 6.x-1.x-dev build due to the fundamental changes in the dev version.

I have created a new patch that takes these new designs into account, and seems to work for me. I have attached the patch, and the one additional theme file needed to implement JSONP simple.

The gzip / tarball contents should be placed in the 'views_datasource/theme' directory. To use JSONP, select "Simple JSONP Callback" as the JSON Data format under the style settings in your view. Then pass the URL of your view the standard callback= parameter in your javascript and you should receive a valid jsonp result.

Eg: http://www.example.com/path/to/my/view?callback=

allisterbeharry’s picture

Thanks valkum and cap60552 for the code, sorry I've taken so long to include this. I just took your idea and implemented in in the latest views_json module. There is now an option called JSONP Prefix; if you specify a text string here, the JSON output will be enclosed in parentheses and prefixed with the text string. This code is committed to CVS and will be in the beta2 release

allisterbeharry’s picture

Status: Active » Fixed

Added JSON support in beta2: http://drupal.org/node/855894

Status: Fixed » Closed (fixed)

Automatically closed -- issue fixed for 2 weeks with no activity.

cap60552’s picture

Status: Closed (fixed) » Needs work

Your solution doesn't seem to allow for the passing of the prefix via "callback=" rather it only allows for a predesignated function name to be appended to the json at the time the view is setup. In JSONP the function name is an anonymous function who's name is generated at runtime, so the function name (the prefix) changes with each call and cannot be specified in advance.

arthur5005’s picture

Component: Documentation » Code
Category: support » feature

Yes, imho this is not fixed. It's safer, more flexible and better implemented if you don't have to define a globally accessible function ahead of time. In my case I have an embedded webapp which checks for updates on our company website. I've had to include a global callback in my javascript in order to handle the JSONP reply, which is causing all kinds of ugliness. I would be for allowing the current functionality and also optionally allowing for a dynamically generated callback through a parameter 'callback='.

This is how jQuery expects JSONP to be implemented, and as such it's how this module should implement it as well. I'll have a bit of free time soon, I'll look into proposing a patch.

mcantelon’s picture

If you drop the file linked to below in your theme directory (naming it 'views-views-json-style-simple.tpl.php') then go to your modules page and click 'Update' you'll be good to go:

http://gist.github.com/raw/578650/dcf4660d08a3458373d7754cd1f63de185dfa4...

(It makes the JSONP work like it should, playing nice with Jquery and the like.)

ianchan’s picture

How do I format the URL to retrieve the JSONP output? IF I use http://mydomain.org/feed/?callback=, I get an error in Firefox saying that my prefix is undefined. Could someone provide an example of how to make this work? Thanks!

jlporter’s picture

allowing jsonp to any json view with a url argument will be a big security issue. It should only be configured by the user with administer views permission. Otherwise any site could pull your views/json data and use it as their own.

cap60552’s picture

ianchan, using jquery the following example snipit works for me:

   $.getJSON("https://www.example.com/ajax/hours/?callback=?",
        function(data){
        });

The second ? in jquery is the key to solving your undefined prefix problem. This causes jquery to generate a random callback function name such as "AdEgS00421" that is passed along in place of the second ? in the getJSON functions URL string.

The resulting output from the server would be something such as:

AdEgS00421({"Monday": "8:00 am - 9:30 pm", "Tuesday" : "8:00 am - 4:30 pm"})
cap60552’s picture

jlporter

The whole point of jsonp is to allow code on domain a, to access data from domain b. This is only a problem if the JSON-P encoded data contains sensitive information. In my case, I am using it to allow information about our library, such as the library's hours, or the status of our online chat with a librarian system to be embedded into other sites.

This is very useful in the case of creating widgets that are embedded into research databases provided by 3rd party vendors.

liminu’s picture

Not works with current alpha, can you give me a module that woks with jsonp?

bc’s picture

mcantelon + others who need a jquery-style callback GET value,

The template in that gist works well, but for security you should scrub the $_GET input like so:

if ($jsonp_prefix && !preg_match('/\W/', $_GET[$jsonp_prefix])) {
  // we have a nice clean callback function to inject
  $json = $_GET[$jsonp_prefix] . "($json)";
} else {
  // go with default callback
  $json = $jsonp_prefix . "($json)";
}
SilviaT’s picture

How about the D7 version? Is the jsonp been handled? I'm having issues too.

bc’s picture

You can use the same code described above, but you'll need to change drupal_set_header to drupal_set_http_header

bc’s picture

updated gist, rolls together thread tidbits: https://gist.github.com/1795328

dankh’s picture

if ($jsonp_prefix && !preg_match('/\W/', $_GET[$jsonp_prefix]))

Doesn't work with Dojo JSONP. Dojo callbacks are in the form : dojo.io.script.jsonp_dojoIoScript1._jsonpCallback

AFAIK the regular expression \W is "any non-word character" which is too restrictive for Dojo. The wanted regular expression is the one that matches the name of a single JavaScript function.

I have researched what are the characters allowed in a JavaScript function name, but there is a lot of stuff allowed (http://www.ecma-international.org/publications/standards/Ecma-262.htm). So I just wrote something more permissive :

if ($jsonp_prefix && preg_match('/[a-zA-Z0-9_.]+/', $_GET[$jsonp_prefix]))

Note that I have removed the negation. This basically allows dot in function name.

Ecio’s picture

About the gist linked in #25:
the commented Drupal7 change to drupal_http_header is not right, i had to modify it in this way:

 drupal_add_http_header("Content-Type", "$content_type; charset=utf-8"); 

in order to use the right D7 function and parameter passing

bc’s picture

nice catch, changed the gist :) drupal_function_names_from_memory()

yannickoo’s picture

yannickoo’s picture

Status: Needs work » Closed (fixed)

I close this issue because #1673488: JSONP has no dynamic callback was fixed :)

yannickoo’s picture

Status: Closed (fixed) » Closed (duplicate)

Duplicate makes more sense...