Since version 0.9.8, Toscawidgets allows the registration of serverside callbacks. These are a powerful feature, intended to aid the development of self-contained widgets which need support-methods on the server-side, such as AJAX-driven search pages and the like.
As usual, an introductionary example explains things better that thousand words:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | import webob
from tw.api import (
Widget,
ServerSideCallbackMixin,
serverside_callback,
make_middleware,
always_allow,
)
from webtest import TestApp
class TestWidget(Widget, ServerSideCallbackMixin):
@serverside_callback
def test_callback(self, request):
response = webob.Response()
response.content_type= "text/plain"
response.body = "Look mam, no webframework needed!"
return response
def callback_authorization(self, callback, request):
return always_allow(callback, request)
serverside_widget = TestWidget("serverside_widget")
def app(environ, start_response):
start_response("200 OK",
[("Content-type", "text/plain")])
# this method can only be called inside the webapp,
# because only then the resource middleware and host-framework that deal with
# the callbacks are active.
callback_url = serverside_widget.url_for_callback(serverside_widget.test_callback)
return [callback_url]
app = make_middleware(app, stack_registry=True, config={})
app = TestApp(app)
callback_url = app.get("/").body
res = app.get(callback_url)
assert res.body == "Look mam, no webframework needed!"
|
Most of the code is just to create a testing-harness - you don’t need to worry about it.
There are three new concepts though, and these need to be addressed:
- declaring callbacks with serverside_callback.
- getting the url a callback is registered under.
- protecting the callbacks with callback_authorization.
Also, please note that the widget needs to be subclassed from ServerSideCallbackMixin.
A callback is a normal instance-method which is decorated with the decorator serverside_callback. It will always only have one argument, a webob.Request-instance which can be used to get access to all parameters and other attributes of the request.
It must return a webob.Response-instance to render it’s output.
Of course the callback can invoke all methods a widget has by itself.
The above toy-example shows how to reach a widget’s callback. To do so, one needs to know where the callback is mapped to. In a real-world example, this would then be passed to javascript-code, or rendered into links in the widget-template.
The url in our example is
/toscawidgets/resources/__callback__/serverside_widget/test_callback
Pretty straightforward - so why not hardcode them? Because that would fail if the widget is registered in an application that is not mounted as root! So, whenever you need a callback-url, use :method:`tw.core.server.ServerSideCallbackMixin.url_for_callback`.
An important topic is how to prevent unauthorized access to serverside-callbacks.
To govern this, there are two hooks:
- a global one that is passed on middleware-creation. It defaults to denying all access.
- a per-widget-one that is a parameter to the widget called callback_authorization.
In both cases the authorization handler is a function that has the following signature:
(instancemethodf, webob.Request) -> webob.Response
So it takes the callback in question and the current request, and must return a response which is used to convey the results of checking the authorization via it’s status:
- 200 if it’s ok for the current request to call the callback.
- everything else is reported back as result of the request.
As you can see in the example, the authorization is conceived as subclassed method. This works as well as passing a parameter to the constructor, it’s up to you what you prefer.
And there are two built-in checkers, always_allow and always_deny that serve as model how to implement authorization checkers.