summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJenkins <jenkins@review.openstack.org>2014-12-05 21:11:28 +0000
committerGerrit Code Review <review@openstack.org>2014-12-05 21:11:28 +0000
commitdb8314db2596ce2ab823ffdd6609992d7d3712a3 (patch)
tree808e02662934b4ab6d813e1fcb93c1d213b787f0
parent34ce0d8c8b757860c5782de18c0ee6689f842e60 (diff)
parent232a0d4ef0c1750e8b859c89c02061663532a6a0 (diff)
downloadpecan-db8314db2596ce2ab823ffdd6609992d7d3712a3.tar.gz
Merge "Add documentation for generic REST controllers."
-rw-r--r--docs/source/rest.rst84
-rw-r--r--docs/source/routing.rst116
2 files changed, 155 insertions, 45 deletions
diff --git a/docs/source/rest.rst b/docs/source/rest.rst
index 8432a86..6f6a01b 100644
--- a/docs/source/rest.rst
+++ b/docs/source/rest.rst
@@ -1,13 +1,81 @@
.. _rest:
-Writing RESTful Web Services with Pecan
-=======================================
-
-If you need to write controllers to interact with objects, using the
-:class:`~pecan.rest.RestController` may help speed things up. It follows the
-Representational State Transfer Protocol, also known as REST, by routing the
-standard HTTP verbs of ``GET``, ``POST``, ``PUT``, and ``DELETE`` to individual
-methods.
+Writing RESTful Web Services with Generic Controllers
+=====================================================
+
+Pecan simplifies RESTful web services by providing a way to overload URLs based
+on the request method. For most API's, the use of `generic controller`
+definitions give you everything you need to build out robust RESTful
+interfaces (and is the *recommended* approach to writing RESTful web services
+in pecan):
+
+::
+
+ from pecan import abort, expose
+
+ # Note: this is *not* thread-safe. In real life, use a persistent data store.
+ BOOKS = {
+ '0': 'The Last of the Mohicans',
+ '1': 'Catch-22'
+ }
+
+
+ class BookController(object):
+
+ def __init__(self, id_):
+ self.id_ = id_
+ assert self.book
+
+ @property
+ def book(self):
+ if self.id_ in BOOKS:
+ return dict(id=self.id_, name=BOOKS[self.id_])
+ abort(404)
+
+ # HTTP GET /<id>/
+ @expose(generic=True, template='json')
+ def index(self):
+ return self.book
+
+ # HTTP PUT /<id>/
+ @index.when(method='PUT', template='json')
+ def index_PUT(self, **kw):
+ BOOKS[self.id_] = kw['name']
+ return self.book
+
+ # HTTP DELETE /<id>/
+ @index.when(method='DELETE', template='json')
+ def index_DELETE(self):
+ del BOOKS[self.id_]
+ return dict()
+
+
+ class RootController(object):
+
+ @expose()
+ def _lookup(self, id_, *remainder):
+ return BookController(id_), remainder
+
+ # HTTP GET /
+ @expose(generic=True, template='json')
+ def index(self):
+ return [dict(id=k, name=v) for k, v in BOOKS.items()]
+
+ # HTTP POST /
+ @index.when(method='POST', template='json')
+ def index_POST(self, **kw):
+ id_ = len(BOOKS)
+ BOOKS[id_] = kw['name']
+ return dict(id=id_, name=kw['name'])
+
+
+Writing RESTful Web Services with RestController
+================================================
+
+.. _TurboGears2: http://turbogears.org
+
+For compatability with the TurboGears2_ library, Pecan also provides
+a class-based solution to RESTful routing, :class:`~pecan.rest.RestController`:
::
diff --git a/docs/source/routing.rst b/docs/source/routing.rst
index 68a46ba..4b80472 100644
--- a/docs/source/routing.rst
+++ b/docs/source/routing.rst
@@ -106,7 +106,7 @@ Let's look at an example using ``template`` and ``content_type``:
def hello(self):
return {'msg': 'Hello!'}
-You'll notice that we called :func:`~pecan.decoators.expose` three times, with
+You'll notice that we called :func:`~pecan.decorators.expose` three times, with
different arguments.
::
@@ -136,54 +136,44 @@ use the ``text/html`` content type by default.
* :ref:`pecan_decorators`
+Routing Based on Request Method
+-------------------------------
-Pecan's Routing Algorithm
--------------------------
-
-Sometimes, the standard object-dispatch routing isn't adequate to properly
-route a URL to a controller. Pecan provides several ways to short-circuit
-the object-dispatch system to process URLs with more control, including the
-special :func:`_lookup`, :func:`_default`, and :func:`_route` methods. Defining
-these methods on your controller objects provides additional flexibility for
-processing all or part of a URL.
-
-
-Setting a Return Status Code
-----------------------------
-
-Set a specific HTTP response code (such as ``201 Created``) by
-modifying the ``status`` attribute of the response object.
+The ``generic`` argument to :func:`~pecan.decorators.expose` provides support for overloading URLs
+based on the request method. In the following example, the same URL can be
+serviced by two different methods (one for handling HTTP ``GET``, another for
+HTTP ``POST``) using `generic controllers`:
::
- from pecan import expose, response
+ from pecan import expose
- class RootController(object):
- @expose('json')
- def hello(self):
- response.status = 201
- return {'foo': 'bar'}
+ class RootController(object):
-Use the utility function :func:`~pecan.core.abort` to raise HTTP errors.
+ # HTTP GET /
+ @expose(generic=True, template='json')
+ def index(self):
+ return dict()
-::
+ # HTTP POST /
+ @index.when(method='POST', template='json')
+ def index_POST(self, **kw):
+ uuid = create_something()
+ return dict(uuid=uuid)
- from pecan import expose, abort
- class RootController(object):
- @expose('json')
- def hello(self):
- abort(404)
+Pecan's Routing Algorithm
+-------------------------
-:func:`~pecan.core.abort` raises an instance of
-:class:`~webob.exc.WSGIHTTPException` which is used by Pecan to render
-:default response bodies for HTTP errors. This exception is stored in
-:the WSGI request environ at ``pecan.original_exception``, where it
-:can be accessed later in the request cycle (by, for example, other
-:middleware or :ref:`errors`).
+Sometimes, the standard object-dispatch routing isn't adequate to properly
+route a URL to a controller. Pecan provides several ways to short-circuit
+the object-dispatch system to process URLs with more control, including the
+special :func:`_lookup`, :func:`_default`, and :func:`_route` methods. Defining
+these methods on your controller objects provides additional flexibility for
+processing all or part of a URL.
Routing to Subcontrollers with ``_lookup``
@@ -268,7 +258,7 @@ a :func:`_route` method will enable you to have total control.
Interacting with the Request and Response Object
-------------------------------------------------
+================================================
For every HTTP request, Pecan maintains a :ref:`thread-local reference
<contextlocals>` to the request and response object, ``pecan.request`` and
@@ -295,6 +285,58 @@ directly, there may be situations where you want to access them, such as:
* Manually rendering a response body
+Specifying a Custom Response
+----------------------------
+
+Set a specific HTTP response code (such as ``203 Non-Authoritative Information``) by
+modifying the ``status`` attribute of the response object.
+
+::
+
+ from pecan import expose, response
+
+ class RootController(object):
+
+ @expose('json')
+ def hello(self):
+ response.status = 203
+ return {'foo': 'bar'}
+
+Use the utility function :func:`~pecan.core.abort` to raise HTTP errors.
+
+::
+
+ from pecan import expose, abort
+
+ class RootController(object):
+
+ @expose('json')
+ def hello(self):
+ abort(404)
+
+
+:func:`~pecan.core.abort` raises an instance of
+:class:`~webob.exc.WSGIHTTPException` which is used by Pecan to render
+default response bodies for HTTP errors. This exception is stored in
+the WSGI request environ at ``pecan.original_exception``, where it
+can be accessed later in the request cycle (by, for example, other
+middleware or :ref:`errors`).
+
+If you'd like to return an explicit response, you can do so using
+:class:`~pecan.core.Response`:
+
+::
+
+ from pecan import expose, Response
+
+ class RootController(object):
+
+ @expose()
+ def hello(self):
+ return Response('Hello, World!', 202)
+
+
+
Extending Pecan's Request and Response Object
---------------------------------------------