Encoding rest path variable in angularjs - angularjs

I am having a peculiar issue.
I am calling a restful service with path param from angularjs controller/service.
Below is the url format
/payment/{id}/credit/{creditId}/fetch/options
Now, creditId has special charachters in it for eg 'abcd%xyz-433'
I am calling a service which makes a GET rest call as below in angualarjs controller
creditService.creditOption('abc','abcd%xyz-433').success(function(data, status) {
$log.log('log the status ' + status);
});
The service is as below
creditOption: function(id, creditId) {
return $http({
method: 'GET',
url: '/payment/'+id+'/credit/'+creditId+'/fetch/options'
});
}
The Restful service signature is as follows
#RequestMapping(method = RequestMethod.GET, value = "/payment/{id}/credit/{creditId}/fetch/options", produces = APPLICATION_JSON)
public boolean getCreditOption(#PathVariable String id, #PathVariable String creditId) {
return true
}
This is giving me 404 all the time i execute this code. But when i remove the special charachters it works fine and hits the restful service. I also tried to use encodeURIComponent() and then send the value.. but it is still the same case.
Please let me know if there is any way out for this.
Thanks for any help

The % symbol is used for encoding values in URls, see rfc3986 section 2.1. In particular it's expected that there will be two hex digits after the % - which it seems like you are exactly stumbling on to.
You may have to escape the percent (use %25) or choose a different character to substitute.
You can try it on this page, the address is:
http://stackoverflow.com/questions/27641286/encoding-rest-path-variable-in-angularjs
A 1 percent encoded is %31, so you could just as easily go here:
http://stackoverflow.com/questions/2764%31286/encoding-rest-path-variable-in-angularjs

Related

Angular http.get charset

I'm having some trouble getting my angular application to parse my json-data correctly.
When my json-feed contains e.g. { "title": "Halldórsson Pourié" }
my application shows Halld�rsson Pouri�
I figure the problem is the charset, but i can't find where to change it.
Currently i am using ng-bind-html and using $sce.trustAsHtml(), and I'm very sure the problem occurs when $http.get(url) parses my json.
So how do i tell $http.get(url) to parse the data with a specific charset?
I had a similar issue and resolved it by using:
encodeURIComponent(JSON.stringify(p_Query))
where p_Query is a JSON containing the details of the request (i.e. your { "title": "Halldórsson Pourié" }).
EDIT:
You may also need to add to the header of your GET request the following:
'Content-Type': 'application/x-www-form-urlencoded ; charset=UTF-8'
Had same problem with accented characters and some scientific notation in text/JSON fields and found that AngularJS (or whatever native JavaScript XHR/fetch functions it is using) was flattening everything to UTF-8 no matter what else we tried.
The other respondent here claimed that UTF-8 should somehow still be accommodating your extended character set, but we found otherwise: taking samples of the source data directly, we loaded it into text editor with different encodings and UTF-8 would still squash extended characters to that � placeholder.
In our case, the encoding was ISO-8859-15 but you may be sufficient with ISO-8859-1.
Try adding this $http configuration to your $http.get() call, accordingly:
$http.get(url, {
responseType: 'arraybuffer',
transformResponse: function(data) {
let textDecoder = new TextDecoder('ISO-8859-15'); // your encoding may vary!
return JSON.parse(textDecoder.decode(data));
}
});
This should intercept AngularJS default response transformation and instead apply the function here. You will, of course, want to consider adding some error handling, etc.
If there are better ways to do this without a custom transformResponse function, I have yet to find anything in the AngularJS documentation.
$http.get(url, {responseType: 'arraybuffer', }).then(function (response) { var textDecoder = new TextDecoder('ISO-8859-1'); console.log(textDecoder.decode(response.data));});
This code will replace all your � placeholders into normal characters.

AngularJS $http GET comma separated query string parameters

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.

How to use / in parameter during rest webservice call from angularjs?

I need to send a value like app/role as parameter through rest webservice url from angularjs
In controller.js
var roleName = 'app/role';
checkRole.check({'roleName': roleName}, function(data){}
In model.js
popModel.factory('checkRole', function ($resource) {
return $resource('./rest/checkRole/:roleName',{roleName:'#roleName'},{
check: {'method':'GET'},
});
});
The rest webservice call in java
#GET
#Path("/checkRole/{roleName}")
#Produces(MediaType.APPLICATION_JSON)
public Response checkRole(#Context HttpServletRequest request, #PathParam("roleName") String roleName);
When i pass it i am getting browser console error as
Bad request response from the server.
For normal parameter values like 'Test', 'Solution', 'Application' etc. If i use with / as a parameter no process is done and i am getting error.
/ is reserved character for GET request. So, you can't use them directly. If you use them, you would get Bad Request Error.
One of the solution can be to encode the URL on client side and decode it on server.
Reference:
Characters allowed in GET parameter
How to handle special characters in url as parameter values?

Angular does not encode the semi-colon character in an url

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

Making calls from the Javascript client library with #Named and unnamed parameters makes no sense

I have a Cloud Endpoints method that looks like this:
//HTTP POST
#ApiMethod(name = "hylyts.insert")
public Hylyt insertHylyt(#Named("url") String url, Hylyt hylyt, User user)
throws OAuthRequestException{
log.info("Trying to save hylyt '"+hylyt+"' with id '"+hylyt.getId());
if (user== null) throw new OAuthRequestException("Your token is no good here.");
hylyt.setArticle(getArticleKey(url, user));
ofy().save().entity(hylyt);
return hylyt;
}
I call it from the Javascript Client Library using this:
gapi.client.hylytit.hylyts.insert({PARAMS}).execute(callback);
Now, if I structure {PARAMS} as suggested in the docs (second example),
{
'url': url,
'resource': {
'hylyt': {
'contentType': 'application/json',
'data': hylyt
}
}
}
I get a null object in the endpoint (not to mention that the whole point of this library is to make these calls simple, which this structure clearly violates).
When I structure {PARAMS} as these answers suggest,
{
'url': url,
'resource': hylyt
}
I get a null object in the endpoint again. The correct syntax is this:
{
'url': url,
'id': hylyt.id
'text': hylyt.text
}
Which just blows my mind. Am I doing this all wrong? Is this a bug? Is it only happening because gapi is also passing the auth token in the background?
Yes, I could use the request syntax instead, but, again, why even use the library if it's just as complex as making the XHRs in pure javascript? I wouldn't mind the complexity if Google explained in the docs why things are happening. But the docs, paraphrased, just say use these methods and the auth, CORS, and XHR magic will happen behind closed doors.
Is the API method correctly recognized as POST method?
The resource parameter which is sent as POST body won't work correctly in a GET request.
The way it looks you are actually sending a GET request with the Hylyt properties in the query string.
To make sure you can change the method annotation to this:
#ApiMethod(name = "hylyts.insert", httpMethod = HttpMethod.POST)
Yup, agreed it's a bug. caused me great pains as well.
So i guess the work around is to create a combined object to pass to your api all named and un named parameters. Rather than hardcode each.. a quick loop might be better.
var param = {};
param["url"] = url;
for (var prop in hylyt) {
param[prop] = hylyt[prop];
}
gapi.client.hylytit.hylyts.insert(param).execute(callback);
That mashing together of parameters / objects can become a slick function if you really want.. but it's a band aid for what I'd consider a defect.
I see in the related question (cloud endpoints resource attribute for transmitting named params & body not working), you actually logged a defect.. Good stuff. Though there still appears no movement on this one. fingers crossed for someday!
The bug has been resolved. The correct syntax is
gapi.client.hylytit.hylyts.insert({url: url}, hylyt).execute(callback);

Resources