Angular http request with json param - javascript

In my RESTful api one of the resources exposes a GET method that accept json as a parameter named 'query'. This parameter is passed directly to the MongoDB query allowing users to query the database directly using mongo syntax.
The problem I'm having is that the request always looks like this:
?&query=%7B%22source%22:%22incident%22%7D
Where it should look something like this:
?&query={'source': 'incident'}
This is how I am sending the GET request:
var query = {};
if ($scope.sourceFilter) { query.source = $scope.sourceFilter; }
var query = JSON.stringify(query);
$http.get('/api/feedbackEntries', {params: {limit: $scope.limit, query: query}}).success(function(data) { .......
I am doing the same thing on other get requests and I don't get this issue.
Am I doing something wrong here ? Is this to do with the way angular parses params ?
Thanks

Like the $http docs say
params – {Object.<string|Object>} – Map of strings or objects which will be turned to ?key1=value1&key2=value2 after the url. If the value is not a string, it will be JSONified.
Latter emphasis is added by me.
So the query property of the object you pass to the params configuration option is an Object. This means is will be JSONified, which means the same as
JSON.stringify(query);
So this
{'source': 'incident'}
Turns to this:
'{"source": "incident"}'
As RFC 1738 states:
... only alphanumerics, the special characters "$-_.+!*'(),", and
reserved characters used for their reserved purposes may be used
unencoded within a URL.
As it happens {, } and " are not on that list and have to be url encoded to be used in a url. In your case %7B corresponds to {, %7D corresponds to } and %22 corresponds to ".
So what is happening is normal and most server software automatically decodes the url query parameters for you, so they will be presented normally. Most likely you'll need to parse it back to JSON somehow!
Hope this helps!

When using RESTFul apis concider using ngResource, ngResource docs
Include it in your module:
yourApp = angular.module('yourApp', ['ngResource'])
Add your service:
yourApp.factory('YourService', ['$resource', function($resource){
return $resource('link/to/your/object', {});
}]);
Add your controller
yourApp.controller('YourController', [$scope,
'YourService', function($scope, YourService) {
$scope.yourData = YourService.get();
$scope.yourData = YourService.query(); //(When obtaining arrays from JSON.)
I've found this is the best way using a RESTFull api.

After some digging I figured this one out. Looking at the request I was making:
$http.get('/api/feedbackEntries',
I saw that the url does not end with a trailing slash. This was the only difference I could see compared with other requests that were working fine. So I added the trailing slash and magically it works. I can't explain why, whether it's something within angular or elsewhere .. but this is how I fixed the problem.
Hope this helps someone in the future.

Below is the code I used to get search result from my server, it works for me sending POST request with JSON params without .
var service = {
getResult: function ({"username": "roman", "gender": "male"}) {
var promise = $http({
url: ServerManager.getServerUrl(),
method: "POST",
data: params,
headers: {
'Content-Type': 'application/json'
}
})
.success(function (data, status, headers, config) {
console.log('getResult success.');
return data;
}).error(function (data, status) {
console.log('getResult error.');
});
return promise;
}
}
return service;

Related

AngularJS $http GET call getting "Cannot GET /[object%20Object]" error

I am confused why this is happening. I am fairly new with AngularJS/JS so bear with me. I want to create a simple side project that will assist me in learning how to cook, so I am using a recipe database API caled Yummly that will GET recipes with ingredients and other information.
Here's the documentation: https://developer.yummly.com/documentation.
I tried putting api key and ID in the URL as the API said I can. I also tried putting it in the HTTP headers as the API also said I could. I tried both in postman and both worked, but neither work in my code. I get the error:Cannot GET /[object%20Object]. So I am lost. Perhaps this description from the "How to use GET method" from the documentation is the reason?:
Don’t forget to URL-encode parameter names and values (i.e., [ and ] become %5B and %5D). I don't understand this preceding sentence so...
Here's my code. Thanks so much. Much appreciated!
angular.module("app", [])
.controller('AppController', ['$scope', '$http', function ($scope, $http) {
$scope.greeting = 'Hola!';
$scope.getRecipe = function(){
$http.get({
url: 'http://api.yummly.com/v1/api/recipes?q=onion+soup',
method: 'GET',
headers: {
'X-Yummly-App-ID':'myID (secret)',
'X-Yummly-App-Key':'myKey (secret)'
}
}).success(function(response) {
console.log("I got the response.");
}).error(function(error) {
console.log("failed", error);
})
}
}]);
If $http.get is preferred, the code would be:
$http.get('http://api.yummly.com/v1/api/recipes', {
params: {
q: 'onion+soup'
}
headers: {
'X-Yummly-App-ID':'myID (secret)',
'X-Yummly-App-Key':'myKey (secret)'
}
}).success(function(response) {
console.log("I got the response.");
}).error(function(error) {
console.log("failed", error);
})
According to the official document, the syntax of $http.get is:
get(url, [config]);
Shortcut method to perform GET request.
The first parameter of $http.get is a URL string. If an object is passed (as in the question), it would be converted to string, that's why you get the request sent to /[object%20Object] -- [object%20Object] is the toString() result of the config object.
Don’t forget to URL-encode parameter names and values (i.e., [ and ] become %5B and %5D). I don't understand this preceding sentence so...
Don't worry. This will be handled automatically. If the value of q above is 'onion+soup[xxx]', it would be URL-encoded as 'onion%2Bsoup%5Bxxx%5D'.

How exactly means this $resource web service connection configuration in AngularJS?

I am absolutly new in AngularJS and I am studying a tutorial that show how to access to this external weather forecast API: http://openweathermap.org/forecast to retrieve and use weather information.
So, into my application, I have a controller like this:
weatherApp.controller('forecastController', ['$scope', '$resource', '$routeParams', 'cityService', function($scope, $resource, $routeParams, cityService) {
// It contain the city selected in the view:
$scope.city = cityService.city;
$scope.days = $routeParams.days || 2;
$scope.weatherAPI = $resource("http://api.openweathermap.org/data/2.5/forecast/daily",
{ APPID: 'MY_PERSONAL_KEY',
callback: "JSON_CALLBACK",
cnt: 5
},
{ get: { method: "JSONP" }});
$scope.weatherResult = $scope.weatherAPI.get({ q: $scope.city, cnt: $scope.days });
$scope.convertToFahrenheit = function(degK) {
return Math.round((1.8 * (degK - 273)) + 32);
}
$scope.convertToDate = function(dt) {
return new Date(dt * 1000);
};
}]);
And I am finding some difficulties tho understand how exactly the service is called and the informations are required.
So from what I have understand (but I am absolutly not sure about it so correct me if I do wrong assertion) I have the following situation:
1) Into the controller I inject the $resource service provided by Angular that from what I have understand is an object that I can use for a user friendly RESTful web service interaction.
So by this line:
$scope.city = cityService.city;
I put a city (inserted by the user in the view) into the city property of the $scope of this controller and it will be user as one of the parameter used for the webservice query.
Then I create the weatherAPI property on the $scope object that I think should represent the way to access to this resource
$scope.weatherAPI = $resource("http://api.openweathermap.org/data/2.5/forecast/daily",
{ APPID: 'MY_PERSONAL_KEY',
callback: "JSON_CALLBACK"
},
{ get: { method: "JSONP" }});
So I think that weatherAPI is a resource object creted by the $resource service (or am I missing something?).
On this I set the web service URL (http://api.openweathermap.org/data/2.5/forecast/daily) and a couple of JSON objects, these:
{
APPID: 'MY_PERSONAL_KEY',
callback: "JSON_CALLBACK"
},
in which I declare the APPID field that is my personal key to use the key and the callback: "JSON_CALLBACK" field. What exactly represent this callback field?
And this second object:
{ get: { method: "JSONP" }}
Ok, the roole of these 2 objects for me are pretty obscure.
What exactly define? It seems to me that are something related to the security of my application but I am absolutly not sure about it
Finnally it retrieve data from the web service by:
$scope.weatherResult = $scope.weatherAPI.get({ q: $scope.city, cnt: $scope.days });
where I thonk that I am simply passing the 2 parameter (that are something like ?q=value&?cnt=value of a classic HTTPrequest)
So I think that weatherAPI is a resource object creted by the $resource service (or am I missing something?).
No, you're not missing anything. weatherAPI is exactly what you're think it should be.
in which I declare the APPID field that is my personal key to use the key and the callback: "JSON_CALLBACK" field. What exactly represent this callback field?
callback field is just an default GET parameter passed to API on each request. It is set here, so you don't have to pass it each time requesting something from API.
What exactly define? It seems to me that are something related to the security of my application but I am absolutly not sure about it
Third parameter passed into $resource call is an definition of available methods on your API.
In outer object, key is name of method. It is just name on which that method will be available. value is an object of settings for that method, in that case it is only specifying type of your method, which is "JSONP".
JSONP is an special type of request, it can be only an "GET" request, browsers won't allow "POST" here. JSONP must return simple function call, passing one parameter into it, JSON with result data. This function will be called on your JavaScript.
JSONP is just an workaround for making cross site calls.

Angular $http params encoding issue

I have an Angular 1.4 service that makes an $http.get() request to the MapQuest API. If I pass my API key as part of the url directly then it works however if I add it as part of the params field then it does not.
Working:
var url = 'http://open.mapquestapi.com/geocoding/v1/address?key=Gmjtd%7Cluub2d08nh%2C2s%3Do5-2u2gh4';
$http.get(url);
// Key actually sent
Gmjtd%7Cluub2d08nh%2C2s%3Do5-2u2gh4
Not working:
var url = 'http://open.mapquestapi.com/geocoding/v1/address';
$http.get(url, {
params: {
key: 'Gmjtd%7Cluub2d08nh%2C2s%3Do5-2u2gh4'
}
});
// Key actually sent
Gmjtd%257Cluub2d08nh%252C2s%253Do5-su2gh4
Not working either:
var url = 'http://open.mapquestapi.com/geocoding/v1/address';
$http.get(url, {
params: {
key: encodeURIComponent('Gmjtd%7Cluub2d08nh%2C2s%3Do5-2u2gh4');
}
});
// Key actually sent
Gmjtd%25257Cluub2d08nh%25252C2s%25253Do5-2u2gh4
It appears that passing the key in the params field causes it to undergo some kind of encoding process which renders it invalid.
How can I maintain the original key when using the params method?
Angular does indeed encode params. You should call decodeURIComponent since the key is already encoded.
$http.get(url, {
params: {
key: decodeURIComponent('Gmjtd%7Cluub2d08nh%2C2s%3Do5-2u2gh4');
}
});

Angularjs $resource url intercept url encoding

I'm working on a sort of file-manager application that connects to a RESTFUL file api.
On the angular app, each file and directory is an instance of angular $resource using the file-object property relativePathName as resource id .
js
var File = $resource(url + '/:type/:id', {id: '#relativePathName', type: '#type'}, {…});
The problem is, when updating a file resource, the relativePathName parameter gets url encoded, e.g. / becomes %2F which causes the server to intercept the request before it hits the actual API (I assume the server treats this as a physical address and of returns a 404 response). The API is capable of treating whole url segments as a single param, so basically it'd treat path/to/file as a uri parameter of http://myapp.com/api/files/create/path/to/file and not as a different uri.
My question is, is there a way to modify the request url after it's being generated by the private Router instance inside of the resource constructor? If so, how (found nothing on this in the docs)?. What would be a possible solution? passing relativePathName as a parameter instead of declaring it as the resource id (which would require modifying the API)?
Thanks in advance.
Thomas
Using $resource is not the one stop shop for RESTful service calls, it is merely a convenience service for api's that are structured in a certain way. If $resource cannot do what you need, just create your own service using a mix of $resource and $http that that fits the api you are trying to call.
In our app we wanted a different URL for getByName requests, so we override the resource address with the URL parameter of the action getByName like so:
myapp.factory('ListGroup', ['$resource',
function($resource) {
return $resource(
'/API/List/:Id',
{
Id:'#Id',
Name:'#Name'
},
{
getByName: {method: 'GET', url: '/API/List/Group/:Name', isArray: true}
}
);
}
]);
My question is, is there a way to modify the request url after it's being generated by the private Router instance inside of the resource constructor?
I'm not sure about inside of the resource constructor but you can use interceptors to programatically alter route urls after they have been generated by $resource.
structure
hooks.config.js
hooks.factory.js
hooks.module.js
hooks.module.js
module.exports = angular
.module("app.hooks", [])
.factory("hooks", require("./hooks.factory.js"))
.config(require("./hooks.config.js"));
hooks.config.js
module.exports = ["$httpProvider", function($httpProvider) {
$httpProvider.interceptors.push("hooks");
}];
hooks.factory.js
module.exports = ["$q", "$location", function($q, $location) {
var basePrefix = "/api/v1";
return {
//invoked on every http request
request: function(request) {
request.url = interpret(request.url);
return $q.when(request);
}
};
function interpret(str) {
//if requesting an html template, don't do anything
if (str.indexOf(".html") > -1)
return str;
//if you're accessing the api, append the base prefix globally here
else
return basePrefix + str;
}
}];

Javascript + MailChimp API subscribe

When making this request:
// Subscribe a new account holder to a MailChimp list
function subscribeSomeoneToMailChimpList()
{
var options =
{
"apikey": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"id": "xxxxxx",
"email":
{
"email": "me#example.com"
},
"send_welcome": false
};
var mcSubscribeRequest = UrlFetchApp.fetch("https://us4.api.mailchimp.com/2.0/lists/subscribe.json", options);
var mcListObject = Utilities.jsonParse(mcSubscribeRequest.getContentText());
}
This response is returned:
Request failed for https://us4.api.mailchimp.com/2.0/lists/subscribe.json returned code 500. Truncated server response: {"status":"error","code":-100,"name":"ValidationError","error":"You must specify a apikey value"} (use muteHttpExceptions option to examine full response) (line 120, file "v2")
Line 120 is the line on which UrlFetchApp.fetch is called.
The API key is valid (I have tested with simpler API calls that don't include associative arrays). When I append the API key directly to the base URL and remove it from the options, I get an error saying that the list ID is invalid. When I then append the list ID directly to the base URL and remove it from options, I get an error saying that the email address must be in associative array form.
My question is: Using the above format, how does one send requests that contain associative arrays?
The relevant API documentation can be found here.
After further research & tinkering, I was able to solve this:
https://<dc>.api.mailchimp.com/2.0/lists/subscribe.json?apikey=<my_api_key>&id=<my_list_id>&email[email]=test#test.com&merge_vars[FNAME]=John&merge_vars[LNAME]=Doe&double_optin=false&send_welcome=false
Where <dc> should be replaced with the portion after the dash in your API Key. e.g. "us1", "us2", "uk1", etc.
Doing this in javascript exposes your API key to the world. If someone has your key, he/she can make changes to or gain access to your account.
I think I figured out what is going on after reading through the UrlFetchApp.fetch Docs.
https://developers.google.com/apps-script/reference/url-fetch/url-fetch-app?csw=1#fetch(String)
It looks like you should be using some of the extra params to do the request such as payload and method. Your options variable should look like this.
var payload = {
"apikey": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"id": "xxxxxx",
"email": {
"email": "me#example.com"
},
"send_welcome": false
};
payload = Utilities.jsonStringify(payload); // the payload needs to be sent as a string, so we need this
var options = {
method: "post",
contentType: "application/json", // contentType property was mistyped as ContentType - case matters
payload: payload
};
var result = UrlFetchApp.fetch("https://<dc>.api.mailchimp.com/2.0/lists/subscribe.json", options);
Where <dc> should be replaced with the portion after the dash in your API Key. e.g. "us1", "us2", "uk1", etc.
The issue is that your options variable is suppose to be used as JSON and not as a GET url parameter. Also mailchimp specifies that it is better to use POST instead of GET. So over all you should make sure to set you method to "post" and make sure your payload is valid JSON.

Categories