diff --git a/NOTES.md b/NOTES.md index f526f89..24677e7 100644 --- a/NOTES.md +++ b/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. diff --git a/falcon/api_helpers.py b/falcon/api_helpers.py index cee3022..1984e1e 100644 --- a/falcon/api_helpers.py +++ b/falcon/api_helpers.py @@ -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) diff --git a/tests/test_uri_templates.py b/tests/test_uri_templates.py index 8c37e2c..2b4aad9 100644 --- a/tests/test_uri_templates.py +++ b/tests/test_uri_templates.py @@ -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()