Angular $http прикачва данни като обект

Опитвам се да получа списък с песни от API на soundcloud с 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'); });
    });

Тези параметри изобщо не се разпознават, когато проверявам заявката в програмата за отстраняване на грешки на браузъра.

Опитах се да използвам „params“ вместо „data“, но по този начин той превръща обекта „duration“ в json --> след това получавам статус 500 в отговор.
Когато изпращам само client_id в параметри, работи добре, защото няма обект, а само низ.

Ajax методът на jQuery работи добре: 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 заявка прихващач за автоматично прилагане на това към всички заявки).

  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...), с 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 изглежда счупен atm): Заявката наистина е 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 и както е описано, ще направи следното (както видяхте):

Ако свойството data на конфигурационния обект на заявката съдържа обект, сериализирайте го във формат 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", вместо да изискват 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
Това всъщност не отговаря на въпроса. OP вече опита това и не се получи. - person gkalpak; 16.04.2015