feat(API): Routes now treat paths with a trailing slash as identical.
Implements #48
This commit is contained in:
3
NOTES.md
3
NOTES.md
@@ -22,7 +22,8 @@ This requires some discipline on the part of the developer.
|
||||
* URI template and query string field names must include only ASCII a-z, A-Z, and the underscore '_' character. Try it; you'll like it. This simplifies parsing and helps speed things up a bit.
|
||||
* Query params must have a value. In other words, 'foo' or 'foo=' will result in the parameter being ignored.
|
||||
* If the WSGI server passes an empty path, Falcon will force it to '/', so you don't have to test for the empty string in your app.
|
||||
* If you are hosting multiple apps with a single WSGI server, the SCRIPT_NAME variable can read from req.app
|
||||
* The routes '/foo/bar' and '/foo/bar/' are identical as far as Falcon is concerned. Requests coming in for either will be sent to the same resource.
|
||||
* If you are hosting multiple apps with a single WSGI server, the SCRIPT_NAME variable can be read from req.app
|
||||
* If you have several headers to set, consider using set_headers to avoid function call overhead
|
||||
* Don't set content-length. It will only be overridden.
|
||||
* The order in which header fields are sent in the response is undefined. Headers are not grouped according to the recommendation in [RFC 2616](http://tools.ietf.org/html/rfc2616#section-4.2) in order to generate responses as quickly as possible.
|
||||
|
||||
@@ -143,11 +143,13 @@ def compile_uri_template(template=None):
|
||||
|
||||
if not template:
|
||||
template = '/'
|
||||
elif template != '/' and template.endswith('/'):
|
||||
template = template[:-1]
|
||||
|
||||
# Convert Level 1 var patterns to equivalent named regex groups
|
||||
escaped = re.sub(r'([\.\(\)\[\]\?\*\+\^\|])', r'\.', template)
|
||||
pattern = re.sub(r'{([a-zA-Z][a-zA-Z_]*)}', r'(?P<\1>[^/]+)', escaped)
|
||||
pattern = r'\A' + pattern + r'\Z'
|
||||
pattern = r'\A' + pattern + r'/?\Z'
|
||||
|
||||
return re.compile(pattern, re.IGNORECASE)
|
||||
|
||||
|
||||
@@ -68,17 +68,29 @@ class TestUriTemplates(testing.TestSuite):
|
||||
self.assertEquals(req.get_param('id'), None)
|
||||
|
||||
def test_single_trailing_slash(self):
|
||||
resource = IDResource()
|
||||
self.api.add_route('/widgets/{id}/', resource)
|
||||
resource1 = IDResource()
|
||||
self.api.add_route('/1/{id}/', resource1)
|
||||
|
||||
self.simulate_request('/widgets/123')
|
||||
self.assertFalse(resource.called)
|
||||
self.simulate_request('/1/123')
|
||||
self.assertTrue(resource1.called)
|
||||
self.assertEquals(resource1.id, '123')
|
||||
self.assertEquals(resource1.name, None)
|
||||
|
||||
self.simulate_request('/widgets/123/')
|
||||
self.assertTrue(resource.called)
|
||||
resource2 = IDResource()
|
||||
self.api.add_route('/2/{id}/', resource2)
|
||||
|
||||
self.assertEquals(resource.id, '123')
|
||||
self.assertEquals(resource.name, None)
|
||||
self.simulate_request('/2/123/')
|
||||
self.assertTrue(resource2.called)
|
||||
self.assertEquals(resource2.id, '123')
|
||||
self.assertEquals(resource2.name, None)
|
||||
|
||||
resource3 = IDResource()
|
||||
self.api.add_route('/3/{id}', resource3)
|
||||
|
||||
self.simulate_request('/3/123/')
|
||||
self.assertTrue(resource3.called)
|
||||
self.assertEquals(resource3.id, '123')
|
||||
self.assertEquals(resource3.name, None)
|
||||
|
||||
def test_multiple(self):
|
||||
resource = IDResource()
|
||||
|
||||
Reference in New Issue
Block a user