AngularJS - Есть ли способ для $ http.post отправлять параметры запроса вместо JSON?

116

У меня есть старый код, который делает запрос AJAX POST через метод jQuery post и выглядит примерно так:

$.post("/foo/bar", requestData,
    function(responseData)
    {
        //do stuff with response
    }

requestData это просто объект javascript с некоторыми основными строковыми свойствами.

Я сейчас перехожу на использование Angular и хочу заменить этот вызов на $ http.post. Я придумал следующее:

$http.post("/foo/bar", requestData).success(
    function(responseData) {
        //do stuff with response
    }
});

Когда я сделал это, я получил от сервера ответ об ошибке 500. Используя Firebug, я обнаружил, что это отправляет тело запроса следующим образом:

{"param1":"value1","param2":"value2","param3":"value3"}

Успешный jQuery $.postотправляет тело следующим образом:

param1=value1&param2=value2&param3=value3

Конечная точка, на которую я обращаюсь, ожидает параметры запроса, а не JSON. Итак, мой вопрос: есть ли способ $http.postотправить объект javascript в качестве параметров запроса вместо JSON? Да, я знаю, что могу построить строку самостоятельно из объекта, но я хочу знать, предоставляет ли Angular что-нибудь для этого из коробки.

dnc253
источник

Ответы:

140

Я думаю , что paramsпараметр конфигурации не будет работать здесь , так как он добавляет строку в URL вместо тела , но добавить к тому , что Infeligo предложил здесь является примером глобального переопределения по умолчанию преобразования ( с помощью JQuery параметров в качестве примера для преобразования данные в строку параметров).

Настройте глобальную функцию transformRequest:

var app = angular.module('myApp');

app.config(function ($httpProvider) {
    $httpProvider.defaults.transformRequest = function(data){
        if (data === undefined) {
            return data;
        }
        return $.param(data);
    }
});

Таким образом, все вызовы $ http.post автоматически преобразуют тело в тот же формат параметров, который используется при $.postвызове jQuery .

Обратите внимание, что вы также можете установить заголовок Content-Type для каждого вызова или глобально следующим образом:

$httpProvider.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded; charset=UTF-8';

Пример неглобального запроса на преобразование для каждого вызова:

    var transform = function(data){
        return $.param(data);
    }

    $http.post("/foo/bar", requestData, {
        headers: { 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'},
        transformRequest: transform
    }).success(function(responseData) {
        //do stuff with response
    });
Gloopy
источник
Мне было интересно, есть ли что-то еще, кроме функции transformRequest, но похоже, что ее нет. Спасибо за информацию о функции jQuery param.
dnc253
Неглобальный метод для каждого вызова у меня работает хорошо, но при попытке настроить глобально через $httpProvider.defaults, он не работает, есть ли подсказка об этом?
Dfr
1
WRT настраивает его глобально, у меня тоже возникают проблемы. Когда я пытаюсь сделать это с помощью приведенного здесь фрагмента, я получаю сообщение об ошибке. Cannot read property "jquery" of undefined.Как это исправить? PS. Преобразования по вызову работают.
kshep92
@ kshep92 Что происходит, так это то, что функция transformRequest вызывается по запросу без данных, поэтому «данные» не определены. Я добавил охрану перед «return $ .param (data);». Вставьте это как первую строку в функцию transformRequest: 'if (data === undefined) return data;' См. Правку, которую я внес в ответ.
Jere.Jones 05
1
начиная с Angular 1.4, вы можете использовать $ httpParamSerializer вместо jQuery docs.angularjs.org/api/ng/service/$httpParamSerializer
theRemix
21

Если вы используете Angular> = 1.4 , вот самое чистое решение, которое я нашел, которое не полагается ни на что настраиваемое или внешнее:

angular.module('yourModule')
  .config(function ($httpProvider, $httpParamSerializerJQLikeProvider){
    $httpProvider.defaults.transformRequest.unshift($httpParamSerializerJQLikeProvider.$get());
    $httpProvider.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded; charset=utf-8';
});

И тогда вы можете сделать это в любом месте вашего приложения:

$http({
  method: 'POST',
  url: '/requesturl',
  data: {
    param1: 'value1',
    param2: 'value2'
  }
});

И он будет правильно сериализовать данные как param1=value1&param2=value2и отправить их /requesturlс application/x-www-form-urlencoded; charset=utf-8заголовком Content-Type, как обычно ожидается с запросами POST на конечных точках.

Саеб Амини
источник
17

Из документации AngularJS:

params - {Object.} - Карта строк или объектов, которые будут преобразованы в? key1 = value1 & key2 = value2 после url. Если значение не является строкой , оно будет JSONified.

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

Чтобы переопределить это преобразование локально, укажите функции преобразования как свойства transformRequest и / или transformResponse объекта конфигурации. Чтобы глобально переопределить преобразования по умолчанию, переопределите свойства $ httpProvider.defaults.transformRequest и $ httpProvider.defaults.transformResponse $ httpProvider.

Обратитесь к документации для получения более подробной информации.

Infeligo
источник
Я видел параметры в документации, и, как упоминает Глупи, они мне нужны в теле, а не в URL. Мне было интересно, есть ли какой-то вариант или что-то, чего мне не хватало, чтобы задать параметры вместо JSON, но похоже, что мне просто нужно использовать свойство transformRequest.
dnc253
15

Используйте $.paramфункцию jQuery для сериализации данных JSON в requestData.

Короче говоря, используя код, похожий на ваш:

$http.post("/foo/bar",
$.param(requestData),
{
    headers:
    {
        'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'
    }
}
).success(
    function(responseData) {
        //do stuff with response
    }
});

