Basic example of retrieving a node from a Drupal server using JSON/JQuery?

Events happening in the community are now at Drupal community events on www.drupal.org.
stodge's picture

This might be a dumb question but are there any usable examples of retrieving a node's content and title from a Drupal server using JSON in Javascript? I've scoured Google and Drupal but nothing pops up that is usable. Any quick pointers appreciated. Thanks

Comments

Use JQuery and this

sumitk's picture

Hi Stodge,

Use JQuery's ajax function to make services call .. this will save you couple of hrs ;-)

    $.ajax({
     type: "POST",
     url: "http://iphone.tut2tech.com/services/json",
     dataType: "json",
     data: {"method": "node.get", "nid": 15},
     success: function(msg){
       alert( "Data: " + msg["#data"].nid );
     }
    });

Howcome your URL is

FreddieK's picture

What about authentification? When I try to call the server I get 'access denied' even though I don't have any authentification set.

  $.ajax({
        type: "POST",
        url: "http://localhost:8888/services/services/json-rpc",
         dataType: "json",
        data: { method: '"node.get"', nid: 15 },
       success: function(response){
         alert(response["#data"]);
        console.log(response);
       }
});

Logs an error:

{ "#error": false, "#data": { "#error": true, "#message": "Access denied" } }

This works when you don't

jazzdrive3's picture

This works when you don't have any authentication though, right? What about key authentication?

I keep seeing conflicting information. Do you just need to send the api key? Or do you need to hash it with something else first?

And then how do you send the api key? In the services testing and browsing pages on my site, it uses Hash. Is that what I need to use?

I'm trying to use key authentication with sessid.

Thanks for the help!

Getting it to work

HorsePunchKid's picture

There are several gotchas, or at least things that don't quite work how you'd expect them to. To answer your question, though: No, you don't need to send the API key. You use it with HMAC-SHA256 to generate the hash, and then you send the hash and the sessid. You can get the sessid by first calling system.connect, which doesn't require a sessid. Once you have a sessid, you can pass it along just like you would any other parameters.

In particular, it seems that you cannot pass them along as the code example above suggests. It seems that you need to do this awkward double-quoting for strings when using the JSON service. I don't know why the JSON service requires this pseudo-JSON input format. A jQuery data argument like this is closer to the mark:

data: {
  method: '"node.get"',
  hash: '"'+hmac_sha256(...)+'"',
  domain_name: '"example.com"',
  domain_time_stamp: '"'+timestamp+'"',
  nonce: '"'+nonce+'"',
  nid: 15
},

You'll need to find a Javascript implementation of HMAC-SHA256; I'm sure it's out there somewhere. Keep in mind that your API key is going to end up there in your Javascript file.

-- Steven N. Severinghaus <sns@severinghaus.org>

Thank you thank you. Just

jazzdrive3's picture

Thank you thank you. Just what I needed. I appreciate it.

Now you wouldn't know why the views.get methods doesn't work, would you?

My issue it located at http://drupal.org/node/882420 , although I'm not sure that's the right place.

More gotchas, but maybe not much more help

HorsePunchKid's picture

I don't see anything obviously wrong. It will probably take some digging. I thought using the taxonomy service module would be straightforward, but there were unexpected quirks that took putting debug statements in the taxonomy_service module, the JSON server module, and the system taxonomy module just to figure out.

The worst problem was that none of the defaults worked. I was using taxonomy.selectNodes and figured I could just pass a tid, not specifying the sort order, pager, fields, and all these other things that should just work. But in fact, all of these optional fields were just getting passed to the taxonomy service module as NULL, overriding the reasonable defaults (bad), which then passed them on to the system taxonomy module without any kind of sanity checks (bad), leading to SQL like ORDER BY LIMIT 0,10 (bad), and not handling or reporting the errors in any meaningful way (bad).

i think you'll need to dig deep into the module(s) and figure out what causes the "missing required arguments" message, then work your way backward from there. If you figure something out, please post about it here or on your issue; there is a dearth of information on getting these cranky modules to work properly.

-- Steven N. Severinghaus <sns@severinghaus.org>

Which kind of debug

jazzdrive3's picture

Which kind of debug statements did you have in mind? I'm just a little new to some of this hardcore stuff. I'm mainly a themer ;)

