Rest support in GrokΒΆ
Author: | faassen |
---|
REST is a way to build web services, i.e. a web application where the user is another computer, not a human being. REST takes the approach to make the web service look very similar to a normal web application, using well-known semantics of HTTP.
Grok has support that helps you implement REST-based protocols. That is, Grok doesn’t actually implement any RESTful protocols itself, but it allows you to easily add them in your own application.
To implement a REST protocol, you do something very similar to implementing a skin. This way, REST requests are separated from other requests on objects. This means you can have a normal web UI with views on a set of objects in parallel to the implementation of one or more REST protocols.
Let’s see how you define a REST protocol. Similar to the way skins work, first you need to define a layer. In the case of REST, your layer must derive from grok.IRESTLayer.
class AtomPubLayer(grok.IRESTLayer):
grok.restskin('atompub')
The restskin directive is used to name our skin.
REST handlers are very much like views like JSON or XMLRPC views. In the case of REST, you implement the HTTP methods on the view. It needs to be in the right REST layer.
class MyREST(grok.REST):
grok.context(MyContainer)
grok.layer(AtomPubLayer)
def GET(self):
return "GET request, retrieve container listing"
def POST(self):
return "POST request, add something to container"
def PUT(self):
return "PUT request, replace complete contents"
def DELETE(self):
return "DELETE request, delete this object entirely"
When handling a REST request, you often want to get to the raw body of
the request. You can access a special body
attribute that contains
the body as a string.
class MyREST2(grok.REST):
grok.context(SomeObject)
grok.layer(AtomPubLayer)
def POST(self):
return "This is the body: " + self.body
This body should be parsed accordingly by your REST protocol implementation - it could for instance be some form of XML or JSON.
Now you can access the object with the REST skin, through requests like this (issuing GET, POST, PUT or DELETE):
http://localhost:8080/++rest++atompub/mycontainer
As you can see, you need to use the ++rest++<restskin> pattern somewhere in the URL in order to access the REST view for your objects. If you don’t like the ++rest++ bit you can also apply the skin during traversal to the object:
@grok.subscribe(MyApp, grok.IBeforeTraverseEvent)
def restSkin(obj, event):
if not AtomPubLayer.providedBy(event.request):
grok.util.applySkin(event.request,
AtomPubLayer,
grok.IRESTSkinType)
Note that we apply the skin during traversal of the root (application)
object such that the skin is applied early. It is important to realise
that all requests made to views upon MyApp and its subobjects
will have the skin applied to it, since the traverser will always traverse
MyApp
en route. If this is a problem, you could subscribe to
traversal events from more specific objects such as containers and
models. However, you cannot subscribe to traversal events on your views.
This is because the traverser sees views as the end of traversal, not
part of it, and thus the grok.IBeforeTraverseEvent
event will not
be issued.
Alternatively, if you’re using Apache, you can use a few rewrite rules (just like with skins).
Using skins like this means you could have a single object implement several different REST skins. Since layers are used, you could also compose a single REST protocol out of multiple protocols should you so desire.
If you don’t explicitly set a layer using grok.layer
for a REST
subclass, it’ll use the grok.IRESTLayer by default. This layer is the
base of all REST layers.
Similar again to XMLRPC or JSON views, security works with all this: you can use @grok.require() on the REST methods to shield them from public use.