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

时间:2012-10-10 15:55:10

标签: django post angularjs

我正在尝试使用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();

网址被转换为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请求

我知道通过标头传递csrf令牌的this方法,但我正在寻找一种可能将令牌添加到帖子请求的正文,如建议{ {3}}。是否可以在角度中将数据添加到帖子请求主体?

作为附加读物,您可以查看有关资源,删除的尾部斜杠以及当前具有的限制资源的这些讨论:heredisc1。 在其中一个讨论中,作者之一建议当前不使用资源,而是使用disc2方法。

5 个答案:

答案 0 :(得分:39)

我知道这已经超过1年了,但如果有人遇到同样的问题,角度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)的内置系统。

答案 1 :(得分:7)

你不能这样打电话:

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

data可以是您希望传递的任何内容,然后只需将&{{csrf_token}}附加到其中。

在您的资源params:{}中,尝试在csrfmiddlewaretoken:{{csrf_token}}

中添加params

编辑:

您可以将数据作为

传递给请求正文
item.$update({csrfmiddlewaretoken:{{csrf_token}}})

和标题为

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

这是一张未记录的issue

答案 2 :(得分:1)

在最近的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 :(得分:0)

var app = angular.module('angularFoo', ....

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

答案 4 :(得分:0)

我用这个:

在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