Python:bottle.py的类似mod_rewrite的函数

时间:2012-10-26 09:49:45

标签: python bottle

我有一个现有的Python应用程序,它通过Bottle.py服务为一堆文件提供服务。

现在我必须改变服务器应该做出反应的路径 显而易见的方法是更改​​@route语句并在每条路径前添加新路径。

但是我得到了旧路径应该继续工作一段时间的约束。 由于服务器也应对旧请求做出反应,因此我必须复制每个route-statement,以便使用旧路径和新路径进行一次。

所以,直截了当这将是一个变化:

@route('/somefile')
def doSomeStuff():

为:

@route('/somefile')
@route('/newpath/somefile')
def doSomeStuff():

但是因为有很多路由而且我不想搞乱所有代码,所以我正在寻找一种优雅的方式来处理路由发生之前 的请求。

有没有办法挂钩路由流程?


我目前的做法是向浏览器提供301,但我不喜欢这种解决方案,因为它增加了请求数量并更改了用户的URL。

#Serve a 301 (hope the browsers remember it for a session at least)
@route('/newpath<path:path>')
def redirectNewToOld(path):
    if len(path) == 0:                         #Catch the lazy typers
        path = '/'
    redirect(path, code=301)

2 个答案:

答案 0 :(得分:3)

有两件事情可以想到,但是没有一件事能像在webapp前面使用nginx一样好。第一个是使用中间件类重写PATH_INFO - 有WSGIRewrite,但总体思路是:

from bottle import route, run, app

@route('/oldpath/somefile')
def index(): return 'Hello'

class RewriteMiddleware:
    def __init__(self, app):
        self.app = app

    def __call__(self, env, res):
        path = env['PATH_INFO']
        if path.startswith('/newpath'):
            env['PATH_INFO'] = path.replace('/newpath', '/oldpath', 1)

        return self.app(env, res)

app = RewriteMiddleware(app())
run(app)

或者,您可以使用explicit routing,但这会消除瓶子的一些魅力:

from bottle import run, route

def index(): return 'Hello'

routes = (
    ('/oldpath/hello', 'GET', index),
    ('/oldpath/world', ['GET', 'POST'], index),
    )

def setup_routing():
    for path, method, handler in routes:
        if path.startswith('/oldpath'):
            newpath = path.replace('/oldpath', '/newpath', 1)
            route(newpath, method, handler)

        route(path, method, handler)

setup_routing()
run()

在重新阅读你的问题后,似乎第二个选项是不行,因为它涉及修改大量代码。但是,您可以迭代所有现有路线并添加新路线:

for route in bottle.default_app[0].routes:
    if route.rule.startswith(...):
        bottle.route(..., route.call)

答案 1 :(得分:0)

好吧,我为瓶子写了自己的mod_rewrite 代码也可以在 GitHub

上找到

的变化:

(根据今天创建的fork,2012年10月29日)

class Bottle(object): @Lines 754ff,另外一个:

def _handle(self, environ):
    try:
        environ['bottle.app'] = self
+       url_rewrite.apply(environ)    #Added mod_rewrite
        request.bind(environ)
        response.bind()
        route, args = self.router.match(environ)
        environ['route.handle'] = route
        environ['bottle.route'] = route
        environ['route.url_args'] = args
        return route.call(**args)

在底部添加

###############################################################################
# Nippey #### 29.10.2012 #### mod_rewrite #####################################
###############################################################################
# This modification to bottly.py allows the application of rewrite rules to 
# requests _before_ they are processed by the routing system

class UrlRewriteException(BottleException):
    """ This is a base class for all rewrite related exceptions """

class UrlRewrite():
    """ This class processes every URL before is is passed to the routing system 
    In case one of the included rewrite rules matches the URL, the URL will be modified.
    New rules can be added via the .addRule(str, str, bool) method.
    For each requested URL, the method apply will be called with the environ variable as parameter.
    """

    def __init__(self):
        """ Initiates the rules variable """
        print("UrlRewrite init.")
        self.rules = []

    def addRule(self, match, replace, final=False):
        """ Add a new rule.
        match:   Regular expression to search for. Can be a string or a compiled regular expression
        replace: Replacement string. May use backreferences.
        final:   If a rule with <final=True> matches the URL, the evaluation will be stopped afterwards
        """
        print("UrlRewrite addRule.")
        if type(match) is not str and type(replace) is not str:
            raise UrlRewriteException
        pattern = re.compile(match)
        self.rules.append({'pattern':pattern, 'repl':replace, 'final':bool(final)})

    def apply(self, environ):
        """ Test a URL for a match of one of the saved rules and rewrite it if required
        environ: Environmental variable created by bottle on each request. Contains the PATH_INFO 
                 information, modification will happen with the reference to this variable.
        returns: Returns true if a rewrite has been executed. Not used by the main program (yet)
                 Original path will still be available as PATH_INFO_ORIGINAL in the environ variable
        """
        print("UrlRewrite apply.")
        rewritten = False
        url = environ['PATH_INFO']
        for rule in self.rules:             #Try to alppy each of the rules
            (url, noSubs) = rule['pattern'].subn(rule['repl'], url)
            if noSubs > 0:
                rewritten = True
                if rule['final']:
                    break
        if rewritten:
            environ['PATH_INFO_ORIGINAL'] = environ['PATH_INFO']
            environ['PATH_INFO'] = url
            return True
        return False

    """ EXAMPLES:

    #Backreferences may be used by the replacement string
    from bottle import url_rewrite
    url_rewrite.addRule("^/he(l*)o", r"/by\1e", False)
    #Input:  "/hello/test_hello" 
    #Output: "/bye/test_hello"

    #All matching occurences will be replaced
    from bottle import url_rewrite
    url_rewrite.addRule("hello", "bye", False)
    #Input:  "/hello/test_hello" 
    #Output: "/bye/test_bye"

    #Rules will be applied in successive order
    from bottle import url_rewrite
    url_rewrite.addRule("hello", "hi", False)
    url_rewrite.addRule("hi", "bye", False)
    #Input:  "/hello/test_hello" 
    #Output: "/bye/test_bye"

    #Rules won't be re-applied from the start if one rule matches
    from bottle import url_rewrite
    url_rewrite.addRule("hi", "bye", False)
    url_rewrite.addRule("hello", "hi", False)
    #Input:  "/hello/test_hello" 
    #Output: "/hi/test_hi"

    #After applying a rule with <final> set to True, the evaluation will be finished
    from bottle import url_rewrite
    url_rewrite.addRule("hello", "hi", True)
    url_rewrite.addRule("hi", "bye", False)
    #Input:  "/hello/test_hello" 
    #Output: "/hi/test_hi"
    """
# END class UrlRewrite

url_rewrite = UrlRewrite()
# END ## mod_rewrite ## Nippey

我已经发送了一个拉取请求,但是这是为了防止它被拉入。;)