Як я можу розміщувати дані як дані форми замість корисного навантаження запиту?


523

У наведеному нижче коді $httpметод AngularJS викликає 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() {}
});

1
Це було дуже корисне питання. Це дозволяє мені надсилати корисний вантаж у вигляді рядка (зміною типу Content), що не дозволяє мені мати справу з OPTIONS до POST / GET.
earthmeLon

У мене те саме питання, це після того, як я
запитаю

Відповіді:


614

Наступний рядок потрібно додати до переданого об’єкта $ 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, див


3
Чи існує спосіб кодування даних json> url автоматично або вказати це, що відбувається для кожного методу POST або PUT?
Dogoku

51
+1 @mjibson, Для мене навіть передача заголовків не спрацювала, поки я не побачив вашу відповідь, що містить це: var xsrf = $.param({fkey: "key"});Дурне, чому я не можу це зробити внутрішньо?
naikus

12
Щоб ближче дотримуватися поведінки за замовчуванням $ .ajax, шаблону слід також вказати у заголовку типу вмісту -headers: {Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'}
Imre

25
Замість того, щоб використовувати парам-функцію jQuery, просто встановіть властивість params на запит $ http, і вона зробить те, що робить метод jQuery.param, поки заголовок Content-Type є "application / x-www-form-urlencoded" - stackoverflow .com / questions / 18967307 /…
spig

13
@spig Так, це зробить те, що робить jQuery.param, але, якщо ви використовуєте властивість params, ваші властивості будуть закодовані як частина URL-адреси запиту, а не в тілі - навіть якщо ви вказали application / x-www- заголовок у формі, який містить урлен.
stian

194

Якщо ви не хочете використовувати 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 () {});

7
Цей метод працює для мене в кутовій 1.2.x, і я вважаю, що це найкраща відповідь, оскільки він елегантний, він працює в основному кутовому і не залежить від жодних зовнішніх бібліотек, таких як jQuery.
gregtczap

2
Я зіткнувся з проблемою, коли використовував цей метод всередині дії з ресурсом $. Дані форм також включає функції для $ прибуде, $ економії і т.д. Рішення було змінювати Про forсебе трохи , щоб використовувати angular.forEachзамість.
Ентоні

10
Зауважте, що на відміну від $ .param () цей метод не працює рекурсивно на масивах / об'єктах.
MazeChaZer

1
Я перевірив би, що obj[p]це не нульовий чи невизначений . Інакше в кінцевому підсумку ви будете надсилати рядок "null" або "undefined".
тамір

1
Я не розумів, transformRequest: function(obj)як obj не визначено, чи ми повинні припустити передачу xsrf? ЯкtransformRequest: function(xsrf)
Акшай Тару

92

Продовження плутанини навколо цього питання надихнуло мене написати повідомлення про це. Я пропоную рішення в цій публікації краще, ніж ваше поточне рішення з найкращим рейтингом, оскільки воно не обмежує вас параметризувати ваш об'єкт даних для викликів послуги $ http; тобто з моїм рішенням ви можете просто продовжувати передавати фактичні об’єкти даних до $ http.post () тощо, і все-таки досягти бажаного результату.

Крім того, відповідь з найвищим рейтингом покладається на включення повного jQuery на сторінку для функції $ .param (), тоді як моє рішення - агностичний jQuery, чистий AngularJS готовий.

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

Сподіваюсь, це допомагає.


10
+1 для детального блогу, але те, що в цьому є потреба, жахливо ...
iwein

4
Так, можливо, жахливо на двох рівнях: 1) що AngularJS вирішив доповнити де-факто стандартний (хоча, правда, помилковий) та 2), що PHP (і хто знає, що ще має інші серверні мови) якимось чином не автоматично визначає додаток / json вхід. : P
Єзекіїль Віктор

Чи можливо, що angularjs автоматично адаптується до типу вмісту і відповідно кодує? Це передбачено?
unludo

4
Я (як і багато інших) натрапив на це, що мій бекенд ASP.NETне по-справжньому підтримував це. Якщо ви не хочете змінювати поведінку AngularJS (чого я цього не зробив, тому що мій API повертає JSON, чому б не прийняти його також JSON, він є більш гнучким, ніж дані форми), ви можете прочитати з, Request.InputStreamа потім обробити його будь-яким способом Ви хочете. (Я вирішив десеріалізувати це dynamicдля зручності використання.)
Aidiakapi

2
Незважаючи на те, що це не є автономною відповіддю . Хороші відповіді не просто посилаються десь ще. З " Як відповісти" : "Завжди цитуйте найбільш релевантну частину важливого посилання, якщо цільовий сайт недоступний або виходить постійно в офлайн".
Джеймс

83

Я взяв кілька інших відповідей і зробив щось трохи чистіше, поклав цей .config()дзвінок на кінець вашого angular.module у вашому app.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("&");
  });
}]);

1
Працює як шарм - навіть якщо додається до визначення ресурсу.
Кай Маттерн

3
Також подбали про використання, unshift()щоб інші перетворення залишалися непорушеними. Гарна робота.
Депутат Адітія

2
ідеально! добре працював для мене! сумний кутовий це не споконвічно це підтримує.
spierala

2
Ця відповідь повинна бути правильною вгорі, інші - неправильною, дякую товаришу !!
Хосе Ігнасіо Хіта

2
Як щодо рекурсивного кодування?
Petah

58

Станом на 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властивість запиту-об’єкта потрібно змінити:

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

$http(req);

Як ми можемо зробити те ж саме на власному заводі $ ресурсів?
натюрморт

