Skip to main content
 首页 » 编程设计

python之使用 Google 应用引擎的 webapp2 装饰器路由

2025年05月04日24langtianya

我想通过装饰 RequestHandler 对象来定义路由,而不是在创建应用程序时向其提供列表。

我的 WSGIApplication 定义如下:

class WSGIApplication(webapp2.WSGIApplication): 
    def __init__(self, *args, **kwargs): 
        super(WSGIApplication, self).__init__(*args, **kwargs) 
        self.router.set_dispatcher(self.__class__.custom_dispatcher) 
 
    @staticmethod 
    def custom_dispatcher(router, request, response): 
        rv = router.default_dispatcher(request, response) 
        if isinstance(rv, basestring): 
            rv = webapp2.Response(rv) 
        elif isinstance(rv, tuple): 
            rv = webapp2.Response(*rv) 
 
        return rv 
 
    def route(self, url, name): 
        def outer_wrapped(cls): 
            logging.info("Adding route %s to class %s with name %s" 
                         % (url, cls, name)) 
            self.router.add(RedirectRoute(url, cls, 
                                          name=name, 
                                          strict_slash=True)) 
            return cls 
        return outer_wrapped 

在package.utils

我在 package.application

中定义了我的应用程序

app.yaml 有

application: theapplication 
version: 1 
runtime: python27 
api_version: 1 
threadsafe: yes 
 
handlers: 
- url: .* 
  script: package.main.application 
  secure: always 

package.main如下:

from package.application import application 
from package.pages import pages 

package.application 如下( key 已散列):

from package import utils 
 
conf = {} 
conf['webapp2_extras.sessions'] = {'secret_key': 
    """##############################################"""} 
 
application = utils.WSGIApplication([], config=conf) 

这里有一些来自 package.pages 的处理程序:

from package.application import application as app 
 
 
@app.route('/conversations', 'view_messages') 
class Messages(BaseHandler): 
    @users.require_login 
    def get(self): 
        conversations = self.current_user.get_user_conversations() 
        return self.render('conversations.slim', conversations=conversations) 
 
 
@app.route('/', 'index') 
class Index(BaseHandler): 
    def get(self): 
        return self.render('index.slim') 

索引页面工作正常,其他一切都是 404,日志只是说:

INFO     2012-07-23 14:23:46,258 __init__.py:26] /notifications 
INFO     2012-07-23 14:23:46,272 dev_appserver.py:2952] "GET /notifications HTTP/1.1" 404 - 

我认为这是由 Appengine 的导入缓存以某种方式导致的,阻止了装饰器的运行并将它们自己添加到应用程序中。

当然,我陷入了一个陷阱,即假设我对导致问题的原因有任何线索。在尝试每次调度之前,我添加了进一步的日志记录语句来输出路由列表,我发现所有路由都在路由器中,正如人们所期望的那样:

    ... 
    def custom_dispatcher(router, request, response): 
        logging.info(router.match_routes) 
        logging.info(router.build_routes) 
        rv = router.default_dispatcher(request, response) 
    ... 

在将我的浏览器指向/conversations 时使用以下日志输出:

