如何使用 Django 和 AngularJS 创建 POST 请求(包括 CSRF 令牌)

Posted

技术标签:

【中文标题】如何使用 Django 和 AngularJS 创建 POST 请求(包括 CSRF 令牌)【英文标题】:How to create a POST request (including CSRF token) using Django and AngularJS 【发布时间】:2012-10-01 04:06:20 【问题描述】:

我正在尝试使用 angular.js 对此 Django 视图创建 POST 请求。

class PostJSON4SlickGrid(View):
    """
    REST POST Interface for SlickGrid to update workpackages
    """

    def post(self, request, root_id, wp_id, **kwargs):
        print "in PostJSON4SlickGrid"
        print request.POST
        return HttpResponse(status=200)

因此我创建了这个资源。

myModule.factory('gridData', function($resource) 
    //define resource class
    var root =  root.pk ;
    return $resource('% url getJSON4SlickGrid root.pk %:wpID/', wpID:'@id',
            get: method:'GET', params:, isArray:true,
            update:method:'POST'
    );
);

在控制器中调用 get 方法可以正常工作。该网址被翻译为http://127.0.0.1:8000/pm/rest/tree/1/

function gridController($scope, gridData)
    gridData.get(function(result) 
        console.log(result);
        $scope.treeData = result;
        //broadcast that asynchronous xhr call finished
        $scope.$broadcast('mySignal', fake: 'Hello!');  
    );

虽然我在执行更新/POST 方法时遇到问题。

item.$update();

URL 被转换为http://127.0.0.1:8000/pm/rest/tree/1/345,缺少尾部斜杠。如果在 URL 定义中不使用尾部斜杠,则可以轻松避免这种情况。

url(r'^rest/tree/(?P<root_id>\d+)/(?P<wp_id>\d+)$', PostJSON4SlickGrid.as_view(), name='postJSON4SlickGrid'),

而不是

url(r'^rest/tree/(?P<root_id>\d+)/(?P<wp_id>\d+)/$', PostJSON4SlickGrid.as_view(), name='postJSON4SlickGrid'),

使用不带斜杠的解决方法,我现在得到一个 403(禁止)状态代码,这可能是因为我没有在 POST 请求中传递 CSRF 令牌。因此,我的问题归结为如何将 CSRF 令牌传递到 Angular 创建的 POST 请求中?

我知道 this 通过标头传递 csrf 令牌的方法,但我正在寻找一种可能性,以将令牌添加到发布请求的正文中,正如 here 所建议的那样.是否可以在 Angular 中将数据添加到发布请求正文?

作为其他阅读材料,您可以查看这些关于资源、删除尾部斜杠以及资源当前存在的限制的讨论:disc1 和disc2。 在一次讨论中,一位作者建议目前不要使用资源,而是使用this 方法。

【问题讨论】:

【参考方案1】:

我用这个:

在 Django 视图中:

@csrf_protect
def index(request):
    #Set cstf-token cookie for rendered template
    return render_to_response('index.html', RequestContext(request))

在 App.js 中:

(function(A) 
    "use strict";
    A.module('DesktopApplication', 'ngCookies' ]).config(function($interpolateProvider, $resourceProvider) 
        //I use $ and $ as Angular directives
        $interpolateProvider.startSymbol('$');
        $interpolateProvider.endSymbol('$');
        //Without this Django not processed urls without trailing slash
        $resourceProvider.defaults.stripTrailingSlashes = false; 
    ).run(function($http, $cookies) 
        //Set csrf-kookie for every request
        $http.defaults.headers.post['X-CSRFToken'] = $cookies.csrftoken;
        $http.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded';
    );
(this.angular));

要发送正确的请求,您必须将对象转换为参数形式:

$http.post('/items/add/', $.param(name: 'Foo'));//Here $ is jQuery

【讨论】:

【参考方案2】:

在最近的 angularjs 版本中给出的解决方案不起作用。所以我尝试了以下

首先在标记中添加django标签% csrf_token %。

在您的应用配置文件中添加 $http 检查器

angular.module('myApp').config(function ( $httpProvider) 
   $httpProvider.interceptors.push('myHttpRequestInterceptor'); 
);
然后定义myHttpRequestInterceptor

