2

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

现在我必须更改服务器应该响应的路径。
显而易见的方法是更改@route​​语句并将新路径添加到每条路线。

但是我得到了旧路径应该继续工作一段时间的限制。由于服务器也应该对旧请求做出反应,因此我必须复制每个路由语句才能使用旧路径和新路径一次。

因此,直截了当,这将是一个变化:

@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)
4

2 回答 2

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)

或者,您可以使用显式路由,但这会带走一些瓶子的魅力:

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)
于 2012-10-26T10:31:51.967 回答
0

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

变化:

(基于今天创建的分叉,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

我已经发送了一个拉取请求,但这是以防万一它不会被拉进来。;)

于 2012-10-29T07:05:01.387 回答