• Home
  • History
  • Annotate
Name Date Size #Lines LOC

..03-May-2022-

txrestapi/H12-Feb-2015-301242

txrestapi.egg-info/H03-May-2022-1110

PKG-INFOH A D12-Feb-2015270 1110

README.rstH A D12-Feb-20155.2 KiB147117

setup.cfgH A D12-Feb-201559 64

setup.pyH A D12-Feb-2015764 2723

README.rst

1============
2Introduction
3============
4
5``txrestapi`` makes it easier to create Twisted REST API services. Normally, one
6would create ``Resource`` subclasses defining each segment of a path; this is
7cubersome to implement and results in output that isn't very readable.
8``txrestapi`` provides an ``APIResource`` class allowing complex mapping of path to
9callback (a la Django) with a readable decorator.
10
11===============================
12Basic URL callback registration
13===============================
14
15First, let's create a bare API service::
16
17    >>> from txrestapi.resource import APIResource
18    >>> api = APIResource()
19
20and a web server to serve it::
21
22    >>> from twisted.web.server import Site
23    >>> from twisted.internet import reactor
24    >>> site = Site(api, timeout=None)
25
26and a function to make it easy for us to make requests (only for doctest
27purposes; normally you would of course use ``reactor.listenTCP(8080, site)``)::
28
29    >>> from twisted.web.server import Request
30    >>> class FakeChannel(object):
31    ...     transport = None
32    >>> def makeRequest(method, path):
33    ...     req = Request(FakeChannel(), None)
34    ...     req.prepath = req.postpath = None
35    ...     req.method = method; req.path = path
36    ...     resource = site.getChildWithDefault(path, req)
37    ...     return resource.render(req)
38
39We can now register callbacks for paths we care about. We can provide different
40callbacks for different methods; they must accept ``request`` as the first
41argument::
42
43    >>> def get_callback(request): return 'GET callback'
44    >>> api.register('GET', '^/path/to/method', get_callback)
45    >>> def post_callback(request): return 'POST callback'
46    >>> api.register('POST', '^/path/to/method', post_callback)
47
48Then, when we make a call, the request is routed to the proper callback::
49
50    >>> print makeRequest('GET', '/path/to/method')
51    GET callback
52    >>> print makeRequest('POST', '/path/to/method')
53    POST callback
54
55We can register multiple callbacks for different requests; the first one that
56matches wins::
57
58    >>> def default_callback(request):
59    ...     return 'Default callback'
60    >>> api.register('GET', '^/.*$', default_callback) # Matches everything
61    >>> print makeRequest('GET', '/path/to/method')
62    GET callback
63    >>> print makeRequest('GET', '/path/to/different/method')
64    Default callback
65
66Our default callback, however, will only match GET requests. For a true default
67callback, we can either register callbacks for each method individually, or we
68can use ALL::
69
70    >>> api.register('ALL', '^/.*$', default_callback)
71    >>> print makeRequest('PUT', '/path/to/method')
72    Default callback
73    >>> print makeRequest('DELETE', '/path/to/method')
74    Default callback
75    >>> print makeRequest('GET', '/path/to/method')
76    GET callback
77
78Let's unregister all references to the default callback so it doesn't interfere
79with later tests (default callbacks should, of course, always be registered
80last, so they don't get called before other callbacks)::
81
82    >>> api.unregister(callback=default_callback)
83
84=============
85URL Arguments
86=============
87
88Since callbacks accept ``request``, they have access to POST data or query
89arguments, but we can also pull arguments out of the URL by using named groups
90in the regular expression (similar to Django). These will be passed into the
91callback as keyword arguments::
92
93    >>> def get_info(request, id):
94    ...     return 'Information for id %s' % id
95    >>> api.register('GET', '/(?P<id>[^/]+)/info$', get_info)
96    >>> print makeRequest('GET', '/someid/info')
97    Information for id someid
98
99Bear in mind all arguments will come in as strings, so code should be
100accordingly defensive.
101
102================
103Decorator syntax
104================
105
106Registration via the ``register()`` method is somewhat awkward, so decorators
107are provided making it much more straightforward. ::
108
109    >>> from txrestapi.methods import GET, POST, PUT, ALL
110    >>> class MyResource(APIResource):
111    ...
112    ...     @GET('^/(?P<id>[^/]+)/info')
113    ...     def get_info(self, request, id):
114    ...         return 'Info for id %s' % id
115    ...
116    ...     @PUT('^/(?P<id>[^/]+)/update')
117    ...     @POST('^/(?P<id>[^/]+)/update')
118    ...     def set_info(self, request, id):
119    ...         return "Setting info for id %s" % id
120    ...
121    ...     @ALL('^/')
122    ...     def default_view(self, request):
123    ...         return "I match any URL"
124
125Again, registrations occur top to bottom, so methods should be written from
126most specific to least. Also notice that one can use the decorator syntax as
127one would expect to register a method as the target for two URLs ::
128
129    >>> site = Site(MyResource(), timeout=None)
130    >>> print makeRequest('GET', '/anid/info')
131    Info for id anid
132    >>> print makeRequest('PUT', '/anid/update')
133    Setting info for id anid
134    >>> print makeRequest('POST', '/anid/update')
135    Setting info for id anid
136    >>> print makeRequest('DELETE', '/anid/delete')
137    I match any URL
138
139======================
140Callback return values
141======================
142
143You can return Resource objects from a callback if you wish, allowing you to
144have APIs that send you to other kinds of resources, or even other APIs.
145Normally, however, you'll most likely want to return strings, which will be
146wrapped in a Resource object for convenience.
147