Примітка. Я оновлюю додаток з кутового 1.3 до 1.5. Це змінило заголовки в transformRequest. Чомусь метод вище не працює для мене, Angular додає подвійні лапки навколо рядка, кодованого URL-адресою. Вирішено с transformRequest: $httpParamSerializer, data: formDataObj. Дякую за рішення.
PhiLho

24

Ви можете визначити поведінку в усьому світі:

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

46
Ваш приклад так неправильний ... Все, що ви змінюєте, це заголовок. Самі дані все ще будуть кодовані JSON і не читаються на старих серверах, які не можуть читати JSON.
alexk

victorblog.com/2012/12/20/… - ось хороший приклад, коли ви переосмислюєте заголовок $ http за замовчуванням, а також конвертуєте об’єкт у серіалізовані дані форми.
Федеріко

20

Як вирішення, ви можете просто змусити код, що приймає POST, відповідати на дані програми / 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 .. браво
Вігнеш

16

Ці відповіді виглядають як божевільні надмірності, іноді, просто краще:

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

1
Для мене мені ще довелося вказати заголовок Content-Typeі встановити його application/x-www-form-urlencoded.
Віктор Рамос

9

Ви можете спробувати з рішенням нижче

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

8

Створіть послугу адаптера для публікації:

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.
Декстер

7

Існує дійсно приємний підручник, який стосується цього та інших пов’язаних речей - Надіслати форми AJAX: 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 використовується тут для серіалізації даних у рядок, але ви можете це зробити і вручну, якщо не використовуєте jQuery.


1
Модератори просто видалили мою попередню відповідь, оскільки я не надав подробиці фактично реалізованої реалізації, згаданої у посиланні. Було б краще, якби вони попросили мене спочатку надати більш детальну інформацію, а не видаляти її, оскільки я вже редагував свою відповідь, щоб надати деталі, як це було зазначено у цій відповіді!
robinmitra

$.paramЗробити магію. ідеальне рішення для того, хто має jQuery + додаток AngularJS.
dashtinejad


4

Для користувачів 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 працює зараз чудово. Я не знаю, чому ти отримав протизаконний голос, але я його зняв.
Річард

3

Просто встановити Content-Type недостатньо, URL-код кодує дані форми перед надсиланням. $http.post(url, jQuery.param(data))


3

Зараз я використовую таке рішення, яке я знайшов у групі Google AngularJS.

$ http
.post ('/ echo / json /', 'json =' + encodeURIComponent (angular.toJson (дані)), {
    заголовки: {
        'Тип вмісту': 'application / x-www-form-urlencoded; charset = UTF-8 '
    }
}). успіх (функція (дані) {
    $ range.data = дані;
});

Зверніть увагу, що якщо ви використовуєте PHP, вам потрібно буде використовувати щось на зразок Symfony 2 HTTP-компонента, Request::createFromGlobals()щоб прочитати це, оскільки $ _POST не завантажується автоматично з ним.


2

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, і ви можете використовувати AngularJS його рідний спосіб робити такі запити.


2

Повна відповідь (починаючи з кутового 1.4). Вам потрібно включити залежність $ 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) { 

            });

1

У налаштуваннях програми -

$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

Це не пряма відповідь, а дещо інший напрямок дизайну:

Не публікуйте дані як форму, а як об'єкт JSON, безпосередньо підключений до об'єкта на стороні сервера, або використовуйте змінну шляху стилю REST

Тепер я знаю, що жоден варіант не підходить у вашому випадку, оскільки ви намагаєтесь передати ключ XSRF. Зображення його в змінну контуру на зразок цієї страшної конструкції:

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

Тому що за своєю природою ви хочете передати ключ xsrf в інший шлях /loginі /book-appointmentт. Д., І ви не хочете зіпсувати свою гарну URL-адресу

Цікаво, що додавання його в поле об'єкта також не підходить, оскільки тепер для кожного з об’єктів json, який ви переходите на сервер, ви повинні додати поле

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

Ви, звичайно, не хочете додавати інше поле у ​​свій серверний клас, яке не має прямої семантичної асоціації з об’єктом домену.

На мою думку, найкращий спосіб передавати ваш ключ xsrf - через заголовок HTTP. Багато бібліотеки веб-фреймворків захисту xsrf підтримують це. Наприклад, у Java Spring, ви можете передати його за допомогою X-CSRF-TOKENзаголовка .

Відмінна здатність Angular зв’язувати JS-об'єкт з об'єктом інтерфейсу означає, що ми можемо позбутися практики розміщення форми разом і замість цього розмістити JSON. JSON можна легко десериалізувати на об’єкт на сервері та підтримувати складні структури даних, такі як карта, масиви, вкладені об'єкти тощо.

Як розміщувати масив у корисному навантаженні форми? Можливо так:

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

або це:

shopLocation=downtwon&daysOpen=Monday,Tuesday,Wednesday

Обидва мають поганий дизайн ..


0

Це те, що я роблю для моїх потреб. Де мені потрібно надіслати дані для входу в 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
            }

-1

Єдине, що вам потрібно змінити - це використовувати властивості "params", а не "data", коли ви створюєте ваш $ http об'єкт:

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

У наведеному вище прикладі клієнти [i] - це просто об'єкт JSON (не серіалізований жодним чином). Якщо ви використовуєте параметри, а не "дані", кутовий буде серіалізувати об'єкт для вас за допомогою $ httpParamSerializer: https://docs.angularjs.org/api/ng/service/ $ httpParamSerializer


2
Використовуючи параметри замість даних, Angular розміщує дані в параметрах URL замість тіла запиту. Це не те, що очікується від публікації форми.
cmenning

-3

Використовуйте $httpсервіс AngularJS і використовуйте його postметод або $httpфункцію налаштування .

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.