I know enough PHP and custom module development to get around the block.

Thanks.

I couldn't really tell you

HorsePunchKid's picture

I couldn't really tell you without going into the code. If it's ostensibly missing arguments, you can look for that error message, figure out what test is done that makes it get shown, and perhaps just before that, dump out the list of all the arguments that it thinks it has received. That's basically what I had to do with the taxonomy module.

I hope you can get it working. This whole services package needs to have all this awkwardness ironed out or at least documented somehow.

-- Steven N. Severinghaus <sns@severinghaus.org>

Hah! Figured it out. Did a

jazzdrive3's picture

Hah! Figured it out. Did a print_r before the services module called the error and found that it still thought the "view_name" argument was missing.

Tried the following and it suddenly started working!

$.ajax({
url: service_url,
dataType: "jsonp",
data: {"method" : "views.get", "view_name" : "\"rest_a\"" },
success: function(data){
if (data["#error"] == false) {
   console.log(data);
   $("#progress").toggle();
} else {
   console.log(data);
   alert( "Error: " + data["#data"] );
   $("#progress").toggle();
}
}
});

The views.get method, at least going through the json server, needs escaped quotes around the view_name argument. Blah. I knew it had to be something stupid.

Thanks for the help.

Are you using the json server

FreddieK's picture

Are you using the json server module for this, and if so, have you modified it to get jsonp to work?

trying to get this working

sunset_bill's picture

It sure looks straightforward enough, but I'm having no luck yet. Based on the above, I've got this:

console.log('begin');
  jQuery.ajax({
     type: "POST",
     url: http://localhost/services/json-rpc,
     dataType: "json",
     data: {method: '"node.get"', nid: 47},
     success: function(msg){
       alert( "Data: " + msg["#data"].nid );
       console.log(msg);
     }
  });
console.log('done');

I get the begin and done messages in my console, but I don't see any evidence of anything happening in between. When I try
http://localhost/services/json-rpc

directly in my browser, I at least get an "Invalid method" error, but even adding an error: element in my ajax call, nothing happens.

arg,
SB

Still at it

sunset_bill's picture

I'm now trying to do this with a prototype.js Ajax request. Here's my function

function protoGetNode(){
  var send = {method: "node.get", nid: 47};
  //send = Object.toJSON(send);

  new Ajax.Request('http://localhost/drupal6/services/json-rpc', {
    method: 'post',
    parameters: send,
    onSuccess: function(transport){
      var json = transport.responseText.evalJSON();
alert(json.error.message); // just for convenience, hoping not to see this
      // now, do actual useful stuff
    }
  });

}

It gets to my onSuccess function, which is good, but I'm now getting an error telling me "No parameters received, the likely reason is malformed json, the method 'node.get' has required parameters." However, when I check my Firebug console, it shows both a method and an nid being sent
Parameters    application/x-www-form-urlencoded
method    node.get
nid    47

If I uncomment the toJSON line, I get an error saying "The received JSON not a valid JSON-RPC Request". I've also tried putting the nid into a params array
{method: "node.get", params: {nid: 47}}
But then params shows up as merely an Object if I don't call toJSON, and I get "The received JSON not a valid JSON-RPC Request" if I do call toJSON.

arg*2,
SB

Is this a bug in Drupal's

stodge's picture

Is this a bug in Drupal's json module? It might be returning incorrect/invalid json?

don't think so

sunset_bill's picture

I put

error_log(print_r($in));

in the jsonrpc_server module to see what it's getting, and now what I get in Firebug is
Array
(
    [method] => node.get
    [nid] => 47
    [params] =>
)
{"error":{"name":"JSONRPCError","code":-32700,"message":"No parameters received, the likely reason is malformed json, the method 'node.get' has required parameters."},"version":"1.1"}

So,
var send = {method: "node.get", nid: 47};

is the problem, but I'm having no luck figuring out how to move that [nid => 47] into [params].
My Javascript is lame enough that I'm now just flailing.

I'm having the same

stodge's picture

I'm having the same problem:

params = urllib.urlencode({
'method': 'node.get',
'params': {
'nid': 1,
},
})

Gives:

{"error":{"name":"JSONRPCError","code":-32700,"message":"No parameters received, the likely reason is malformed json, the method 'node.get' has required parameters."},"version":"1.1"}

