2011-01-26 31 views
3

我想使用Google App Engine隊列API,我在測試這個時遇到問題。看起來在CSRF的某些部分,它不起作用。使用Google App Engine隊列服務與Django

據我所知,api執行調用url的任務,並在後臺製作和http請求。

完整的URL是API的調用是→http://localhost.localdomain:8000/admin/cooking/recipe/36/chefworker/

當引發此異常:

Traceback (most recent call last): 
    File "/home/mariocesar/Proyectos/Cooking/cooking/django/core/handlers/base.py", line 100, in get_response 
    response = callback(request, *callback_args, **callback_kwargs) 
    File "/home/mariocesar/Proyectos/Cooking/cooking/django/views/decorators/csrf.py", line 24, in wrapped_view 
    resp.csrf_exempt = True 
AttributeError: 'NoneType' object has no attribute 'csrf_exempt' 

所以,CSRF中間件,餅乾,一些數據或響應本身是從失蹤GAE api在後臺執行任務所做的請求。

如何解決這個問題,而不禁用Django的CSRF?然而,它可以與djangoappengine在所有?

向下是我正在使用的models.py和admin.py文件。

models.py

from django.db import models 

class Recipe(models.Model): 
    name = models.CharField(max_length=140) 
    description = models.TextField() 
    cooking_time = models.PositiveIntegerField() 
    status = models.CharField(max_length=40) 

    def __unicode__(self): 
     return self.name 

    def cookthis(self): 
     import time 
     self.status = 'The chef is cooking this recipe' 
     self.save() 
     time.sleep(obj.cooking_time) 
     self.status = 'It\'s done ! the recipe is ready to serve' 
     self.save() 

admin.py

import logging 

from django.contrib import admin, messages 
from django.http import HttpResponse 
from django.utils.functional import update_wrapper 
from django.contrib.admin.util import unquote 
from django.shortcuts import get_object_or_404, render_to_response 
from django import template 
from django.core.urlresolvers import reverse 
from google.appengine.api import taskqueue 
from google.appengine.api.taskqueue import TaskAlreadyExistsError 

from cooking.models import Recipe 
from django.views.decorators.csrf import csrf_exempt 

class AdminRecipe(admin.ModelAdmin): 
    def get_urls(self): 
     from django.conf.urls.defaults import patterns, url 

     def wrap(view): 
      def wrapper(*args, **kwargs): 
       return self.admin_site.admin_view(view)(*args, **kwargs) 
      return update_wrapper(wrapper, view) 

     info = self.model._meta.app_label, self.model._meta.module_name 

     urlpatterns = super(AdminRecipe, self).get_urls() 
     myurls = patterns('', 
      url(r'^(.+)/cook/$', 
       wrap(self.cook_view), 
       name='%s_%s_chefworker' % info), 
      url(r'^(.+)/chefworker/$', 
       wrap(self.chefworker_worker), 
       name='%s_%s_chefworker' % info), 
     ) 
     return myurls + urlpatterns 

    def cook_view(self, request, object_id, extra_context=None): 
     obj = get_object_or_404(Recipe, pk=unquote(object_id)) 
     if request.POST: 
      try: 
       taskqueue.add(
        name="recipie-%s" % obj.id, 
        url=reverse('admin:cooking_recipe_chefworker', args=(obj.id,)) 
       ) 
       messages.add_message(request, messages.INFO, 'Chef is cooking the recipe.') 
      except TaskAlreadyExistsError: 
       messages.add_message(request, messages.ERROR, 'chef is already cooking that recipe.') 

     context_instance = template.RequestContext(request, current_app=self.admin_site.name) 
     return render_to_response("admin/cooking/recipe/cook_view.html", {'object': obj}, context_instance=context_instance) 

    #TODO: Add csrf token on form 
    @csrf_exempt 
    def chefworker_worker(self, request, object_id, extra_context=None): 
     import time 

     if request.POST: 
      obj = get_object_or_404(Recipe, pk=unquote(object_id)) 
      obj.cookthis() 

     return HttpResponse('done') 

admin.site.register(Recipe, AdminRecipe) 

重要提示: 很難調試這個錯誤,導致dev_appserver記錄器只是提高403錯誤,沒有其他信息;因此,我必須修補google/appengine/api/taskqueue/taskqueue_stub.py第574行文件並添加「logging.info('response --- \ n%s'%result)」以獲取輸出。

回答

3

看看csrf.py的source,看起來只有當你的視圖函數返回None(或者沒有顯式返回,在這種情況下Python會隱式返回None)時纔會發生這種情況。看看你的代碼,雖然我不明白這是怎麼發生的 - 你確定這是你確切的部署代碼嗎?

此外,您可能不希望在任務隊列任務中使用get_object_or_404 - 如果找不到該對象,則會拋出一個404,這將導致該任務出錯並無限期地重試。

您也不應該需要CSRF保護(根據您的TODO);相反,請確保任務隊列URL標記爲僅限管理員,並且只會由任務隊列服務調用。

+0

的確,http響應是問題。 GAE api使用web請求來啓動任務,而這並沒有與django中間件(作爲CSRF中間件)完全集成。最好的解決方案不是使用POST方法,而是使用GET方法執行任務,因爲@Guy建議使用 –

+0

@Mario這看起來確實不像'最好'的解決方案。最好的解決方案是弄清楚如何從視圖函數中返回None。 –

+0

@Nick,與CSRF中間件相關,@ csrf_exempt修飾器查找google api調用任務時未設置的變量。這是獲得403或301 http錯誤的解決方案。 –

4

如果您啓用了CsrfViewMiddleware,則Django將在您的視圖中的所有POST中需要csrf_token

Django提供了一個裝飾器,@csrf_exempt,您應該放置在您的任務隊列視圖中。這會關閉中間件僅用於這些視圖。

或者,您可以完全避免使用CsrfViewMiddleware,而是在需要的地方使用@csrf_protect裝飾器。我不建議這樣做 - 在任何地方進行保護可能更安全,併爲您的任務隊列視圖劃出少量豁免。

(最後一個註釋:上面的兩個答案 - 你的視圖有問題,或者你應該只使用GET來完成任務隊列 - 打我錯了,你的視圖沒有問題,POST是用於任務隊列任務的右動詞。)

+0

是的..這解決了我的問題:) –