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