如何在 GAE 任务队列中执行需要 OAuth 的操作? [英] How to do OAuth-requiring operations in a GAE Task Queue?

查看:30
本文介绍了如何在 GAE 任务队列中执行需要 OAuth 的操作?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个简单的 Google App Engine 应用,其中包含一个 /update 页面,用于更新 YouTube 播放列表.它看起来像这样:

I have a simple Google App Engine app that includes a /update page which updates a YouTube playlist. It looks like this:

class UpdatePage(webapp2.RequestHandler):

    @decorator.oauth_required
    def get(self):
        update_result = self.update_playlist()
        ...

routes = [('/update', UpdatePage),
          (decorator.callback_path, decorator.callback_handler())]
app = webapp2.WSGIApplication(routes, debug=True)

它按预期工作并且 update_playlist() 方法完成了它的工作,但事实证明在某些情况下它可以运行很长时间,导致 DeadlineExceededError.因此,在阅读了可用选项后,我认为 Task Queue API 是可行的方法(对吗?)并且我正在尝试使用它,遵循 在 Python 中使用推送队列指南.

It works as expected and the update_playlist() method does its job, but it turns out that under some circumstances it can run for a pretty long time, resulting in a DeadlineExceededError. So after reading about the available options, I figured the Task Queue API is the way to go (right?) and I'm trying to use it, following the Using Push Queues in Python guide.

→总之,我把UpdatePage拆分成UpdatePageHandler + UpdatePageWorker:

→ In short, I split UpdatePage into UpdatePageHandler + UpdatePageWorker:

class UpdateHandlerPage(webapp2.RequestHandler):

    @decorator.oauth_required
    def get(self):
        taskqueue.add(url='/updateworker')

class UpdateWorkerPage(webapp2.RequestHandler):

    def post(self):
        update_result = self.update_playlist()
        ...

routes = [('/update', UpdateHandlerPage),
          ('/updateworker', UpdateWorkerPage),
          (decorator.callback_path, decorator.callback_handler())]
app = webapp2.WSGIApplication(routes, debug=True)

不幸的是,在进行拆分之后,我的 OAuth2 装饰器似乎不再起作用了:

Unfortunately, after doing the split it seems my OAuth2 decorator no longer does its job:

INFO     2013-05-30 17:08:53,971 discovery.py:709] URL being requested: https://www.googleapis.com/youtube/v3/playlists?alt=json&part=snippet%2Cstatus
WARNING  2013-05-30 17:08:53,975 urlfetch_stub.py:480] Stripped prohibited headers from URLFetch request: ['content-length']
INFO     2013-05-30 17:08:54,351 client.py:493] Refreshing due to a 401
INFO     2013-05-30 17:08:54,361 appengine.py:276] make: Got type <class 'google.appengine.api.datastore_types.Blob'>
INFO     2013-05-30 17:08:54,363 appengine.py:289] validate: Got type <class 'oauth2client.client.OAuth2Credentials'>
INFO     2013-05-30 17:08:54,364 client.py:680] Refreshing access_token
INFO     2013-05-30 17:08:54,746 client.py:699] Failed to retrieve access token: {
  "error" : "invalid_grant"
}
INFO     2013-05-30 17:08:54,757 appengine.py:276] make: Got type <class 'google.appengine.api.datastore_types.Blob'>
INFO     2013-05-30 17:08:54,759 appengine.py:289] validate: Got type <class 'oauth2client.client.OAuth2Credentials'>
INFO     2013-05-30 17:08:54,761 appengine.py:289] validate: Got type <class 'oauth2client.client.OAuth2Credentials'>
INFO     2013-05-30 17:08:54,762 appengine.py:265] get: Got type <class 'oauth2client.appengine.CredentialsModel'>

如果不是装饰 UpdateHandlerPage:get,而是装饰 UpdateWorkerPage:post,我会得到一个无限失败循环:

And if instead of decorating UpdateHandlerPage:get I decorate UpdateWorkerPage:post, I get an infinite failure loop instead:

INFO     2013-05-30 17:24:31,307 discovery.py:190] URL being requested: https://www.googleapis.com/discovery/v1/apis/youtube/v3/rest?userIp=127.0.0.1
INFO     2013-05-30 17:24:34,960 dev_appserver.py:3105] "GET /update HTTP/1.1" 200 -
INFO     2013-05-30 17:24:35,060 dev_appserver.py:3105] "POST /updateworker HTTP/1.1" 302 -
WARNING  2013-05-30 17:24:35,065 taskqueue_stub.py:1980] Task task1 failed to execute. This task will retry in 0.100 seconds
INFO     2013-05-30 17:24:35,240 dev_appserver.py:3105] "POST /updateworker HTTP/1.1" 302 -
WARNING  2013-05-30 17:24:35,245 taskqueue_stub.py:1980] Task task1 failed to execute. This task will retry in 0.200 seconds
...

我能做什么?感谢您的帮助!

What can I do? Thanks for your help!

推荐答案

由于任务队列任务将由您的应用程序生成,因此您的原始请求中的任何标头都不会通过.特别是,Cookies 标头通过您的应用程序的 SACSID cookie(由 App Engine 用户 API 提供)识别您的用户.

Since the Task Queue task will be spawned by your application, none of the headers from your original request will be sent through. In particular, the Cookies header identifying your user via the SACSID cookie for your application (provided by the App Engine Users API).

更新:(这是在原始帖子之后添加的.) 由于没有 cookie,标识用户的 SACSID cookie 将不存在,因此decorator.oauth_required 指定将强制重定向(即 HTTP 302)每次 cron 作业运行.

UPDATE: (This was added after the original post.) As a result of no cookies, the SACSID cookie identifying the user won't be there, hence the decorator.oauth_required designation will force a redirect (which is HTTP 302) EVERY time the cron job runs.

与其尝试从装饰器中获取当前用户,不如将 App Engine 用户 ID 传递给您的任务.首先获取当前用户(在你的装饰方法中):

Instead of trying to get the current user from the decorator, you are better off passing along the App Engine User ID to your task. First get the current user (within your decorated method):

from google.appengine.api import users
# Guaranteed not to be None by the decorator
current_user = users.get_current_user()

然后在任务中传递 App Engine 用户 ID

and then pass along the App Engine User ID in the task

import urllib
query_string = urllib.urlencode({'user_id': current_user.user_id()})
taskqueue.add(url='/updateworker?' + query_string)

然后在您的任务中,您可以检索该 user_id

Then within your task, you can retrieve grab that user_id

# This is the 'user_id' you appended in the query string
user_id = self.request.get('user_id')

并使用它来获取该用户的凭据 在装饰器中完成:

and use it to get that user's credentials as is done in the decorator:

from oauth2client.appengine import CredentialsModel
from oauth2client.appengine import StorageByKeyName
# This assumes you are using the defaults for OAuth2Decorator,
# which your above code is
credentials = StorageByKeyName(
    CredentialsModel, user_id, 'credentials').get()

这篇关于如何在 GAE 任务队列中执行需要 OAuth 的操作?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