How do I make a trailing slash optional with webapp2?

寵の児 提交于 2019-12-04 16:54:21

问题


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.


回答1:


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




回答2:


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 :-)




回答3:


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.
    ...



回答4:


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)



回答5:


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>




回答6:


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



回答7:


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?




回答8:


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.




回答9:


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

/feed(/)?


来源:https://stackoverflow.com/questions/8834332/how-do-i-make-a-trailing-slash-optional-with-webapp2

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!