params = urllib.urlencode({
'method': 'node.get',
'nid': 1,
'params': {
},
})

Gives:

{"error":{"name":"JSONRPCError","code":-32602,"message":"Argument 'nid' is required but was not received"},"version":"1.1"}

Ok I'm one step closer but

stodge's picture

Ok I'm one step closer but I'm getting "Token has expired" on login, even though I'm running the client and server on the same PC and token expiry time is set to 180. Here's my code:

import hashlib
import urllib
import urllib2
import cookielib
from django.utils import simplejson
import random
import string
import datetime
import sys
import hmac
import hashlib

url = "http://localhost/drupal/services/json-rpc"

cookiejar = cookielib.CookieJar()
urlOpener = urllib2.build_opener(urllib2.HTTPCookieProcessor(cookiejar))
csrf_token = ''

def generate_password(length=8, chars=string.letters + string.digits):
   return ''.join([random.choice(chars) for i in range(length)])

api_key = "8fac7d020a01b256209879bb499b959a"
#~ domain = "localhost.local"
domain = "antelope"

def generate_hash(command, ts, domain, api_key, nonce):

    msg = "%s;%s;%s;%s" % (ts.isoformat(),domain,nonce,command)
  hm = hmac.new(api_key, msg, hashlib.sha256)
    print(hm.hexdigest())
  return hm.hexdigest()

def connect():
  ts = datetime.datetime.utcnow()
    command = 'system.connect'
   nonce = generate_password(10)
  hash = generate_hash(command, ts, domain, api_key, nonce)

data = {
   }

headers = {'X-Requested-With': 'XMLHttpRequest'}
   params = urllib.urlencode({
        'method': command,
       'params': simplejson.dumps(data),
    })

    session_id = ""
  username = ""
    uid = -1
   roles =  None
 
   request = urllib2.Request(url, params, headers)
    try:
       result = urlOpener.open(request)
       content = result.read()
        d = simplejson.loads(content)
  except Exception, e:
       print(str(e))
      sys.exit(-1)

  # Decode the result.
   print(d)
   session_id = d['result']['sessid']
uid = d['result']['user']['uid']
roles = d['result']['user']['roles']

   session_data = {
       'session_id': session_id,
        'uid': uid,
      'roles': roles,
  }

return session_data

def login(session_data):
  ts = datetime.datetime.utcnow()
    command = 'user.login'
   nonce = generate_password(10)
  hash = generate_hash(command, ts, domain, api_key, nonce)

data = {
       'hash': hash,
        'domain_name': domain,
       'domain_time_stamp': ts.isoformat(),
     'nonce': nonce,
      'sessid': session_data['session_id'],
      'username': 'test',
        'password': 'test',
    }

headers = {'X-Requested-With': 'XMLHttpRequest'}
   params = urllib.urlencode({
        'method': command,
       'params': simplejson.dumps(data),
    })
request = urllib2.Request(url, params, headers)
    try:
       result = urlOpener.open(request)
       content = result.read()
        print(content)
except Exception, e:
       print(str(e))
      sys.exit(-1)
  
def get_node(nid, session_data):
  command = "node.get"
ts = datetime.datetime.utcnow()
    nonce = generate_password(10)
  hash = generate_hash(command, ts, domain, api_key, nonce)

data = {
       'nid': 1,
        'hash': hash,
        'domain_name': domain,
       'domain_time_stamp': ts.isoformat(),
     'nonce': nonce,
      'sessid': session_data['session_id'],
  }

headers = {'X-Requested-With': 'XMLHttpRequest'}
   params = urllib.urlencode({
        'method': command,
       'params': simplejson.dumps(data),
        'fields': [
          'title',
         'body',
      ],
})
request = urllib2.Request(url, params, headers)
    try:
       result = urlOpener.open(request)
       content = result.read()
        print(content)
except Exception, e:
       print(str(e))
      sys.exit(-1)
      
  
if name=="main":
    session_data = connect()
   login(session_data)
    #~ get_node(1, session_data)

Any suggestions appreciated.

I solved my problem -

stodge's picture

I solved my problem - timestamps have to be passed as number of seconds since the epoch, not a string.

Services

Group organizers

Group categories

Group notifications

This group offers an RSS feed. Or subscribe to these personalized, sitewide feeds: