我正在尝试使用Google App Engine队列API,我在测试时遇到了问题。似乎在这个过程的某些部分,CSRF无法正常工作。
据我所知,api执行调用url的任务,并在后台执行和http请求。
完整的网址是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'
因此,GAE api在后台执行任务的请求中缺少csrf中间件,cookie,一些数据或响应本身。
如何解决这个问题而不在Django上禁用CSRF?然而,它与djangoappengine完全一致吗?
Down是我正在使用的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)”来获取输出。
答案 0 :(得分:4)
如果您启用了CsrfViewMiddleware
,则Django会在您的观看次数的所有POST中都需要csrf_token
。
Django提供了一个装饰器@csrf_exempt
,您应将其置于任务队列视图中。这会为这些视图关闭中间件。
或者,您可以完全避免使用CsrfViewMiddleware
,而是在需要的地方使用@csrf_protect
装饰器。我不建议这样做 - 保护所有地方可能更安全,并为您的任务队列视图留出少量豁免。
(最后一个注意事项:上面的两个答案 - 您的视图出现问题,或者您只是将GET用于任务队列 - 让我错了。您的视图没有问题,POST是正确的用于任务队列任务的动词。)
答案 1 :(得分:3)
查看csrf.py的source,看起来这只会在你的视图函数返回None(或者没有显式返回)时发生,在这种情况下Python会隐式返回None。看看你的代码,我不知道怎么会发生这种情况 - 你确定这是你的确切部署代码吗?
此外,您可能不希望在任务队列任务中使用get_object_or_404
- 如果找不到该对象,它将抛出404,这将导致任务出错并无限期重试
您也不应该需要CSRF保护(根据您的TODO);相反,请确保任务队列URL标记为仅管理员,并且只会由任务队列服务调用。
答案 2 :(得分:1)