Sudhir Jonathan
Sudhir Jonathan

Reputation: 17516

How do I make a trailing slash optional with webapp2?

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

Answers (9)

Aneon
Aneon

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

Ivan Chaer
Ivan Chaer

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

Roman Levin
Roman Levin

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

anguyenhuy
anguyenhuy

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

HorseloverFat
HorseloverFat

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

erichiggins
erichiggins

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

stun
stun

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.

  1. /
  2. /feed
  3. /feed/
  4. /feed/create
  5. /feed/create/
  6. /feed/edit/{entity_id}
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

Ski
Ski

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

Krystian Cybulski
Krystian Cybulski

Reputation: 11108

I am not familiar with webapp2, but if the first parameter is a regular expression, try:

/feed(/)?

Upvotes: -4

Related Questions