Version 2 (modified by anonymous, 14 years ago) (diff)



This is an easy-to-use implementation of a JSON-RPC handler for Django.


Example Django Usage


def my_rpc_view(request):

    from jsonrpc import JsonRpc
    class MyRpcMethods(object):

        url = reverse("myapp-rpc")

        public__add(self, x, y):
            return x+y

        public__subtract(self, x, y):
            return x-y

        public__multiply(x, y):
            return x*y

        public__find_person(self, attribs):

            filters = {}
            for key in attribs.keys():
                if key in ("first_name", "last_name"):
                    filters[key] = attribs.get(key)

            return Person.objects.filter(**filters).values()

    rpc = JsonRpc( MyRpcMethods() )
    result = rpc.handle_request(request)

    return HttpResponse(result)

Example Javascript client usage

var rpc = new dojo.rpc.JsonService("/myapp/rpc/");

rpc.add(3,4).addCallback(function(result) {
>>> 7

rpc.callRemote("subtract", [9,5]).addCallback(function(result) {
>>> 4

rpc.multiply(5, 4).addCallback(function(result) {
>>> 20

rpc.find_person({last_name: "Baggins"}).addCallback(function(result) {
    dojo.forEach(result, function(person) {
      console.log("Found: " + person.first_name, person.last_name);
>>> Found: Bilbo Baggins
>>> Found: Frodo Baggins


# Copyright (c) 2009, Ben Wilber (
# All rights reserved
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License. You should have
# received a copy of the GPL license along with this program; if you
# did not, you can find it at

class JsonRpc(object):

    def __init__(self, instance=None, url=None, allow_errors=True, auto_smd=True):
            'instance' is required and is an object containing public
            RPC methods (like what you would pass to register_instance in SimpleXMLRPCServer)
            Passing arbitrary functions (register_function) isn't supported yet

            'url' is optional if given as an attibute of the instance.  It's where the JSON-RPC
            client posts its RPC requests

            'allow_errors' tells the serializer to report exception values under the key 'error' in
            the result dict back to the client.  If set to False and an exception occurs, the client
            will just get: {'error': 'error'}

            'auto_smd' lets us introspect the instance to find the RPC methods and generate the
            the appropriate method/signature descriptions.

            If the instance contains a dict attribute 'methods', then that will be used instead

            class MyRpcMethods(object):
                methods = {'add': ['x','y']}

                # Will be reported
                public__add(self, x,y): return x+y

                # Wont be reported
                public__subtract(self, x,y): return x-y

            This is useful if you want to filter the reported methods based on some other condition
            than the 'public__' prefix  --- be advised that a clever client can still call non-reported
            methods via a stringified method name ie with Dojo's 'callRemote' method.  So this shouldn't
            be used for securty.  See the 'dispatch' method below if you want to secure your public RPC methods.


        if instance is None:
            raise Exception("'instance' needed")
        self.instance = instance

        if url is None:
            if hasattr(self.instance, "url"):
                url = str(self.instance.url)
                raise Exception("'url' needed")
        self.url = url

        self.allow_errors = allow_errors

        self.smd = {
            "serviceType": "JSON-RPC",
            "serviceURL": self.url,
            "methods": []

        if hasattr(self.instance, "methods") and \
            isinstance(self.instance.methods, dict):
            for key in self.instance.methods.keys():
                params = [ {"name": val} for val in self.instance.methods[key] ]
                    "name": key,
                    "parameters": params
        elif auto_smd:
            public_methods = [ m for m in dir(instance) \
                if ( m.startswith("public__") and callable(getattr(instance, m)) ) ]
            if public_methods:
                    import inspect
                    for method in public_methods:
                        sig = inspect.getargspec(getattr(instance, method))
                            "name": method.replace("public__", ""),
                            "parameters": [ {"name": val} for val in sig.args if (val != "self") ]

    def dispatch(self, method, params):
            The default dispatcher method.

            It looks for a method in the supplied instance
            of the form 'public__method' and calls it
            with the params.  If a method 'dispatch' exists
            in the supplied instance, then it will be used


                class MyRPCMethods(object):

                    def dispatch(self, method, params):

                        if method == 'get_soup':
                            return 'No soup for you!'
                        elif method in ('get_cake', 'get_pie'):
                            return getattr(self, method)(*params)
                            return 'Not on the menu!'

        if hasattr(self.instance, "dispatch") and \
            return self.instance.dispatch(method, params)
            if hasattr(self.instance, "public__" + method):
                return getattr(self.instance, "public__" + method)(*params)
                return "method not supported"

    def serialize(self, raw_post_data):
            Serializes the POST data from request.raw_post_data into JSON,
            calls the dispatcher to route the method call, then serializes
            and returns the response.

            TODO: Better error handling!

        raw_request        = simplejson.loads(raw_post_data)
        request_id        = raw_request.get("id", 0)
        request_method    = raw_request.get("method")
        request_params    = raw_request.get("params", [])

        response        = {"id": request_id}

            response["result"] = self.dispatch(request_method, request_params)
            if self.allow_errors:
                from sys import exc_type, exc_value
                response["error"] = "%s: %s" % (exc_type, exc_value)
                response["error"] = "error"

        return simplejson.dumps(response)

    def handle_request(self, request):
            Takes an HttpRequest object and returns a JSON data structure
            either of the results of an RPC method call or
            the SMD if it was a GET request, or the POST was empty.

            In most cases, this can be returned directly to the client:

            return HttpResponse(rpc.handle_request(request))

        if request.method == "POST" and \
            len(request.POST) > 0:
            return self.serialize(request.raw_post_data)
            return simplejson.dumps(self.smd)

Back to Top