5

简短版:在 django 网站上,我可以从 Twilio 的请求中获取值,request.GET但不能request.POST响应。我怀疑它与csrf有关,但我不确定如何调试问题。详情如下。

长版: 我正在帮助一位朋友完成一个项目,我们正在使用 Twilio REST API 通过 SMS 进行医学调查。我有一个域和一个非常简单的 django 构建的站点,我构建它只是为了更好地熟悉 django,所以我们正在使用它。

我们正在收集对我们的调查的 SMS 响应,作为 Twilio API 的一部分,它会将对我们号码的任何响应发送到帐户下指定的 url,因此我们的响应目标类似于以下内容:

...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: {},所以没有抓取到键值。

我认为这可能是因为POST在 django 下需要 a 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) 与其将其设为 csrf_exempt,不如将其保留为GET请求,因为它仍在检查所有传入请求是否与我们的 account_sid 相对应。我应该这样做还是这样做真的很幼稚?

2)我渴望学习做到这一点的最佳方法:我是否应该构建一个 django 表单,然后将请求路由到我的表单并测试有效性并以这种方式清理数据?如果是这样,当没有表单模板时,谁能给我一个关于视图/表单外观的粗略大纲(完整的 csrf_token)?

4

2 回答 2

7

来自 Twilio Developer Evangelist 团队的 Matt 在这里。

1) 使用@csrf_exempt 包装您的 twilio_response 函数以删除 Django CSRF 令牌检查是正确的方法。Twilio 不会生成 Django CSRF 令牌。相反,还有其他方法可以验证来自 Twilio 的 POST,例如使用 X-Twilio-Signature 标头进行签名验证。有关详细信息,请参阅Twilio 安全文档

1a) 使用 GET 请求方便测试和调试,但 POST 应该在生产中使用。根据 HTTP 规范,GET 请求没有正文,因此结果在查询字符串中传递。如果参数太大,例如文本消息的最大长度为 1600 个字符,则 URL 中的查询字符串可能会超过 URL 的最大长度,并可能在处理字符串时出现问题。

2) Django 表单是解决此用例的好方法,尤其是利用现有模型来保存响应的 ModelForm。例如,如果您将数据保存到 TwilioMessage 模型,您的 ModelForm 可能如下所示:

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',] 
于 2014-01-13T21:21:46.203 回答
0

在我的选项中,您是否在 settings.py 中打开了 DEBUG(= True)?在调试模式下,你可以引发一个异常(引发异常('')),然后你可以在你的页面中看到环境变量如url,GET,POST.etc....或返回HttpResponse(request.POST .dict()) 查看 post 变量。发布时使用 crsf_exempt 是正确的方法

于 2013-08-12T23:19:45.877 回答