crossbar
crossbar copied to clipboard
WAMP/Klein example (2014) server_web.py trying to update to work in 2022
If someone can help me get it working, I'm happy to do a pull request and update the klein example, which is very old and doesn't work with any current versions of crossbar or autobahn/python.
To handle Klein request/response asynchronously, the example code (2014) needs updating for crossbar 21.3.1 and newer, as the example code uses the keyword "standalone" which errors out as it was for way back when WAMP router shipped with autobahn/python (now separated).
The problem is the last line of https://github.com/crossbario/autobahn-python/blob/master/examples/twisted/wamp/app/klein/example1/server_web.py
wampapp.run("ws://127.0.0.1:9000", "realm1", standalone=False)
What I tried so far:
I included the squaring rRPC function in one of my existing components. That registered the squaring function fine, so no need for server_wamp.py as used in the example.
It also meant I could remove the problem line:
wampapp.run("ws://127.0.0.1:9000", "realm1", standalone=False)
from server_web.py which now is:
from klein import Klein
from twisted.internet import reactor
from twisted.internet.defer import inlineCallbacks
from autobahn.wamp import register
from autobahn.twisted.wamp import ApplicationSession
from autobahn.wamp.types import PublishOptions
webapp = Klein()
class ClientSession(ApplicationSession):
def onConnect(self):
[...]
def onChallenge(self, challenge):
[...]
@inlineCallbacks
def onJoin(self, details):
## REGISTER the squaring rRPC for the Klein app to call remotely
##
def square(x):
print(f'square() called with {x}')
return x * x
try:
reg = yield self.register(square, 'com.test.square')
print(f'procedure square() registered')
except Exception as e:
print(f'could not register procedure: {e}')
try:
res = yield self.call('com.test.square', 3)
print(f'call result: {res}')
## works as expected: produces '9' when first running server_web.py
except Exception as e:
print(f'call error: {e}')
@webapp.route('/square/submit', methods=['POST'])
def square_submit(request):
print(f'com.test.square called with "request": {request}')
x = int(request.args.get('x', [0])[0])
res = yield self.call('com.test.square', x)
print(f'res: {res}')
# returnValue("{} squared is {}".format(x, res))
def onLeave(self, details):
self.disconnect()
def onDisconnect(self):
reactor.stop()
if __name__ == '__main__':
from autobahn.twisted.wamp import ApplicationRunner
from twisted.web.server import Site
reactor.listenTCP(8081, Site(webapp.resource()))
runner = ApplicationRunner(
url='ws://127.0.0.1:9000/ws',
realm='realm1'
)
runner.run(ClientSession)
I used test_web.html:
<!DOCTYPE html>
<html>
<body>
<form action="http://localhost:8081/square/submit" method="post">
<p>
Square this <input type="number" name="x" value="23" />
</p>
<p>
<input type="submit" />
</p>
</form>
</body>
</html>
The above tests the squaring function, so I know that is registered successfully, and server_web.py runs fine until I try to click the submit button in test_web.html, when the following traceback: Unhandled Error writing response [...] builtins.TypeError: object of type 'generator' has no len()
yes, indeed, this example stems from a time when autobahn and crossbar still were one package. this no longer works, as both are separated now.
what would be needed is a crossbar web service for klein .. probably. we already have a lot of options in crossbar rgd webservices, and also mapping rest and wamp ... so I would first try to understand what you actually want
eg there is a powerful feature
https://github.com/crossbario/crossbar-examples/tree/master/webservices/wap
this allows you to provide REST endpoints, which will forward to WAMP RPC calls, with the result rendered through Jinja2 templates into HTML returned in the REST endpoint, eg
https://github.com/crossbario/crossbar-examples/blob/3dbda8d44f4c8ac261714a97fd94f6d6462743c1/webservices/wap/.crossbar/config.json#L68
Thanks, I need simply to have Klein (or something else but Klein seems very lightweight and made with Twisted) listen for a POST (twilio app) at a url endpoint, and have python call an rRPC that's already registered and return it to the browser.
My modified server_web.py above works mostly, including having Klein listen and the @webapp.route() part fires. And within that def, this line doesn't cause an error:
res = yield self.call('com.test.square', x)
but trying to print or use the res results in:
builtins.TypeError: object of type 'generator' has no len()
Given that you already have your backend WAMP procedure you can achieve this using WAP in Crossbar.io with node configuration only
{
"path": "/square/<int:x>",
"method": "POST",
"call": "com.test.square",
"render": "square_result.html"
}
since the method is POST, the com.test.square
will - in addition to x
argument automatically parsed from URL - receive two more arguments:
-
data
: the binary POST data -
data_type
: content type of POST'ed data
I'll check that out, thanks!
Putting the routes in config.json worked great, thanks! And way faster than running a separate listening server (the Klein way).
I built the webservices/wap example you linked to...very useful!
If I need to respond to HTTP requests with just a string rather than rendered html, is there another dict key I should use (instead of "render")?
"sandbox": true,
"routes": [
{
"path": "/greeting/<name>",
"method": "GET",
"call": "com.test.greeting",
"render": "greeting.html" <== is there a different key for responding with a string rather than html?
}
]