如何使用 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
可以是您希望传递的任何内容,然后只需将 &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 + Angular:如何将 angularjs $http 数据或参数发送到 django 并在 django 中解释?