Reputation: 17516
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:
webapp.Route('/feed', handler = feed)
I've tried /feed/?
, /feed/*
, /feed\/*
and /feed\/?
, all to no avail.
Upvotes: 19
Views: 5624
Reputation: 826
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
Upvotes: 13
Reputation: 7100
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?
Upvotes: 1
Reputation: 461
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
Upvotes: 1
Reputation: 1
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.
Upvotes: 0
Reputation: 3326
This works for me and is very simple. It uses the template format for URI routing in the webapp2 Route class. Trailing slash in this example is optional with no redirection:
webapp2.Route('/your_url<:/?>', PageHandler)
Everything after the colon between the chevrons is considered to be a regex: <:regex>
Upvotes: 1
Reputation: 596
Here's how I handle these routes.
from webapp2 import Route
from webapp2_extras.routes import PathPrefixRoute
import handlers
urls = [
Route('/foo<:/?>', handlers.Foo),
Route('/bars', handlers.BarList),
PathPrefixRoute('/bar', [
Route('/', handlers.BarList),
Route('/<bar_id:\w+><:/?>', handlers.Bar),
]),
]
...
It's important to note that your handlers will need to define *args
and **kwargs
to deal with the potential trailing slash, which gets sent to them as an argument using this method.
class Bar(webapp2.RequestHandler):
def get(bar_id, *args, **kwargs):
# Lookup and render this Bar using bar_id.
...
Upvotes: 4
Reputation: 1744
I don't like the RedirectRoute
class because it causes an unnecessary HTTP Redirect.
Based on the documentation for webapp2 Route class, here is a more detailed answer in this webapp2.Route with optional leading part thread.
Short Answer
My route patterns works for the following URLs.
SITE_URLS = [
webapp2.Route(r'/', handler=HomePageHandler, name='route-home'),
webapp2.Route(r'/feed/<:(create/?)|edit/><entity_id:(\d*)>',
handler=MyFeedHandler,
name='route-entity-create-or-edit'),
webapp2.SimpleRoute(r'/feed/?',
handler=MyFeedListHandler,
name='route-entity-list'),
]
Hope it helps :-)
Upvotes: 7
Reputation: 14487
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)
Upvotes: 2
Reputation: 11108
I am not familiar with webapp2, but if the first parameter is a regular expression, try:
/feed(/)?
Upvotes: -4