Angular $http прикрепить данные как объект

Я пытаюсь получить список треков из Soundcloud API с помощью angularjs.
Параметры, которые я пытаюсь отправить:
1) client_id (строка)
2) продолжительность (объект с двумя характеристики).

Вот код:

    var CLIENT_ID = 'a81f01ef5d0036415431e8be76c8db0e';
    var TRACKS_URL = 'https://api.soundcloud.com/tracks.json';

    var app = angular.module('soundcloud', []);

    app.controller('tracksController', function ($scope, $http) {

        $http({
            url: 'https://api.soundcloud.com/tracks.json',
            method: 'GET',
            data: {
                client_id: CLIENT_ID,
                duration: { // in milliseconds
                    from: 300000,
                    to: 400000
                }
            }
        })
            .success(function (data) {
                $scope.trackList = data;
            })
            .error(function () { alert('error'); });
    });

Эти параметры вообще не распознаются, когда я проверяю запрос в отладчике браузера.

Я пытался использовать «параметры» вместо «данные», но таким образом он превращает объект «длительность» в json ->, тогда я получаю в ответ статус 500.
Когда я отправляю только client_id в параметрах, он работает нормально, потому что нет объекта, только строка.

Метод jQuery ajax работает нормально: https://jsfiddle.net/oacwz1up/3/

Что я должен делать ? Как я могу отправить параметры в обычном режиме?
Помогите пожалуйста! Спасибо!


person user3125999    schedule 16.04.2015    source источник


Ответы (4)


Это происходит потому, что Angular сериализует параметры запроса не так, как JQuery.

Angular 1.4 решит эту проблему, добавив свойство paramSerializer в $http config. объект (и на $httpProvider.defaults ). Это можно использовать для произвольной сериализации параметров запросов (для всех или для конкретного запроса).

Обратите внимание, что эта функция доступна только с v1.4.0-rc.0.


Помимо сериализатора по умолчанию (который преобразует объекты в строки JSON), есть дополнительный встроенный сериализатор, который эмулирует param() JQuery: https://code.angularjs.org/1.4.0-rc.0/docs/api/ng/service/$httpParamSerializerJQLike.

Вы можете использовать его следующим образом:

$http.get(<API_URL>, {
  params: {...},
  paramSerializer: '$httpParamSerializerJQLike'
});

См. также эту короткую демонстрацию.


Если вы используете более старую версию Angular, вы можете сделать одно из следующих действий:

  1. Создайте весь URL-адрес (включая строку запроса) самостоятельно (возможно, используя $http запрос interceptor для автоматического применения ко всем запросам).

  2. Сохраняйте параметры в плоском формате, чтобы Angular сериализовал их так, как ожидалось:

    var REQ_PARAMS = {
      client_id: 'a81f01ef5d0036415431e8be76c8db0e',
      'duration[from]': 200000,
      'duration[to]': 205000
    };
    
person gkalpak    schedule 16.04.2015
comment
Спасибо за решение, я заметил, что с методом $httpParamSerializerJQLike для angularjs 1.4 параметры отправляются с использованием GET, даже метод config.method установлен как «post», знаете почему? - person ken; 02.06.2015
comment
@ken: я не могу это воспроизвести. У вас есть образец кода (желательно живое воспроизведение в CodePen или аналогичном)? $httpParamSerializerJQLike не должно иметь ничего общего с методом. - person gkalpak; 02.06.2015
comment
вы можете использовать $http(config).then(on...), with config = { method: 'post', url: API_URL, params: REQ_PARAMS, paramSerializer: '$httpParamSerializerJQLike' }; Затем вы увидите, что он добавляет запрос в конец URL-адреса. В Chrome мы можем увидеть URL-адрес запроса: http...server.php?client_id=a81f01ef5d0036415431e8be76c8db0e&duration%5Bfrom%5D=200000&duration%5Bto%5D=205000 Метод запроса: POST. Я отредактировал ручку кода с помощью вашей демонстрации, но я тестировался на локальном сервере php. Вы можете посмотреть, хотя codepen.io/kenngsimply/pen/zGZvQL - person ken; 02.06.2015
comment
Я попытался немного отладить angular.js, я думаю, что это можно проследить до строки buildUrl(config.url, config.paramSerializer(config.params)); который находится в функции sendReq(). - person ken; 02.06.2015
comment
Я думаю, что понимаю, что вы имеете в виду (хотя CodePen кажется сломанным банкоматом): запрос действительно является запросом POST (а не запросом GET), но параметры запроса добавляются в URL-адрес. Это не зависит от Angular: параметры запроса также разрешено отправлять в POST-запросах. (Вас может смутить тот факт, что в PHP вы можете получить к ним доступ, используя $_GET.) К сожалению, сериализация параметров применяется только к этому (сериализация параметров запроса); это не относится к сериализации тела запроса (то есть данных POST), но это может быть реализовано в будущем. - person gkalpak; 02.06.2015
comment
Angular (по умолчанию) сериализует тела запросов как JSON. Если вам нужна другая сериализация, вы должны сделать это самостоятельно (и обойти запрос-преобразование Angular по умолчанию). В любом случае, чтобы опубликовать данные, вы должны указать их в свойстве data объекта конфигурации (например, $http({method: 'POST', data: ...})) или в качестве первого аргумента ярлыка post() (например, $http.post(<data>)). - person gkalpak; 02.06.2015
comment
да, мне просто интересно, является ли это ошибкой, поскольку никто не ожидает, что параметры будут сериализованы как часть запроса, когда метод указан как POST. Вместо этого jQuery отправил его с использованием данных POST. - person ken; 03.06.2015
comment
Это точно не баг. Я не знаю, как это делает jQuery, но параметры запроса в POST-запросах, безусловно, являются допустимыми (и полезными в некоторых случаях), а Angular отделяет параметры запроса от тела запроса (данные POST). - person gkalpak; 03.06.2015

