在下面的代码中,AngularJS $http方法调用URL,并提交xsrf对象作为“Request Payload”(在Chrome调试器网络选项卡中描述)。jQuery $。ajax方法做同样的调用,但提交xsrf作为“表单数据”。

如何让AngularJS将xsrf作为表单数据而不是请求有效载荷提交?

var url = 'http://somewhere.com/';
var xsrf = {fkey: 'xsrf key'};

$http({
    method: 'POST',
    url: url,
    data: xsrf
}).success(function () {});

$.ajax({
    type: 'POST',
    url: url,
    data: xsrf,
    dataType: 'json',
    success: function() {}
});

当前回答

AngularJS的做法是正确的,因为它在http-request头中执行了以下内容类型:

Content-Type: application/json

如果你像我一样使用php,甚至使用Symfony2,你可以简单地扩展json标准的服务器兼容性:http://silex.sensiolabs.org/doc/cookbook/json_request_body.html

Symfony2方式(例如在你的DefaultController中):

$request = $this->getRequest();
if (0 === strpos($request->headers->get('Content-Type'), 'application/json')) {
    $data = json_decode($request->getContent(), true);
    $request->request->replace(is_array($data) ? $data : array());
}
var_dump($request->request->all());

好处是,你不需要使用jQuery参数,你可以使用AngularJS的原生方式来做这样的请求。

其他回答

这并不是一个直接的答案,而是一个略有不同的设计方向:

不要将数据作为表单发布,而是作为JSON对象直接映射到服务器端对象,或者使用REST风格的路径变量

现在我知道这两个选项都不适合您的情况,因为您试图传递一个XSRF键。像这样把它映射成一个路径变量是一个糟糕的设计:

http://www.someexample.com/xsrf/{xsrfKey}

因为本质上你会想要传递xsrf密钥到其他路径,/login, /book-appointment等,你不想弄乱你漂亮的URL

有趣的是,将它作为对象字段添加也不合适,因为现在你传递给服务器的每个json对象都必须添加字段

{
  appointmentId : 23,
  name : 'Joe Citizen',
  xsrf : '...'
}

您当然不希望在服务器端类上添加另一个与域对象没有直接语义关联的字段。

在我看来,传递xsrf密钥的最佳方式是通过HTTP报头。许多xsrf保护服务器端web框架库都支持这一点。例如,在Java Spring中,您可以使用X-CSRF-TOKEN头传递它。

Angular出色的将JS对象绑定到UI对象的能力意味着我们可以摆脱一起发布表单的做法,而是发布JSON。JSON可以很容易地反序列化为服务器端对象,并支持复杂的数据结构,如映射、数组、嵌套对象等。

如何在表单有效负载中发布数组?也许是这样的:

shopLocation=downtown&daysOpen=Monday&daysOpen=Tuesday&daysOpen=Wednesday

或:

shopLocation=downtwon&daysOpen=Monday,Tuesday,Wednesday

都是糟糕的设计。

从AngularJS v1.4.0开始,就有一个内置的$httpParamSerializer服务,它可以根据文档页面上列出的规则将任何对象转换为HTTP请求的一部分。

它可以这样使用:

$http.post('http://example.com', $httpParamSerializer(formDataObj)).
    success(function(data){/* response status 200-299 */}).
    error(function(data){/* response status 400-999 */});

请记住,对于正确的表单post, Content-Type头必须更改。要对所有POST请求全局执行此操作,可以使用以下代码(取自Albireo的half-answer):

$http.defaults.headers.post["Content-Type"] = "application/x-www-form-urlencoded";

为了只针对当前post,需要修改request-object的headers属性:

var req = {
 method: 'POST',
 url: 'http://example.com',
 headers: {
   'Content-Type': 'application/x-www-form-urlencoded'
 },
 data: $httpParamSerializer(formDataObj)
};

$http(req);

我目前使用的是我在AngularJS谷歌组中找到的解决方案。

$http
.post('/echo/json/', 'json=' + encodeURIComponent(angular.toJson(data)), {
    headers: {
        'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'
    }
}).success(function(data) {
    $scope.data = data;
});

注意,如果您使用的是PHP,则需要使用类似Symfony 2 HTTP组件的Request::createFromGlobals()来读取该文件,因为$_POST不会自动加载。

你可以全局定义行为:

$http.defaults.headers.post["Content-Type"] = "application/x-www-form-urlencoded";

所以你不必每次都重新定义它:

$http.post("/handle/post", {
    foo: "FOO",
    bar: "BAR"
}).success(function (data, status, headers, config) {
    // TODO
}).error(function (data, status, headers, config) {
    // TODO
});

你唯一需要改变的是在创建$http对象时使用属性"params"而不是"data":

$http({
   method: 'POST',
   url: serviceUrl + '/ClientUpdate',
   params: { LangUserId: userId, clientJSON: clients[i] },
})

在上面的例子中,客户端[i]只是JSON对象(没有以任何方式序列化)。如果你使用“params”而不是“data”,angular会使用$httpParamSerializer: https://docs.angularjs.org/api/ng/service/$httpParamSerializer为你序列化对象