Как я могу публиковать данные в виде данных формы вместо полезной нагрузки запроса?

в коде ниже, 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() {}
});

22 ответов


следующая строка должна быть добавлена к переданному объекту $ 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'}
})

From:https://groups.google.com/forum#!msg / угловой/5nAedJ1LyO0/4Vj_72EZcDsJ

обновление

использовать новые сервисы, добавленные с AngularJS V1.4, см


Если вы не хотите использовать 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 () {});

продолжающаяся путаница вокруг этой проблемы вдохновила меня написать об этом в блоге. Решение, которое я предлагаю в этом посте, лучше, чем ваше текущее топовое решение, потому что оно не ограничивает вас параметризацией объекта данных для вызовов службы $http; т. е. с моим решением вы можете просто продолжать передавать фактические объекты данных в $http.пост () и т. д. и все-таки добиться желаемого результата.

кроме того, лучший ответ зависит от включения полного jQuery в страница для $.функция param (), тогда как мое решение jQuery agnostic, чистый AngularJS готов.

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

надеюсь, что это помогает.


Я взял несколько других ответов и сделал что-то немного чище, положил это .config() звонок на угловой.модуль в вашем приложении.js:

.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("&");
  });
}]);

по состоянию на 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 */});

помните, что для правильной формы сообщения,Content-Type заголовок должен быть изменен. Чтобы сделать это глобально для всех запросов POST, этот код (взятый из полуответа Albireo) можно использовать:

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

сделать это только для текущая должность,headers свойство request-object необходимо изменить:

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

$http(req);

Вы можете определить поведение глобально:

$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. Для 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

эти ответы выглядят как безумный перебор, иногда, просто лучше:

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

вы можете попробовать с помощью решения ниже

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

создайте службу адаптера для 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)
        })
    }

})

есть действительно хороший учебник, который идет над этим и другими связанными вещами -Отправка форм AJAX: путь AngularJS.

в принципе, вам нужно установить заголовок запроса 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)
});

обратите внимание, что вспомогательная функция param() jQuery используется здесь для сериализации данных в строку, но вы также можете сделать это вручную, если не с помощью jQuery.


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


для пользователей Symfony2:

Если вы не хотите ничего менять в своем javascript, чтобы это работало, вы можете сделать эти изменения в приложении symfony:

создайте класс, который расширяет Symfony\Component\HttpFoundation\Request class:

<?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);

просто установить тип контента недостаточно, url-адрес кодирует данные формы перед отправкой. $http.post(url, jQuery.param(data))


в настоящее время я использую следующее решение I нашел в группе AngularJS google.

$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 component's Request::createFromGlobals() чтобы прочитать это, так как $_POST не будет автоматически загружен с ним.


AngularJS делает это правильно, как это делает следующий тип контента внутри заголовка http-запроса:

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 свой собственный способ выполнения таких запросов.


полный ответ (начиная с углового 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) { 

            });

в конфигурации приложения -

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

это не прямой ответ, а скорее немного другое направление дизайна:

не публикуйте данные как форму, а как объект JSON для непосредственного сопоставления с объектом на стороне сервера или используйте переменную пути стиля REST

теперь я знаю, что ни один из вариантов не может быть подходящим в вашем случае, так как вы пытаетесь передать ключ XSRF. Отображение его в переменную пути, как это ужасный дизайн:

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

потому что по природе вы хотели бы пройти XSRF ключ к другому пути тоже,/login, /book-appointment etc. и вы не хотите испортить свой красивый URL

интересно добавить его в качестве поля объекта также не подходит, потому что теперь на каждом из объектов json, которые вы передаете серверу, вы должны добавить поле

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

вы, конечно, не хотите добавлять другое поле в свой серверный класс, который не имеет прямой семантической связи с объектом домена.

на мой взгляд, лучший способ передать ваш xsrf ключ через HTTP-заголовок. Многие библиотеки XSRF protection server-side web framework поддерживают это. например, в Java Spring вы можете передать его с помощью X-CSRF-TOKEN заголовок.

отличная возможность Angular привязать объект JS к объекту UI означает, что мы можем избавиться от практики публикации формы все вместе и опубликовать JSON вместо этого. JSON можно легко де-сериализовать в серверный объект и поддерживать сложные структуры данных, такие как карта, массивы, вложенные объекты, так далее.

как вы размещаете массив в полезной нагрузке формы? Может быть, так:

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

или такой:

shopLocation=downtwon&daysOpen=Monday,Tuesday,Wednesday

оба плохая конструкция..


это то, что я делаю для моей потребности, где мне нужно отправить данные для входа в 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
            }

единственный тонкий, который вам нужно изменить, - это использовать свойство "params", а не" data " при создании объекта $http:

$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


Использовать AngularJS $http сервис и использовать его post метод или настроить .