INFO     2012-08-04 13:16:40,513 utils.py:13] [<Route('/friendship', <class 'package.pages.pages.Friendship'>, name='friendship', defaults={}, build_only=False)>, <Route('/friendship/', <class 'webapp2.RedirectHandler'>, name=None, defaults={'_uri': <bound method RedirectRoute._redirect of <Route('/friendship', <class 'package.pages.pages.Friendship'>, name='friendship', defaults={}, build_only=False)>>, '_name': 'friendship'}, build_only=False)>, <Route('/signup', <class 'package.pages.pages.Signup'>, name='signup', defaults={}, build_only=False)>, <Route('/signup/', <class 'webapp2.RedirectHandler'>, name=None, defaults={'_uri': <bound method RedirectRoute._redirect of <Route('/signup', <class 'package.pages.pages.Signup'>, name='signup', defaults={}, build_only=False)>>, '_name': 'signup'}, build_only=False)>, <Route('/preferences', <class 'package.pages.pages.UserPage'>, name='user_prefs', defaults={}, build_only=False)>, <Route('/preferences/', <class 'webapp2.RedirectHandler'>, name=None, defaults={'_uri': <bound method RedirectRoute._redirect of <Route('/preferences', <class 'package.pages.pages.UserPage'>, name='user_prefs', defaults={}, build_only=False)>>, '_name': 'user_prefs'}, build_only=False)>, <Route('/<username:[a-zA-Z]+>', <class 'package.pages.pages.UserPage'>, name='user_page', defaults={}, build_only=False)>, <Route('/<username:[a-zA-Z]+>/', <class 'webapp2.RedirectHandler'>, name=None, defaults={'_uri': <bound method RedirectRoute._redirect of <Route('/<username:[a-zA-Z]+>', <class 'package.pages.pages.UserPage'>, name='user_page', defaults={}, build_only=False)>>, '_name': 'user_page'}, build_only=False)>, <Route('/message', <class 'package.pages.pages.SendMessagePage'>, name='send_message', defaults={}, build_only=False)>, <Route('/message/', <class 'webapp2.RedirectHandler'>, name=None, defaults={'_uri': <bound method RedirectRoute._redirect of <Route('/message', <class 'package.pages.pages.SendMessagePage'>, name='send_message', defaults={}, build_only=False)>>, '_name': 'send_message'}, build_only=False)>, <Route('/conversations', <class 'package.pages.pages.Messages'>, name='view_messages', defaults={}, build_only=False)>, <Route('/conversations/', <class 'webapp2.RedirectHandler'>, name=None, defaults={'_uri': <bound method RedirectRoute._redirect of <Route('/conversations', <class 'package.pages.pages.Messages'>, name='view_messages', defaults={}, build_only=False)>>, '_name': 'view_messages'}, build_only=False)>, <Route('/conversation/<conversation_id>', <class 'package.pages.pages.ConversationPage'>, name='conversation', defaults={}, build_only=False)>, <Route('/conversation/<conversation_id>/', <class 'webapp2.RedirectHandler'>, name=None, defaults={'_uri': <bound method RedirectRoute._redirect of <Route('/conversation/<conversation_id>', <class 'package.pages.pages.ConversationPage'>, name='conversation', defaults={}, build_only=False)>>, '_name': 'conversation'}, build_only=False)>, <Route('/boards', <class 'package.pages.pages.Boards'>, name='view_boards', defaults={}, build_only=False)>, <Route('/boards/', <class 'webapp2.RedirectHandler'>, name=None, defaults={'_uri': <bound method RedirectRoute._redirect of <Route('/boards', <class 'package.pages.pages.Boards'>, name='view_boards', defaults={}, build_only=False)>>, '_name': 'view_boards'}, build_only=False)>, <Route('/boards/<board>', <class 'package.pages.pages.BoardPage'>, name='view_board', defaults={}, build_only=False)>, <Route('/boards/<board>/', <class 'webapp2.RedirectHandler'>, name=None, defaults={'_uri': <bound method RedirectRoute._redirect of <Route('/boards/<board>', <class 'package.pages.pages.BoardPage'>, name='view_board', defaults={}, build_only=False)>>, '_name': 'view_board'}, build_only=False)>, <Route('/boards/<board>/new_thread', <class 'package.pages.pages.NewThreadPage'>, name='new_thread', defaults={}, build_only=False)>, <Route('/boards/<board>/new_thread/', <class 'webapp2.RedirectHandler'>, name=None, defaults={'_uri': <bound method RedirectRoute._redirect of <Route('/boards/<board>/new_thread', <class 'package.pages.pages.NewThreadPage'>, name='new_thread', defaults={}, build_only=False)>>, '_name': 'new_thread'}, build_only=False)>, <Route('/notifications', <class 'package.pages.pages.Notifications'>, name='notifications', defaults={}, build_only=False)>, <Route('/notifications/', <class 'webapp2.RedirectHandler'>, name=None, defaults={'_uri': <bound method RedirectRoute._redirect of <Route('/notifications', <class 'package.pages.pages.Notifications'>, name='notifications', defaults={}, build_only=False)>>, '_name': 'notifications'}, build_only=False)>, <Route('/img/<blob_key>', <class 'package.pages.pages.DisplayImagePage'>, name='display_image', defaults={}, build_only=False)>, <Route('/img/<blob_key>/', <class 'webapp2.RedirectHandler'>, name=None, defaults={'_uri': <bound method RedirectRoute._redirect of <Route('/img/<blob_key>', <class 'package.pages.pages.DisplayImagePage'>, name='display_image', defaults={}, build_only=False)>>, '_name': 'display_image'}, build_only=False)>, <Route('/', <class 'package.pages.pages.Index'>, name='index', defaults={}, build_only=False)>, <Route('/', <class 'webapp2.RedirectHandler'>, name=None, defaults={'_uri': <bound method RedirectRoute._redirect of <Route('/', <class 'package.pages.pages.Index'>, name='index', defaults={}, build_only=False)>>, '_name': 'index'}, build_only=False)>] 
INFO     2012-08-04 13:16:40,513 utils.py:14] {'index': <Route('/', <class 'package.pages.pages.Index'>, name='index', defaults={}, build_only=False)>, 'view_boards': <Route('/boards', <class 'package.pages.pages.Boards'>, name='view_boards', defaults={}, build_only=False)>, 'notifications': <Route('/notifications', <class 'package.pages.pages.Notifications'>, name='notifications', defaults={}, build_only=False)>, 'friendship': <Route('/friendship', <class 'package.pages.pages.Friendship'>, name='friendship', defaults={}, build_only=False)>, 'signup': <Route('/signup', <class 'package.pages.pages.Signup'>, name='signup', defaults={}, build_only=False)>, 'view_messages': <Route('/conversations', <class 'package.pages.pages.Messages'>, name='view_messages', defaults={}, build_only=False)>, 'conversation': <Route('/conversation/<conversation_id>', <class 'package.pages.pages.ConversationPage'>, name='conversation', defaults={}, build_only=False)>, 'send_message': <Route('/message', <class 'package.pages.pages.SendMessagePage'>, name='send_message', defaults={}, build_only=False)>, 'new_thread': <Route('/boards/<board>/new_thread', <class 'package.pages.pages.NewThreadPage'>, name='new_thread', defaults={}, build_only=False)>, 'view_board': <Route('/boards/<board>', <class 'package.pages.pages.BoardPage'>, name='view_board', defaults={}, build_only=False)>, 'display_image': <Route('/img/<blob_key>', <class 'package.pages.pages.DisplayImagePage'>, name='display_image', defaults={}, build_only=False)>, 'user_prefs': <Route('/preferences', <class 'package.pages.pages.UserPage'>, name='user_prefs', defaults={}, build_only=False)>, 'user_page': <Route('/<username:[a-zA-Z]+>', <class 'package.pages.pages.UserPage'>, name='user_page', defaults={}, build_only=False)>} 
INFO     2012-08-04 13:16:40,515 __init__.py:26] /conversations 
INFO     2012-08-04 13:16:40,533 dev_appserver.py:2952] "GET /conversations HTTP/1.1" 404 - 
INFO     2012-08-04 13:16:40,626 dev_appserver.py:2952] "GET /favicon.ico HTTP/1.1" 200 - 

我到底有什么没注意到?

请您参考如下方法:

我有一条路线覆盖了所有其他路线,但在它们上面引发了 404(/username 导致用户页面;没有名为“对话”的用户)。在切换到装饰器路由方法时,我忘了让这条路由具有最低优先级。

我首先使用相同的路由方法构建了一个最小的应用程序,以查看它在 App Engine 上运行良好,从而弄明白了这一点。然后我在我的处理程序中捕获了 HTTPNotFound 异常,并在它们发生时打印堆栈跟踪;这很清楚地表明这是我的用户页面类的 get 方法中的一行故意抛出 404,然后问题就相当明显了。