2

I'm trying to get list of tracks from soundcloud API with angularjs.
The parameters i'm trying to send are:
1) client_id (string)
2) duration (object with two properties).

Here's the code:

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

These parameters aren't recognized at all when I check the request in the broweser's debugger.

I tried to use 'params' instead of 'data', but this way it turns the 'duration' object to json --> then I get status 500 in response.
When I only send the client_id in params, it works fine because there's no object, only string.

jQuery's ajax method works fine: https://jsfiddle.net/oacwz1up/3/

What should I do ? How can I send the parameters normally ?
Help please! Thanks!

4 Answers 4

7

This happens because Angular serializes the request parameters differently than JQuery.

Angular 1.4 will address this problem introducing a paramSerializer property on the $http config object (and on $httpProvider.defaults). This can be used for arbitrarily serializing the requests parameters (for all or a particular request).

Note, that the feature is available only since v1.4.0-rc.0.


Besides the default serializer (which converts objects to JSON strings), there is an additional built-in serializer that emulates JQuery's param(): $httpParamSerializerJQLike.

You can use it like this:

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

See, also, this short demo.


If you are using an older version of Angular, you could do one of the following:

  1. Construct the whole URL (including the query string) yourself (possibly using an $http request interceptor to automatically apply this to all requests).

  2. Keep the params in a flat format that will result in Angular's serializing them as expected:

    var REQ_PARAMS = {
      client_id: 'a81f01ef5d0036415431e8be76c8db0e',
      'duration[from]': 200000,
      'duration[to]': 205000
    };
    
Sign up to request clarification or add additional context in comments.

8 Comments

Thanks for the solution, I noticed that with the $httpParamSerializerJQLike method for angularjs 1.4, the params is sent using GET, even the config.method is set as 'post', you know why?
@ken: I can't reproduce that. Do you have a code sample (preferably a live reproduction in CodePen or similar) ? $httpParamSerializerJQLike shouldn't have anything to do with the method.
you can use $http(config).then(on...), with config = { method: 'post', url: API_URL, params: REQ_PARAMS, paramSerializer: '$httpParamSerializerJQLike' }; Then you will see it's appending the query to the end of URL. From Chrome, we can see Request URL:http...server.php?client_id=a81f01ef5d0036415431e8be76c8db0e&duration%5Bfrom%5D=200000&duration%5Bto%5D=205000 Request Method:POST. I edited the code pen with your demo, but I am tested with local php server. You can take a look though codepen.io/kenngsimply/pen/zGZvQL
I have tried to debug a little on angular.js, I think it can be traced to the line buildUrl(config.url, config.paramSerializer(config.params)); which is in the function sendReq().
I think I see what you mean (although the CodePen seems broken atm): The request is indeed a POST request (not a GET request), but the request parameters are appended in the URL. This is not Angular-specific: query params are allowed to be sent in POST requests as well. (You might be confused by the fact that in PHP you can access them using $_GET.) Unfortunately, param serialization is only applied to that (query parameter serialization); it does not apply to the serialization of the request body (i.e. POST data), but it's something that might be implemented in the future.
|
1

If you look at $http documentation, on request it applies a default transformation which is $http.defaults.transformRequest and as described will do the following (as you saw) :

If the data property of the request configuration object contains an object, serialize it into JSON format.

What you need to do is override this function by specifying your own transformRequest object.

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

You need to find a way to get the same syntax as jQuery provide which is :

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

Use a condition is is an object and generate manually your String. Should not be difficult.

1 Comment

This won't work. Transformations apply to data, not params. The transformRequest/transformResponse functions do not even have access to the URL or the params, let alone being able to transform them.
0

that's a strange API - I don't know why they don't do something like "duration_from" rather than requiring duration[from] - as suggested you could certainly transform the request, but if this is just a one off you could also try simply hard-coding it using url escaped values for [ and ]:

 var dataToSend = {
     client_id: 'a81f01ef5d0036415431e8be76c8db0e',
     'duration%5Bfrom%5D': 200000,
     'duration%5Bto%5D': 205000
 }; 

3 Comments

yeah I prefer your approach, but I figured this might be useful for someone who's testing the soundcloud API from a jsfiddle or whatever
You don't even need to escape the [ and ]; the browser will do it for you. Fortunately, Angular 1.4 introduces a more elegant way to handle this :)
true, but I don't always trust that browsers will behave consistently with url encoding
0
$http.get('http://api.soundcloud.com/tracks.json', {params:dataToSend});

The params property is way to transform query in restful way. Just transform data in dataToSend object, and it will work.

This is a URL that should be created:

https://api.soundcloud.com/tracks.json?client_id=a81f01ef5d0036415431e8be76c8db0e&duration%5Bfrom%5D=200000&duration%5Bto%5D=205000

1 Comment

This does not really answer the question. OP has already tried that and it didn't work.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.