简短版:在django网站上,我可以从request.GET
但不是request.POST
获取值,以响应Twilio的请求。我怀疑它与csrf有关,但我不确定如何调试问题。详情如下。
长版: 我正在通过一个项目帮助一个朋友,我们正在使用Twilio REST API对SMS进行医学调查。我在该域上有一个域和一个非常简单的django构建的站点,我构建的只是为了更好地熟悉django,所以我们正在使用它。
我们正在收集对我们调查的短信回复,并且作为Twilio API的一部分,它会将我们号码的任何回复发送到帐户下指定的网址,因此我们的回复定位如下:
...mydomain.com/some_page/another_page/
Twilio请求看起来如下所示:
...mydomain.com/some_page/another_page/?AccountSid=###SOME_LONG_ACCOUNT_SIDE&From=%2BPHONE_NUMBER&Body=bla+BLA+bla+BLA&SmsSid=##MESSAGE_ID_KEY&SmsMessageSid=##MESSAGE_ID_KEY&FromCity=Santa+Cruz&FromState=California...
工作代码
我正在测试传入的请求里面有我们的AccountSid
(与数据库中的值相比),而我的views.py
中的应用程序,我有一些看起来像下面的内容(和这工作):
from our_app import TwilioAccount
our_account = TwilioAccount.objects.get(id=1)
def twilio_response(request):
assert request.GET.get('AccountSid', None) == our_account.account_sid
## log the incoming request to the database under survey responses...
非工作代码
如果我登录我们的Twilio帐户并将请求方法切换为POST
,然后我将所有我的数据收集切换到request.POST
,则上述断言声明失败。进一步调试显示我的QueryDict在POST POST: {}
下是空的,因此没有抓取键值。
我认为这可能是因为django下的POST
需要csrf_token
,但我认为检查AccountSid是相当不错的,所以我导入了csrf_exempt
并用上面的函数包装了上面的函数:
@csrf_exempt
def twilio_response(request):
assert request.POST.get('AccountSid', None) == our_account.account_sid
## log the incoming request to the database under survey responses...
AssertionError: ...
这与完全相同的请求不起作用:QueryDict为空。
问题:
1)为了让@csrf_exempt
工作,我还需要做些什么吗?替代问题:这是一个非常糟糕的方式吗?在使用其他公司的API而非实际的登录用户时,人们通常如何满足此要求?
1a)我可以将它作为GET
请求保留,而不是使其成为csrf_exempt,因为它知道它仍在检查针对我们的account_sid的所有传入请求。我应该这样做还是真的很天真的方式呢?
2)我渴望学习最好的方法:我应该构建一个django表单然后将请求路由到我的表单并测试有效性并以这种方式清理数据吗?如果是这样,任何人都可以给我一个关于视图/表单看起来像什么的宽松轮廓(完成csrf_token),而不是表格的模板?
答案 0 :(得分:6)
来自Twilio Developer Evangelist团队的Matt。
1)用@csrf_exempt包装twilio_response函数以删除Django CSRF令牌检查是正确的方法。 Twilio不会生成Django CSRF令牌。相反,还有其他方法来验证来自Twilio的POST,例如使用X-Twilio-Signature标头进行签名验证。有关详细信息,请参阅Twilio security docs。
1a)使用GET请求便于测试和调试,但POST应该用于生产。根据HTTP规范,GET请求没有正文,因此结果将在查询字符串中传递。如果参数太大,例如文本消息的最大长度为1600个字符,则URL中的查询字符串可能超过URL的最大长度,并且在处理字符串时可能会导致问题。
2)Django表单是这个用例的好方法,特别是利用现有模型来保存响应的ModelForm。例如,您的ModelForm可以 如果要将数据保存到TwilioMessage模型,请查看以下内容:
from django.forms import ModelForm
from .models import TwilioMessage
class MessageForm(ModelForm):
pass
class Meta:
model = ReactionEvent
# include all fields you're saving from the form here
fields = ['body', 'to', 'from_', 'signature',]
答案 1 :(得分:0)
在我的选项中,您是否在settings.py中打开了DEBUG(= True)? 在DEBUG模式下,你可以引发异常(引发Exception('')),然后你可以在你的页面中看到环境Varialbe,如url,GET,POST.etc .... 或者返回HttpResponse(request.POST.dict()) 看到post变量。 使用crsf_exempt是发布时的正确方法