JSON-RPC for twisted.web2
Dependencies
- Twisted (+twisted.web2)
- simplejson
- setuptools
Notes
The original tests for twisted.web2.xmlrpc were also included/adapted and pass successfully.
Import
This is a stand-alone package that uses the adytum namespace, so if you have any other adytum code installed, you'll need to use setuptools' pkg_resource module:
>>> from pkg_resources import require
>>> require('Twisted-JSONRPC')
>>> from adytum.twisted.web2 import jsonrpcIf not, then you can simply use the standard import:
>>> from adytum.twisted.web2 import jsonrpc
Usage
Server
This is a direct copy from twisted.web2.xmlrpc and you use it identically, substituting JSONRPC and jsonrpc everywhere you see XMLRPC and xmlrpc. The docs for t.w2.xmlrpc are not up yet, but here are some example usages:
from twisted.web2 import server
from twisted.web2.channel import http
from twisted.application import service, internet
from adytum.twisted.web2 import jsonrpc
class Example(jsonrpc.JSONRPC):
"""An example object to be published."""
addSlash = True
def jsonrpc_echo(self, request, x):
"""Return all passed args."""
return x
def jsonrpc_add(self, request, a, b):
"""Return sum of arguments."""
return a + b
site = server.Site(Example())
chan = http.HTTPFactory(site)
application = service.Application("Example JSON-RPC Server")
jsonrpcServer = internet.TCPServer(7080, chan)
jsonrpcServer.setServiceParent(application)Save this as server.tac and run with the following command:
$ twistd -noy server.tac
Client
As far as I know, twisted.web2 doesn't have a (functional? good? any?) client yet. For this reason, t.w2.xmlrpc and a.t.w2.jsonrpc unit tests both use t.w for the client. We will do the same here:
from adytum.twisted.web.jsonrpc import Proxy
from twisted.internet import reactor
def printValue(value):
print repr(value)
reactor.stop()
def printError(error):
print 'error', error
reactor.stop()
proxy = Proxy('http://127.0.0.1:7080/')
proxy.callRemote('add', 3, 5).addCallbacks(printValue, printError)
reactor.run()Save this file as client.py and run it from the command line. You should see an '8' printed to stdout.
You can use the client from jsolait.net with this, e.g.
from twisted.web2 import server, static from twisted.web2.channel import http from twisted.application import service, internet, strports from adytum.twisted.web2 import jsonrpc # Serve the inventory directory toplevel = static.File("/var/www/jsolait") class JsolaitTest(jsonrpc.JSONRPC): """An example object to be published.""" addSlash = True child_main=toplevel def jsonrpc_echo(self, request, x): """Return all passed args.""" return x def jsonrpc_add(self, request, a, b): """Return sum of arguments.""" return a + b def jsonrpc_system_listMethods(self, request): return ["echo", "add"] application = service.Application("jsolait test server") #Start the server on 7080 jsite = server.Site(JsolaitTest()) jsonrpcServer = internet.TCPServer(7080, http.HTTPFactory(jsite)) jsonrpcServer.setServiceParent(application)<html> <head> <script type="text/javascript" src="/main/jsolait/jsolait.js">//foo</script> <script type="text/javascript" src="/main/jsolait/lib/urllib.js">//foo</script> <script type="text/javascript" src="/main/jsolait/lib/jsonrpc.js">//foo</script> <script type="text/javascript"> var test=new jsolait.modules.jsonrpc.ServiceProxy("http://localhost:7080/", ["echo","add"]); </script> </head> <body> Loaded. </body> </html>Suggest you use firebug (e.g. via F12) to play with it.
Hm. I'm having problems with this: the returned value is put in a single element array rather than something like {id:blah, request:blah, error:null} and so is ignored by the JSON-RPC implementation.
A simple hack to try and make this work. Not sure why this isn't the normal behaviour, and I'd think an implementation that dealt with more cases and at least used ids in the returned objects would be necessary.
from twisted.web2 import server, static, channel from twisted.application import service, internet from adytum.twisted.web2 import jsonrpc #for overridden _cbRender method. from twisted.web2 import http, responsecode, http_headers from adytum import jsonrpclib # Serve the main directory toplevel = static.File("/var/www/webapp/") class JsolaitTest2(jsonrpc.JSONRPC): addSlash = True child_main=toplevel def jsonrpc_echo(self, request, x): """Return all passed args.""" return x def jsonrpc_add(self, request, a, b): """Return sum of arguments.""" return a + b #Return values are wrong: returning an array with the returned value (e.g. if a+b==42, returning [42] for the add fn) rather than, e.g. #{"id":'httpReq', "result":'hello echo', "error":null} #Hack to fix this: def _cbRender(self, result, request): if not isinstance(result, jsonrpclib.Fault): #Was result=(result,) result = {"result":result,"error":None} try: s = jsonrpclib.dumps(result) except: f = jsonrpclib.Fault(self.FAILURE, "can't serialize output") s = jsonrpclib.dumps(f) return http.Response(responsecode.OK, {'content-type': http_headers.MimeType('text', 'json')}, s) #Start the JSONRPC server on 7080 site = server.Site(JsolaitTest2()) chan=channel.http.HTTPFactory(site) application = service.Application("JSolait Test Server") jsonrpcServer = internet.TCPServer(7080, chan) jsonrpcServer.setServiceParent(application) #$ twistd -noy main.tac