Added special handling for mapped paths with trailing slashes

master
Dustin C. Hatch 2011-04-10 19:19:57 -05:00
parent b11438b69c
commit 92ba7c9387
1 changed files with 60 additions and 10 deletions

View File

@ -11,12 +11,6 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from milla.dispatch import UnresolvedPath
import functools
import re
import sys
import urllib
import urlparse
'''URL router
:Created: Mar 13, 2011
@ -25,6 +19,13 @@ import urlparse
:Updater: $Author$
'''
from milla.dispatch import UnresolvedPath
import functools
import milla
import re
import sys
import urllib
import urlparse
class Router(object):
'''A dispatcher that maps arbitrary paths to controller callables
@ -34,14 +35,43 @@ class Router(object):
router = Router()
router.add_route('/foo/{bar}/{baz:\d+}', some_func)
app = milla.Application(dispatcher=router)
In many cases, paths with trailing slashes need special handling.
The ``Router`` has two ways of dealing with requests that should
have a trailing slash but do not. The default is to send the client
an HTTP 301 Moved Permanently response, and the other is to
simply treat the request as if it had the necessary trailing slash.
A third option is to disable special handling entirely and simply
return HTTP 404 Not Found for requests with missing trailing
slashes. To change the behavior, pass a different value to the
constructor's ``trailing_slash`` keyword.
Redirect the client to the proper path (the default)::
router = Router(trailing_slash=Router.REDIRECT)
router.add_route('/my_collection/', some_func)
Pretend the request had a trailing slash, even if it didn't::
router = Router(trailing_slash=Router.SILENT)
router.add_route('/my_collection/', some_func)
Do nothing, let the client get a 404 error::
router = Router(trailing_slash=None)
router.add_route('/my_collection/', some_func)
'''
class REDIRECT(object): pass
class SILENT(object): pass
#: Compiled regular expression for variable segments
template_re = re.compile(r'\{(\w+)(?::([^}]+))?\}')
def __init__(self):
def __init__(self, trailing_slash=REDIRECT):
self.routes = []
self._cache = {}
self.trailing_slash=trailing_slash
def resolve(self, path_info):
'''Find a controller for a given path
@ -56,9 +86,8 @@ class Router(object):
matches the given path. Variable segments are added as keywords
to the controller function.
'''
try:
return self._cache[path_info]
except KeyError:
def lookup(path_info):
for regex, controller, vars in self.routes:
match = regex.match(path_info)
if match:
@ -71,6 +100,27 @@ class Router(object):
func.__doc__ = controller.__doc__
self._cache[path_info] = func
return func
try:
return self._cache[path_info]
except KeyError:
func = lookup(path_info)
if func:
return func
elif self.trailing_slash and not path_info.endswith('/'):
# Try to resolve the path with a trailing slash
new_path_info = path_info + '/'
func = lookup(new_path_info)
if func and self.trailing_slash is Router.REDIRECT:
# Return a dummy function that just raises
# HTTPMovedPermanently to redirect the client to
# the canonical URL
def redir(*args, **kwargs):
raise milla.HTTPMovedPermanently(location=new_path_info)
return redir
elif func and self.trailing_slash is Router.SILENT:
# Return the function found at the alternate path
return func
raise UnresolvedPath
def _compile_template(self, template):