Чтобы использовать это, вы должны включить jQuery на свою страницу вместе с AngularJS.

Сагар Бхосале
источник
7

Обратите внимание, что начиная с Angular 1.4 вы можете сериализовать данные формы без использования jQuery.

В app.js:

module.run(function($http, $httpParamSerializerJQLike) {
  $http.defaults.transformRequest.unshift($httpParamSerializerJQLike);
});

Затем в вашем контроллере:

$http({
    method: 'POST',
    url: myUrl',
    headers: {'Content-Type': 'application/x-www-form-urlencoded'},
    data: myData
});
Томас Грациани
источник
Это отличный ответ. Он решает 2 основные проблемы с Post от Angular. Заголовок должен быть установлен правильно, и вы должны сериализовать данные json. Если вам не нужна поддержка IE8, используйте 1.4+ или новее.
mbokil
Я только что реализовал это, и он решает проблемы, которые у меня были с публикацией, но это также меняет работу патча и, похоже, нарушает все мои способы использования $ http.patch ().
Майк Фельтман
5

Это может быть чем-то вроде взлома, но я избежал этой проблемы и преобразовал json в массив PHP POST на стороне сервера:

$_POST = json_decode(file_get_contents('php://input'), true);
TimoSolo
источник
Я использовал этот метод, но ненавижу его; и мне потребовалось много времени, чтобы понять, почему я должен был это использовать.
meconroy
как я уже сказал - это кажется хакерским. Как и большинство php;)
TimoSolo
5

У меня также есть проблемы с настройкой пользовательской HTTP-аутентификации, потому что $ resource кеширует запрос.

Чтобы заставить его работать, вам нужно перезаписать существующие заголовки, выполнив это

var transformRequest = function(data, headersGetter){
  var headers = headersGetter();
  headers['Authorization'] = 'WSSE profile="UsernameToken"';
  headers['X-WSSE'] = 'UsernameToken ' + nonce
  headers['Content-Type'] = 'application/json';
};

return $resource(
  url,
    {
    },
    {
      query: {
        method: 'POST',
        url: apiURL + '/profile',
        transformRequest: transformRequest,
        params: {userId: '@userId'}
      },
    }
);

Надеюсь, я смог кому-то помочь. Мне потребовалось 3 дня, чтобы понять это.

Фрэнк Марсело
источник
Думаю, вы сэкономили мне 3 дня работы. Спасибо!!! Я все еще пытаюсь выяснить, могу ли я каким-то образом перехватить вызов запроса, чтобы вставлять собственный заголовок для каждого вызова.
marosisu 06
4

Измените заголовки по умолчанию:

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

Затем используйте $.paramметод JQuery :

var payload = $.param({key: value});
$http.post(targetURL, payload);
Zags
источник
3
   .controller('pieChartController', ['$scope', '$http', '$httpParamSerializerJQLike', function($scope, $http, $httpParamSerializerJQLike) {
        var data = {
                TimeStamp : "2016-04-25 12:50:00"
        };
        $http({
            method: 'POST',
            url: 'serverutilizationreport',
            headers: {'Content-Type': 'application/x-www-form-urlencoded'},
            data: $httpParamSerializerJQLike(data),
        }).success(function () {});
    }
  ]);
Рохит Лутра
источник
На мой
взгляд, это
2

Быстрая настройка - для тех из вас, у кого проблемы с глобальной конфигурацией функции transformRequest, вот фрагмент, который я использую, чтобы избавиться от Cannot read property 'jquery' of undefinedошибки:

$httpProvider.defaults.transformRequest = function(data) {
        return data != undefined ? $.param(data) : null;
    }
kshep92
источник
0

Я много раз обнаруживал проблемное поведение этого целого. Я использовал его из express (без типизации) и bodyParser (с типами dt ~ body-parser).

Я не пытался загрузить файл, а просто интерпретировал JSON, указанный в строке сообщения.

Это request.bodyбыл просто пустой json ( {}).

После долгих исследований это сработало для меня:

import { json } from 'body-parser';
...
app.use(json()); <-- should be defined before the first POST handler!

Также может быть важно application/jsonуказать тип содержимого в строке запроса со стороны клиента.

Питер - Восстановить Монику
источник
Прошу прощения за ответ в стиле «пожертвуйте черной курицей», что, к сожалению, является обычным явлением на текущем этапе среды машинописного текста / узла / angular.
Питер - Восстановить Монику
0

Синтаксис для AngularJS v1.4.8 + (v1.5.0)

       $http.post(url, data, config)
            .then(
                    function (response) {
                        // success callback
                    },
                    function (response) {
                        // failure callback
                    }
            );

Например:

    var url = "http://example.com";

    var data = {
        "param1": "value1",
        "param2": "value2",
        "param3": "value3"
    };

    var config = {
        headers: {
            'Content-Type': "application/json"
        }
    };

    $http.post(url, data, config)
            .then(
                    function (response) {
                        // success callback
                    },
                    function (response) {
                        // failure callback
                    }
            );
Пранав VR
источник