ChatGPT解决这个技术问题 Extra ChatGPT

如何将数据作为表单数据而不是请求有效负载发布?

在下面的代码中,AngularJS $http 方法调用 URL,并将 xsrf 对象作为“请求有效负载”提交(如 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() {}
});
这是一个非常有用的问题。它允许我将有效负载作为字符串发送(通过更改 Content-Type),这使我不必在 POST/GET 之前处理 OPTIONS。
我有同样的问题,这是在我请求网址之后,但我无法获取我提交的参数

C
Community

需要在传递的 $http 对象中添加以下行:

headers: {'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'}

并且传递的数据应转换为 URL 编码的字符串:

> $.param({fkey: "key"})
'fkey=key'

所以你有类似的东西:

$http({
    method: 'POST',
    url: url,
    data: $.param({fkey: "key"}),
    headers: {'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'}
})

来自:https://groups.google.com/forum/#!msg/angular/5nAedJ1LyO0/4Vj_72EZcDsJ

更新

要使用 AngularJS V1.4 添加的新服务,请参阅

仅使用 AngularJS 服务的 URL 编码变量


有没有办法让数据的 json > url 编码自动发生或为每个 POST 或 PUT 方法指定发生这种情况?
+1 @mjibson,对我来说,即使传递标题也行不通,直到我看到您的答案包含以下内容:var xsrf = $.param({fkey: "key"}); 那太愚蠢了,为什么 Angular 不能在内部进行?
为了更接近 $.ajax 默认行为,还应在内容类型标头中指定字符集 - headers: {Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'}
不要使用 jQuery 的 param 函数,只需在 $http 请求上设置 params 属性,只要 Content-Type 标头是 'application/x-www-form-urlencoded',它就会执行 jQuery.param 方法所做的事情 - { 1}
@spig 是的,它会做 jQuery.param 所做的事情,但是,如果您使用 params 属性,您的属性将被编码为请求 URL 的一部分,而不是在正文中 - 即使您指定了 application/x-www- form-urlencoded 标头。
C
Community

如果你不想在解决方案中使用 jQuery,你可以试试这个。从这里找到的解决方案 https://stackoverflow.com/a/1714899/1784301

$http({
    method: 'POST',
    url: url,
    headers: {'Content-Type': 'application/x-www-form-urlencoded'},
    transformRequest: function(obj) {
        var str = [];
        for(var p in obj)
        str.push(encodeURIComponent(p) + "=" + encodeURIComponent(obj[p]));
        return str.join("&");
    },
    data: xsrf
}).success(function () {});

这种方法在 angular 1.2.x 中适用于我,我认为这是最好的答案,因为它很优雅,它适用于核心 angular,并且不依赖于任何外部库,如 jQuery。
在 $resource 操作中使用此方法时遇到了一个问题。表单数据还包括 $get、$save 等函数。解决方案是稍微改变 for 语句以使用 angular.forEach
请注意,与 $.param() 相比,此方法不适用于数组/对象递归。
我会检查 obj[p] 是否不是 nullundefined。否则,您最终将发送“null”或“undefined”字符串作为值。
我不明白 transformRequest: function(obj) 由于 obj 未定义,我们是否假设通过 xsrf?喜欢transformRequest: function(xsrf)
k
kzar

我采用了其他一些答案并做了一些更简洁的事情,将此 .config() 调用放在 app.js 中 angular.module 的末尾:

.config(['$httpProvider', function ($httpProvider) {
  // Intercept POST requests, convert to standard form encoding
  $httpProvider.defaults.headers.post["Content-Type"] = "application/x-www-form-urlencoded";
  $httpProvider.defaults.transformRequest.unshift(function (data, headersGetter) {
    var key, result = [];

    if (typeof data === "string")
      return data;

    for (key in data) {
      if (data.hasOwnProperty(key))
        result.push(encodeURIComponent(key) + "=" + encodeURIComponent(data[key]));
    }
    return result.join("&");
  });
}]);

像魅力一样工作 - 即使附加到资源定义。
还注意使用 unshift(),以便其他转换保持不受干扰。干得好。
完美的!对我来说很好!悲伤的角度本身并不支持这一点。
这个答案应该是正确的,其他的都是错误的,谢谢大佬!!
递归编码怎么样?
M
Mitja

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

它可以这样使用:

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

请记住,对于正确的表单帖子,必须更改 Content-Type 标题。要对所有 POST 请求全局执行此操作,可以使用此代码(取自 Albireo 的半答案):

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

要仅为当前帖子执行此操作,需要修改请求对象的 headers 属性:

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

$http(req);

我们如何在自定义 $resource factory 上做同样的事情?
注意:我将应用程序从 Angular 1.3 升级到 1.5。它更改了 transformRequest 中的标头。出于某种原因,上面的方法对我不起作用,Angular 在 URL 编码的字符串周围添加了双引号。用 transformRequest: $httpParamSerializer, data: formDataObj 解决。感谢您的解决方案。
w
wesbos

您可以全局定义行为:

$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
});

