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.