Если вы посмотрите на https://docs.angularjs.org/api/ng/service/$http#!, по запросу он применяет преобразование по умолчанию, которое равно $http.defaults.transformRequest и, как описано, будет делать следующее (как вы видели):

Если свойство данных объекта конфигурации запроса содержит объект, сериализуйте его в формат JSON.

Что вам нужно сделать, так это переопределить эту функцию, указав свой собственный объект transformRequest.

function appendTransform(defaults, transform) {

  // We can't guarantee that the default transformation is an array
  defaults = angular.isArray(defaults) ? defaults : [defaults];

  // Append the new transformation to the defaults
  return defaults.concat(transform);
}

  $http({
      url: '...',
      method: 'GET',
      transformResponse: appendTransform($http.defaults.transformResponse, function(value) {
        return doTransform(value);
      })
  });

Вам нужно найти способ получить тот же синтаксис, что и jQuery, а именно:

https://api.soundcloud.com/tracks.json?client_id=a81f01ef5d0036415431e8be76c8db0e&duration[from]=200000&duration[to]=205000

Используйте условие - это объект и сгенерируйте вручную свою строку. Не должно быть сложно.

person sebastienbarbier    schedule 16.04.2015
comment
Это не сработает. Преобразования применяются к data, а не к params. Функции transformRequest/transformResponse даже не имеют доступа к URL-адресу или параметрам, не говоря уже о возможности их преобразования. - person gkalpak; 16.04.2015

это странный API - я не знаю, почему они не делают что-то вроде "duration_from", а не требуют продолжительности [от] - как было предложено, вы, безусловно, можете преобразовать запрос, но если это всего лишь один раз, вы также можете попробовать просто жестко закодируйте его, используя экранированные значения URL для [ и ]:

 var dataToSend = {
     client_id: 'a81f01ef5d0036415431e8be76c8db0e',
     'duration%5Bfrom%5D': 200000,
     'duration%5Bto%5D': 205000
 }; 
person Jordan    schedule 16.04.2015
comment
да, я предпочитаю ваш подход, но я подумал, что это может быть полезно для тех, кто тестирует API SoundCloud из jsfiddle или что-то еще - person Jordan; 16.04.2015
comment
Вам даже не нужно экранировать [ и ]; браузер сделает это за вас. К счастью, Angular 1.4 предлагает более элегантный способ справиться с этим :) - person gkalpak; 16.04.2015
comment
верно, но я не всегда верю, что браузеры будут вести себя последовательно с кодировкой URL - person Jordan; 16.04.2015

$http.get('http://api.soundcloud.com/tracks.json', {params:dataToSend});

Свойство params — это способ преобразования запроса в спокойный способ. Просто преобразуйте данные в объект dataToSend, и все заработает.

Это URL, который должен быть создан:

https://api.soundcloud.com/tracks.json?client_id=a81f01ef5d0036415431e8be76c8db0e&duration%5Bfrom%5D=200000&duration%5Bto%5D=205000
person Janko    schedule 16.04.2015
comment
Это на самом деле не отвечает на вопрос. ОП уже пробовал это, и это не сработало. - person gkalpak; 16.04.2015