您的示例非常错误...您要修改的只是标题。数据本身仍将是 JSON 编码的,并且无法被无法读取 JSON 的旧服务器读取。
victorblog.com/2012/12/20/… - 这是一个很好的示例,您可以覆盖 $http 默认标头,并将对象转换为序列化的表单数据。
J
James Bell

作为一种解决方法,您可以简单地使接收 POST 的代码响应 application/json 数据。对于 PHP,我添加了下面的代码,允许我以表单编码或 JSON 的形式发布到它。

//handles JSON posted arguments and stuffs them into $_POST
//angular's $http makes JSON posts (not normal "form encoded")
$content_type_args = explode(';', $_SERVER['CONTENT_TYPE']); //parse content_type string
if ($content_type_args[0] == 'application/json')
  $_POST = json_decode(file_get_contents('php://input'),true);

//now continue to reference $_POST vars as usual

这是服务器端修复的一个很好的例子,因为这个问题的真正问题在于服务器端 API .. bravo
S
Serj Sagan

这些答案看起来像是疯狂的矫枉过正,有时,简单就是更好:

$http.post(loginUrl, "userName=" + encodeURIComponent(email) +
                     "&password=" + encodeURIComponent(password) +
                     "&grant_type=password"
).success(function (data) {
//...

对我来说,我仍然必须指定标题 Content-Type 并将其设置为 application/x-www-form-urlencoded
t
tmquang6805

您可以尝试以下解决方案

$http({
        method: 'POST',
        url: url-post,
        data: data-post-object-json,
        headers: {'Content-Type': 'application/x-www-form-urlencoded'},
        transformRequest: function(obj) {
            var str = [];
            for (var key in obj) {
                if (obj[key] instanceof Array) {
                    for(var idx in obj[key]){
                        var subObj = obj[key][idx];
                        for(var subKey in subObj){
                            str.push(encodeURIComponent(key) + "[" + idx + "][" + encodeURIComponent(subKey) + "]=" + encodeURIComponent(subObj[subKey]));
                        }
                    }
                }
                else {
                    str.push(encodeURIComponent(key) + "=" + encodeURIComponent(obj[key]));
                }
            }
            return str.join("&");
        }
    }).success(function(response) {
          /* Do something */
        });

O
Ozgur

为 post 创建一个适配器服务:

services.service('Http', function ($http) {

    var self = this

    this.post = function (url, data) {
        return $http({
            method: 'POST',
            url: url,
            data: $.param(data),
            headers: {'Content-Type': 'application/x-www-form-urlencoded'}
        })
    }

}) 

在您的控制器或其他任何东西中使用它:

ctrls.controller('PersonCtrl', function (Http /* our service */) {
    var self = this
    self.user = {name: "Ozgur", eMail: null}

    self.register = function () {
        Http.post('/user/register', self.user).then(function (r) {
            //response
            console.log(r)
        })
    }

})

$.param 仅在 jquery abi 中。 jsfiddle.net/4n9fao9q/27 $httpParamSerializer 等效于 Angularjs。
r
robinmitra

有一个非常好的教程,涵盖了这个和其他相关的东西 - Submitting AJAX Forms: The AngularJS Way

基本上,您需要设置 POST 请求的标头以指示您将表单数据作为 URL 编码字符串发送,并将要发送的数据设置为相同的格式

$http({
  method  : 'POST',
  url     : 'url',
  data    : $.param(xsrf),  // pass in data as strings
  headers : { 'Content-Type': 'application/x-www-form-urlencoded' }  // set the headers so angular passing info as form data (not request payload)
});

请注意,这里使用 jQuery 的 param() 辅助函数将数据序列化为字符串,但如果不使用 jQuery,您也可以手动执行此操作。


版主只是删除了我之前的答案,因为我没有提供链接中提到的实际实现的详细信息。如果他们先要求我提供更多详细信息而不是删除它会更好,因为我已经在编辑我的答案以提供此答案中看到的详细信息!
$.param 施展魔法。谁拥有基于 jQuery+AngularJS 的应用程序的完美解决方案。
a
aul
var fd = new FormData();
    fd.append('file', file);
    $http.post(uploadUrl, fd, {
        transformRequest: angular.identity,
        headers: {'Content-Type': undefined}
    })
    .success(function(){
    })
    .error(function(){
    });

请结账! https://uncorkedstudios.com/blog/multipartformdata-file-upload-with-angularjs


它是最新的。
c
carmel

对于 Symfony2 用户:

如果您不想更改 javascript 中的任何内容以使其正常工作,您可以在 symfony 应用程序中进行以下修改:

创建一个扩展 Symfony\Component\HttpFoundation\Request 类的类:

<?php

namespace Acme\Test\MyRequest;

use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\ParameterBag;

class MyRequest extends Request{


/**
* Override and extend the createFromGlobals function.
* 
* 
*
* @return Request A new request
*
* @api
*/
public static function createFromGlobals()
{
  // Get what we would get from the parent
  $request = parent::createFromGlobals();

  // Add the handling for 'application/json' content type.
  if(0 === strpos($request->headers->get('CONTENT_TYPE'), 'application/json')){

    // The json is in the content
    $cont = $request->getContent();

    $json = json_decode($cont);

    // ParameterBag must be an Array.
    if(is_object($json)) {
      $json = (array) $json;
  }
  $request->request = new ParameterBag($json);

}

return $request;

}

}

现在在 app_dev.php 中使用你的类(或你使用的任何索引文件)

// web/app_dev.php

$kernel = new AppKernel('dev', true);
// $kernel->loadClassCache();
$request = ForumBundleRequest::createFromGlobals();

// use your class instead
// $request = Request::createFromGlobals();
$response = $kernel->handle($request);
$response->send();
$kernel->terminate($request, $response);

这对我来说真的很有用,新的 createFromGlobals 现在工作得很好。我不知道你为什么投了反对票,但我删除了它。
M
Merlin Ran

仅仅设置 Content-Type 是不够的,发送前对表单数据进行 url 编码。 $http.post(url, jQuery.param(data))


A
Aditya M P

我目前在 AngularJS google 组中使用以下解决方案我 found

$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 不会自动加载它。


M
Michael

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 的原生方式来执行此类请求。


S
Sebastián Rojas

完整答案(从角度 1.4 开始)。您需要包括 de 依赖 $httpParamSerializer

var res = $resource(serverUrl + 'Token', { }, {
                save: { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' } }
            });

            res.save({ }, $httpParamSerializer({ param1: 'sdsd', param2: 'sdsd' }), function (response) {

            }, function (error) { 

            });

v
vivex

在您的应用配置中 -

$httpProvider.defaults.transformRequest = function (data) {
        if (data === undefined)
            return data;
        var clonedData = $.extend(true, {}, data);
        for (var property in clonedData)
            if (property.substr(0, 1) == '$')
                delete clonedData[property];

        return $.param(clonedData);
    };

根据您的资源请求 -

 headers: {
                'Content-Type': 'application/x-www-form-urlencoded'
            }

g
gerrytan

这不是一个直接的答案,而是一个稍微不同的设计方向:

不要将数据作为表单发布,而是作为 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 框架库都支持这一点。 For example in Java Spring, you can pass it using X-CSRF-TOKEN header

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

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

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

或这个:

shopLocation=downtwon&daysOpen=Monday,Tuesday,Wednesday

两个设计都很差。。


S
Shubham

这就是我正在做的事情,我需要将登录数据作为表单数据发送到 API,并且 Javascript 对象(userData)会自动转换为 URL 编码数据

        var deferred = $q.defer();
        $http({
            method: 'POST',
            url: apiserver + '/authenticate',
            headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
            transformRequest: function (obj) {
                var str = [];
                for (var p in obj)
                    str.push(encodeURIComponent(p) + "=" + encodeURIComponent(obj[p]));
                return str.join("&");
            },
            data: userData
        }).success(function (response) {
            //logics
            deferred.resolve(response);
        }).error(function (err, status) {
           deferred.reject(err);
        });

这就是我的用户数据

var userData = {
                grant_type: 'password',
                username: loginData.userName,
                password: loginData.password
            }

R
Rafal Zajac

您唯一需要更改的是在创建 $http 对象时使用属性“params”而不是“data”:

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

在上面的示例中,clients[i] 只是 JSON 对象(未以任何方式序列化)。如果您使用“参数”而不是“数据”,角度将使用 $httpParamSerializer 为您序列化对象:https://docs.angularjs.org/api/ng/service/$httpParamSerializer


通过使用参数而不是数据,Angular 将数据放在 URL 参数而不是请求正文中。这不是表单帖子所期望的。
S
Shivang Gupta

使用 AngularJS $http 服务并使用它的 post 方法或配置 $http 函数。