angular.module("myApp").factory('myHttpRequestInterceptor', function ( ) 

   return 
             config.headers =  
              'X-CSRFToken': $('input[name=csrfmiddlewaretoken]').val()  
              
   return config; 
  ; 
);

它将在所有角度请求中添加 X-CSRFToken

最后你需要添加 Django 中间件“django.middleware.csrf.CsrfViewMiddleware'” 它将解决 CSRF 问题

【讨论】:

【参考方案3】:

我知道这已经超过 1 年了,但是如果有人偶然发现了同样的问题,Angular JS 已经有一个 CSRF cookie 获取机制(AngularJS 的版本从 1.1.5 开始),你只需要告诉 angular 什么是 django 使用的 cookie 的名称,也是它应该用来与服务器通信的 HTTP 标头。

为此使用模块配置:

var app = angular.module('yourApp');
app.config(['$httpProvider', function($httpProvider) 
    $httpProvider.defaults.xsrfCookieName = 'csrftoken';
    $httpProvider.defaults.xsrfHeaderName = 'X-CSRFToken';
]);

现在每个请求都会有正确的 django CSRF 令牌。在我看来,这比在每个请求上手动放置令牌要正确得多,因为它使用了两个框架(django 和 angularJS)的内置系统。

【讨论】:

您还需要确保使用 ensure_csrf_cookie 装饰器包裹视图:docs.djangoproject.com/en/dev/ref/contrib/csrf/… 只有在您激活 CSRF 中间件后由于某种原因您的视图没有发送 CSRF 令牌时。 非常感谢。我从 20 小时开始就头疼了。【参考方案4】:
var app = angular.module('angularFoo', ....

app.config(["$httpProvider", function(provider) 
  provider.defaults.headers.common['X-CSRFToken'] = '<<csrftoken value from template or cookie>>';
])

【讨论】:

【参考方案5】:

你不能这样打电话吗:

$http(
    method: 'POST',
    url: url,
    data: xsrf,
    headers: 'Content-Type': 'application/x-www-form-urlencoded'
)

data 可以是您希望传递的任何内容,然后只需将 &amp;csrf_token 附加到它。

在您的资源params: 中,尝试在params 中添加csrfmiddlewaretoken:csrf_token

编辑:

您可以将数据传递给请求正文

item.$update(csrfmiddlewaretoken:csrf_token)

并将标题作为

var csrf = ' csrf_token '; 
update:method:'POST', headers: 'X-CSRFToken' : csrf  

这是一个无证的issue

【讨论】:

谢谢。我更喜欢使用资源功能,因为它通常可以正常工作。我只是错过了添加 CSFR 令牌的可能性/语法。 编辑了一下,我认为 csrf 应该通过资源调用进入你的参数中。 不,不幸的是没有运气。我猜 Django 只有在通过 POST 请求的主体传输时才能识别令牌。将它添加到参数时,我得到一个 403 权限被拒绝,使用这个角度生成的 url:POST /pm/rest/tree/1/384?csrfmiddlewaretoken=FKV6x4YUurDEPerFhcGRHK1qVJwzXn HTTP/1.1" 403 是的,它必须是正文,没有意识到参数引用了 url 参数,编辑了我的答案以显示如何将数据传递给请求正文。 太好了,我还发现您可以轻松设置标题: var csrf = ' csrf_token ';更新:方法:'POST',标题:'X-CSRFToken':csrf 。您是否介意将此解决方案也包含在您的答案中以供进一步参考?

以上是关于如何使用 Django 和 AngularJS 创建 POST 请求(包括 CSRF 令牌)的主要内容,如果未能解决你的问题,请参考以下文章

Django 和 AngularJS:如何显示来自 Django 调试错误消息的 Angular $http 错误

如何使用 AngularJS(Django 框架)将值保存到数据库

电子邮件验证和密码重置 - django rest 框架和 angularjs

使用 Angularjs 和 Mongoose (meanjs) 创建子文档并保存值

如何让 Django 和 ReactJS 一起工作?

Django + Angular:如何将 angularjs $http 数据或参数发送到 django 并在 django 中解释?