Added special handling for mapped paths with trailing slashes
parent
b11438b69c
commit
92ba7c9387
|
@ -11,12 +11,6 @@
|
||||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
from milla.dispatch import UnresolvedPath
|
|
||||||
import functools
|
|
||||||
import re
|
|
||||||
import sys
|
|
||||||
import urllib
|
|
||||||
import urlparse
|
|
||||||
'''URL router
|
'''URL router
|
||||||
|
|
||||||
:Created: Mar 13, 2011
|
:Created: Mar 13, 2011
|
||||||
|
@ -25,6 +19,13 @@ import urlparse
|
||||||
:Updater: $Author$
|
:Updater: $Author$
|
||||||
'''
|
'''
|
||||||
|
|
||||||
|
from milla.dispatch import UnresolvedPath
|
||||||
|
import functools
|
||||||
|
import milla
|
||||||
|
import re
|
||||||
|
import sys
|
||||||
|
import urllib
|
||||||
|
import urlparse
|
||||||
|
|
||||||
class Router(object):
|
class Router(object):
|
||||||
'''A dispatcher that maps arbitrary paths to controller callables
|
'''A dispatcher that maps arbitrary paths to controller callables
|
||||||
|
@ -34,14 +35,43 @@ class Router(object):
|
||||||
router = Router()
|
router = Router()
|
||||||
router.add_route('/foo/{bar}/{baz:\d+}', some_func)
|
router.add_route('/foo/{bar}/{baz:\d+}', some_func)
|
||||||
app = milla.Application(dispatcher=router)
|
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
|
#: Compiled regular expression for variable segments
|
||||||
template_re = re.compile(r'\{(\w+)(?::([^}]+))?\}')
|
template_re = re.compile(r'\{(\w+)(?::([^}]+))?\}')
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self, trailing_slash=REDIRECT):
|
||||||
self.routes = []
|
self.routes = []
|
||||||
self._cache = {}
|
self._cache = {}
|
||||||
|
self.trailing_slash=trailing_slash
|
||||||
|
|
||||||
def resolve(self, path_info):
|
def resolve(self, path_info):
|
||||||
'''Find a controller for a given path
|
'''Find a controller for a given path
|
||||||
|
@ -56,9 +86,8 @@ class Router(object):
|
||||||
matches the given path. Variable segments are added as keywords
|
matches the given path. Variable segments are added as keywords
|
||||||
to the controller function.
|
to the controller function.
|
||||||
'''
|
'''
|
||||||
try:
|
|
||||||
return self._cache[path_info]
|
def lookup(path_info):
|
||||||
except KeyError:
|
|
||||||
for regex, controller, vars in self.routes:
|
for regex, controller, vars in self.routes:
|
||||||
match = regex.match(path_info)
|
match = regex.match(path_info)
|
||||||
if match:
|
if match:
|
||||||
|
@ -71,6 +100,27 @@ class Router(object):
|
||||||
func.__doc__ = controller.__doc__
|
func.__doc__ = controller.__doc__
|
||||||
self._cache[path_info] = func
|
self._cache[path_info] = func
|
||||||
return 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
|
raise UnresolvedPath
|
||||||
|
|
||||||
def _compile_template(self, template):
|
def _compile_template(self, template):
|
||||||
|
|
Loading…
Reference in New Issue