I\'m using the new webapp2 (now the default webapp in 1.6), and I haven\'t been able to figure out how to make the trailing slash optional in code like this:
web
I was looking for a way to make the trailling slash on the root of a PathPrefixRoute block optional.
If you have, say:
from webapp2_extras.routes import RedirectRoute, PathPrefixRoute
from webapp2 import Route
app = webapp2.WSGIApplication([
PathPrefixRoute('admin', [
RedirectRoute('/', handler='DashboardHandler', name='admin-dashboard', strict_slash=True),
RedirectRoute('/sample-page/', handler='SamplePageHandler', name='sample-page', strict_slash=True),
]),
])
You will be able to access /admin/
, but not /admin
.
Since I couldn't find any better solution, I've added a redirect_to_name
to an extra route, like:
from webapp2_extras.routes import RedirectRoute, PathPrefixRoute
from webapp2 import Route
app = webapp2.WSGIApplication([
Route('admin', handler='DashboardHandler', name='admin-dashboard'),
PathPrefixRoute('admin', [
RedirectRoute('/', redirect_to_name='admin-dashboard'),
RedirectRoute('/sample-page/', handler='SamplePageHandler', name='sample-page', strict_slash=True),
]),
])
I'm interested in better solutions to this problem.
Should I go for Stun's solution and simply not use RedirectRoute?
I came up with a sort of hacky way. I define the following class:
class UrlConf(object):
def __init__(self, *args, **kwargs):
self.confs = []
for arg in args:
if isinstance(arg, webapp2.Route):
slash_route = webapp2.Route(arg.template + '/', arg.handler)
self.confs += [arg, slash_route]
def __iter__(self):
for route in self.confs:
yield route
Then I set up my routes like the following:
MIRROR_URLS = list(UrlConf(
Route('/path/to/stuff', handler=StuffHandler, name='stuff.page'),
Route('/path/to/more/stuff', handler= MoreStuffHandler, name='more.stuff.page')
))
If you do choose to go this route, you can obviously improve upon it to be more flexible with other types of BaseRoute objects.
webapp2.Route
template is not a regular expressions and your value is being escaped with re.escape
. You can use old style rules which provides regular expression templates:
webapp2.SimpleRoute('^/feed/?$', handler = feed)
To avoid creating duplicate URL:s to the same page, you should use a RedirectRoute with strict_slash set to True to automatically redirect /feed/ to /feed, like this:
from webapp2_extras.routes import RedirectRoute
route = RedirectRoute('/feed', handler=feed, strict_slash=True)
Read more at http://webapp2.readthedocs.io/en/latest/api/webapp2_extras/routes.html
If you don't want to use redirects (and you probably don't), you can override Route.match()
:
from webapp2 import Route, _get_route_variables
import urllib
from webob import exc
class SloppyRoute(Route):
"""
A route with optional trailing slash.
"""
def __init__(self, *args, **kwargs):
super(SloppyRoute, self).__init__(*args, **kwargs)
def match(self, request):
path = urllib.unquote(request.path)
match = self.regex.match(path)
try:
if not match and not path.endswith('/'):
match = self.regex.match(path + '/')
except:
pass
if not match or self.schemes and request.scheme not in self.schemes:
return None
if self.methods and request.method not in self.methods:
# This will be caught by the router, so routes with different
# methods can be tried.
raise exc.HTTPMethodNotAllowed()
args, kwargs = _get_route_variables(match, self.defaults.copy())
return self, args, kwargs
I am not familiar with webapp2, but if the first parameter is a regular expression, try:
/feed(/)?