I'm trying to do a HTTP GET call using Angular's $http. I've written a generic function like this,
function doGETRequest(url, params) {
return $http({
method: 'GET',
url: baseURL + url,
headers: {
'Authorization': 'Bearer ' + access_token
},
params: params
});
}
I want to create a URL like this,
https://content.googleapis.com/drive/v2/about?fields=quotaBytesTotal%2CquotaBytesUsed%2CrootFolderId%2Cuser but I cannot for the life of me figure out the query param part. So far I tried the following:
doGETRequest('/about', {fields:'user,quotaBytesTotal,quotaBytesUsed,rootFolderId'})
doGETRequest('/about', {fields:['user','quotaBytesTotal','quotaBytesUsed','rootFolderId']})
doGETRequest('/about', {fields:encodeURIComponent('user,quotaBytesTotal,quotaBytesUsed,rootFolderId')})
It does work for the second one, but it parses it as https://content.googleapis.com/drive/v2/about?fields=user&fields=quotaBytesTotal&fields=quotaBytesUsed&fields=rootFolderId and Google just sends response for the first query and ignores the rest (as expected).
So the question is how do I pass comma separated values in query params? Do I have to manually write URL encoded values? Doesn't this beat the purpose of having a params field?
Okay, for those who stumbled upon the same problem as I did, I found two ways of solving this.
First, as #kiro112 suggested in the comments, I write the params directly in the URL. Since $http doesn't encode the URL it'll work. So the code will be something like
doGETRequest('/about?fields='+encodeURIComponent("user,quotaBytesTotal,quotaBytesUsed,rootFolderId"))
and we'll have to get rid of the params part in the doGetRequest() method. Personally, I don't like this one as it beats the purpose of having the params part in $http.
Second method is overriding the default $httpParamSerializer and supplying our own. To do that, the doGetRequest() method has to be modified like so,
function doGETRequest(url, params) {
return $http({
method: 'GET',
url: baseURL + url,
headers: {
'Authorization': 'Bearer ' +access_token
},
params: params,
paramSerializer: function (value){
return Object.keys(value)+'='+encodeURIComponent(Object.values(value));
}
});
}
What happens here is, we do not let angular encode the params. Let me tell you why. As per the docs here, angular only does encoding like so,
{'foo': 'bar'} results in foo=bar
{'foo': Date.now()} results in foo=2015-04-01T09%3A50%3A49.262Z (toISOString() and encoded representation of a Date object)
{'foo': ['bar', 'baz']} results in foo=bar&foo=baz (repeated key for each array element)
{'foo': {'bar':'baz'}} results in foo=%7B%22bar%22%3A%22baz%22%7D" (stringified and encoded representation of an object)
Whereas we require something in the lines of {'foo':['bar', 'baz']} resulting in foo=bar%2Cbaz. So we override the paramSerializer and write our own serializer.
The second approach looks much cleaner to me as far as using generic methods is concerned. However, I am still on a look out for a better option.
Related
This question has been asked before but those solutions i have already tried and i shall
explain along the way.
Task:
I want to send data to api with content-type as "application/x-www-form-urlencoded".
Problem:
The URL works fine in Postman and returns me the correct response. When i try the same body parameters through my Reactjs app, it responds with 400 bad request error.
What i have tried:
myBody:{
grant_type:"XYZ",
client_id:"XYZ"
}
var request = {
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
method: "POST",
body:myBody
};
Then i fetch something like this.
const data = yield fetch(complete_url,request);
I also tried to compose myBody as a formData . Still gives the same error.
Apologies i have not given the URL because of restrictions. I am looking for possibilities of errors i could make.
With 'Content-Type': 'application/x-www-form-urlencoded',
Your request body should follow certain rule (https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/POST).
the keys and values are encoded in key-value tuples separated by '&', with a '=' between the key and the value,
example:
grant_type =value1&client_id=value2.
So i suggest what it looks like in debugger -> network tab.
If the request body is not a problem. Then you should check if there's any special chars.
You also will need to escape symbols and special chars.
controller:
$scope.fileToUpload = function(input) {
if (input.files && input.files[0]) {
CommonService.uploadContactImage.upload({
fileName : input.files[0].name
}, input.files[0], function(data) {
});
}
}
Service:
uploadContactImage:function(input){
console.log("game image");
var req = $http({method: 'POST', url: options.api.base_url + '/gameimageupload/',
dataType: 'json', headers: {'Content-Type': undefined}})
.success(function (data)
{
console.log("data" + data);
return data;
});
If you take a good look at your code you will see that there are quite a few things wrong with it. For example, you have defined in your service an uploadContactImage function which takes a single Javascript object as argument (input), while in your controller you attempt to call CommonService.uploadContactImage.upload(...) instead of CommonService.uploadContactImage(...). Additionally, even if the uploadContactImage function was called correctly it doesn't actually do anything with its argument, ie. the input object is never used in the function body.
These issues aside you cannot submit a file to the server just by adding it to the body of a POST request the way you (seem to be) trying to do. Without going into too much detail here, in order to upload a file from the browser a request with content type multipart/form-data needs to be submitted, which will contain your file as well as the necessary HTTP headers for the server to identify it and parse it correctly. I suppose you could try and construct this request yourself, however it's not a task for the faint-hearted. What I would suggest instead is to use one of the many file upload modules available for Angular.js. A Google search will give you quite a few modules that you can check out to see which better fits your needs.
I'm trying to mock a $http get request with parameters. Using the actual $http service we could do something like this
$http.get(url, {params : { articleId:10, id : 1}})
.success(function (response) {
defer.resolve(response.data);
});
Can we do the same with $httpBacked?
$httpBackend.when('GET', url, { params: { articleId: 10, id : 1}})
.respond({data: areas});
The obvious unpleasant alternative is writing out the full expected url which I am hoping I don't have to do as this feels neater.
Thanks
I had forgotten about this question, unfortunately the answer is that you have to specify the full get url with parameters
....just incase someone stumbles across this with the same problem
Apparently the only thing that can be passed parameters in a regular expression. According to the documentation of angular only four parameters can be passed, method, url, data, and headers.
We need an encoded semi-colon character in our url parameter but angular does not encode this character.
The resource we use looks something like this:
app.factory('TestResource', ['$resource', function ($resource) {
return $resource('http://somedomain.com');
}]);
app.run(['TestResource', function (TestResource) {
TestResource.query({
q: 'sin;sout'
});
}]);
This is the result we get:
http://somedomain.com/?q=sin;sout
We want the url to be:
http://somedomain.com/?q=sin%3Bsout
But if we pre-encode the parameter before sending the % char get's encoded like this:
http://somedomain.com/?q=sin%253Bsout
How can we get the desired result? (http://somedomain.com/?q=sin%3Bsout)
Angular uses its own function to encode the URL. And that function doesn't encode characters that usually don't need to be encoded like / or ?. ; is such a character, too. It's used in matrix URIs, e.g. So Angular's behavior actually conforms to the standard, but it could be a problem when such characters are used literally, i.e. without a special meaning. That seems to be the case here.
Since the URL is build just before sending the request, there's not much we can do. There is kind of a loophole though: Only parameters are encoded, not the base URL. So any workaround would involve creating the URL or at least a part of it yourself.
You could add an interceptor, that adds the properly encoded parameters to the URL before Angular does it. You could even completely replace Angular's behavior that way.
UPDATE:
Another solution came to my mind. The $http service delegates sending the actual request to the $httpBackend service which receives the already constructed URL. Using a decorator you can replace either the ; or the incorrectly encoded %253B with %3B right before the request is sent:
app.config(function($provide) {
$provide.decorator('$httpBackend', function($delegate) {
return function(method, url, post, callback, headers, timeout, withCredentials, responseType) {
url = url.replace(';', '%3B');
$delegate(method, url, post, callback, headers, timeout, withCredentials, responseType);
};
})
});
Building on zeroflagL's answer one could consider changing the replace to change all occurrences of the semi-colon (as it is written it will only replace the first semi-colon):
url = url.replace(/;/g, '%3B');
There is a closed issue for this https://github.com/angular/angular.js/issues/9224
path: Don't escape semicolons. Breaks matrix parameters, and no evidence that Angular's current behavior is problematic.
query: Escaping the entire query string would be unwise, as it breaks an established (albeit archaic) expectation for servers to be able to handle semicolon-delimited query strings.
query values: Probably safe to escape, especially if the server interprets semicolons as query delimiters. Passing complex data types (ie. JSON) via query parameters is a bad idea, but "real-world" string values often contain semicolons, and it seems like a good idea to escape them.
I'll go with zeroflagL's answer but only changing the query values. Like this:
$provide.decorator('$httpBackend', function($delegate) {
return function(method, url, post, callback, headers, timeout, withCredentials, responseType) {
var a = document.createElement('a');
a.href = url;
if(a.search.length > 1) {
var params = a.search.substring(1).split('&');
for (var i=0; i<params.length; i++) {
var param = params[i].split('=');
if(param[1]) {
params[i] = [param[0], param[1].replace(';', '%3B')].join('=');
}
}
url = url.substring(0, url.lastIndexOf('?')) + '?' + params.join('&');
}
$delegate(method, url, post, callback, headers, timeout, withCredentials, responseType);
};
});
You can use encodeURIComponent() method:
q: encodeURIComponent('sin;sout')
I'm working on an API with FosRestBundle and AngularJS for the front part.
GET requests working but i have problems with POST requests. In the API controller the request object is empty..
Here is the front part
$http.post(Route.api + '/leads', {lead: "test"}).success(function(data) {
});
And here is the FosRestBundle controller
public function postAction(Request $request) {
var_dump($request->request->all()); // empty ? :(
}
Someone already have the same problem ?
Thanks ;)
Yes I had such problem, and I've solve this problem passing Content-Type header to Symfony, here is my code example
return $http({
method: 'POST',
url: Route.api + '/leads',
data: dataYouWantToSend, //see the note regarding this parameter
headers: {'Content-Type': 'application/x-www-form-urlencoded'}
})
NOTE :
If you pass data parameter with object, you will receive in in the POST your data by JSON with key of your JSON's root.
If you want to receive it like form data, you have to convert it to querystring (e.g. ?deal=test&ok=1), for example if you're using jQuery too, you can convert it like this
var dataYouWantToSendQueryString = $.param(dataYouWantToSend);
hope it will help you too.
P.S. I've researched and haven't found how to convert object to query string in angular, so if Angular has such functionality, I will glad to know about it, if someone know, please comment.