feat(api): set_default_route as a catch-all route

A default route which is triggered when no route is found for a
request.  The change simplifies implementing a proxy-like app.
This commit is contained in:
Zhihao Yuan
2013-08-27 16:17:34 -04:00
committed by Zhihao Yuan
parent 47ce800a31
commit f30deb8fea
2 changed files with 87 additions and 3 deletions

View File

@@ -37,7 +37,8 @@ class API(object):
"""
__slots__ = ('_after', '_before', '_media_type', '_routes')
__slots__ = ('_after', '_before', '_media_type', '_routes',
'_default_route')
def __init__(self, media_type=DEFAULT_MEDIA_TYPE, before=None, after=None):
"""Initialize a new Falcon API instances
@@ -57,6 +58,7 @@ class API(object):
"""
self._routes = []
self._default_route = None
self._media_type = media_type
self._before = helpers.prepare_global_hooks(before)
@@ -212,6 +214,21 @@ class API(object):
# adds (will cause the last one to win).
self._routes.insert(0, (path_template, method_map, na_responder))
def set_default_route(self, default_resource):
"""Route all the unrouted requests to a default resource
Args:
default_resource: Object which works like an HTTP/REST resource.
Falcon will pass "GET" requests to on_get, "PUT" requests to
on_put, etc. If you want to exclude some HTTP method from the
default routing, just simply don't define the corresponding
request handlers.
"""
self._default_route = helpers.create_http_method_map(
default_resource, set(), self._before, self._after)
#----------------------------------------------------------------------------
# Helpers
#----------------------------------------------------------------------------
@@ -242,8 +259,18 @@ class API(object):
break
else:
responder = falcon.responders.path_not_found
params = {}
na_responder = falcon.responders.create_method_not_allowed([])
if self._default_route is not None:
method_map, na_responder = self._default_route
try:
responder = method_map[method]
except KeyError:
responder = falcon.responders.bad_request
else:
responder = falcon.responders.path_not_found
na_responder = falcon.responders.create_method_not_allowed([])
return (responder, params, na_responder)

View File

@@ -0,0 +1,57 @@
from testtools.matchers import Contains
import falcon
import falcon.testing as testing
class HumanResource(object):
def on_delete(self, req, resp, name):
resp.status = falcon.HTTP_204
def on_get(self, req, resp, name):
resp.status = falcon.HTTP_402
class UndeadResource(object):
def on_get(self, req, resp):
resp.status = falcon.HTTP_200
class TestDefaultRouting(testing.TestBase):
def before(self):
self.default_resource = UndeadResource()
self.resource = HumanResource()
def test_default_only(self):
self.api.set_default_route(self.default_resource)
self.simulate_request('/')
self.assertEquals(self.srmock.status, falcon.HTTP_200)
self.simulate_request('/any')
self.assertEquals(self.srmock.status, falcon.HTTP_200)
def test_routing_prioritise(self):
self.api.set_default_route(self.default_resource)
self.api.add_route('/people/{name}', self.resource)
self.simulate_request('/people/asuka')
self.assertEquals(self.srmock.status, falcon.HTTP_402)
self.simulate_request('/person/asuka')
self.assertEquals(self.srmock.status, falcon.HTTP_200)
self.simulate_request('/people/asuka', method='DELETE')
self.assertEquals(self.srmock.status, falcon.HTTP_204)
self.simulate_request('/person/asuka', method='DELETE')
self.assertEquals(self.srmock.status, falcon.HTTP_405)
headers = self.srmock.headers
allow_header = ('Allow', 'GET, OPTIONS')
self.assertThat(headers, Contains(allow_header))
self.simulate_request('/person/asuka', method=self.getUniqueString())
self.assertEquals(self.srmock.status, falcon.HTTP_400)