首页 文章

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

提问于
浏览
507

在下面的代码中,AngularJS $http 方法调用URL,并将xsrf对象作为"Request Payload"提交(如Chrome调试器网络选项卡中所述) . jQuery $.ajax 方法执行相同的调用,但将xsrf提交为"Form Data" .

如何让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() {}
});

22 回答

  • 3

    我目前正在AngularJS谷歌小组中使用以下解决方案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;
    });
    

    请注意,如果您需要使用类似Symfony 2 HTTP组件的 Request::createFromGlobals() 来读取此内容,因为$ _POST将不会自动加载它 .

  • 91

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

  • 192

    我拿了一些其他的答案并且做了一些更清洁的东西,把这个 .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("&");
      });
    }]);
    
  • 82

    对于Symfony2用户:

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

    创建一个扩展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);
    
  • 3

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

    Do not post the data as a form, but as a JSON object to be directly mapped to server-side object, or use REST style path variable

    现在我知道,由于您尝试传递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
    

    两者都是糟糕的设计..

  • 24

    有一个非常好的教程可以解释这个和其他相关的东西 - 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,也可以手动执行此操作 .

  • 4

    为帖子创建适配器服务:

    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)
            })
        }
    
    })
    
  • 57

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

    //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
    
  • 8

    使用AngularJS $http 服务并使用其 post 方法或配置 $http 函数 .

  • 6

    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 param,并且您可以使用AngularJS以其本机方式执行此类请求 .

  • 0

    需要将以下行添加到传递的$ 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添加的新服务,请参阅

  • 7

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

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

    在您的应用配置中 -

    $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'
                }
    
  • 0

    完整答案(自角度1.4) . 你需要包含de dependency $ 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) { 
    
                });
    
  • -3

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

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

    在上面的示例中,clients [i]只是JSON对象(不以任何方式序列化) . 如果您使用"params"而不是"data" angular将使用$ httpParamSerializer为您序列化对象:https://docs.angularjs.org/api/ng/service/ $ httpParamSerializer

  • 20

    如果你不想在解决方案中使用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 () {});
    
  • 1
    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

  • 9

    从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 Headers . 要为所有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);
    
  • -1

    您可以全局定义行为:

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

    围绕这个问题的持续混乱激发了我写一篇关于它的博客文章 . 我在这篇文章中提出的解决方案优于您当前的最高评级解决方案,因为它不会限制您为$ http服务调用参数化数据对象;即使用我的解决方案,您可以继续将实际数据对象传递到$ http.post()等,并仍然可以实现所需的结果 .

    此外,最受欢迎的答案依赖于在$ .param()函数的页面中包含完整的jQuery,而我的解决方案是jQuery不可知,纯粹的AngularJS就绪 .

    http://victorblog.com/2012/12/20/make-angularjs-http-service-behave-like-jquery-ajax/

    希望这可以帮助 .

  • 2

    您可以尝试以下解决方案

    $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 */
            });
    
  • 595

    这就是我正在为我的需要而做的事情,我需要将登录数据作为表单数据发送到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);
            });
    

    这是我的Userdata的方式

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

相关问题