URI encoding in ODataModel v2 - javascript

I've created a SAPUI5 table widget and made sure that it works. Now, when clicking on a row, the detail view is loaded, but no data is present. The server exposes an entity Site with a primary key which is of type "string".
The client-side code is as follows (assume that oModel is ODataModel, sSiteCode is a string that may contain Cyrillic characters):
// sSiteCode may contain Cyrillic characters
var oKey = {
SiteCode: sSiteCode
};
var sPath = "/" + oModel.createKey("Sites", oKey);
this.getView().bindElement({path: sPath});
It turns out that, if sSiteCode = 'б' (i.e., contains Cyrillic characters), then a GET request will be sent (via batching) to the following URI:
http://<server>:<port>/odata/Sites('б')
However, the server is unable to parse this URI (and subsequently replies with a 404), as it doesn't know what encoding to use. I patched the method ODataModel.prototype._createRequestUrl as follows:
sNormalizedPath = this._normalizePath(sPath, oContext);
sNormalizedPath = encodeURI(sNormalizedPath); // my addition
Then it seems to work, for this particular case. I'm wondering if this is a bug or a feature, and what should I do next?
FYI, I'm using OpenUI5 1.32.11.

Instead of sending
http://<server>:<port>/odata/Sites('б')
The actual string sending to the server should be
http://<server>:<port>/odata/Sites(%27б%27)
Which is the result of the encodeURI() call. Since UI5 allows you to freely define the Models URL and its parameters you have to take care on the correct URI encoding (and all parameters).
So in my opinion this is not a bug but the down part of the possibility to configure the URI without "black-box" behaviour of UI5.

Related

Uploading/Downloading Byte Arrays with AngularJS and ASP.NET Web API

I have spent several days researching and working on a solution for uploading/downloading byte[]’s. I am close, but have one remaining issue that appears to be in my AngularJS code block.
There is a similar question on SO, but it has no responses. See https://stackoverflow.com/questions/23849665/web-api-accept-and-post-byte-array
Here is some background information to set the context before I state my problem.
I am attempting to create a general purpose client/server interface to upload and download byte[]’s, which are used as part of a proprietary server database.
I am using TypeScript, AngularJS, JavaScript, and Bootstrap CSS on the client to create a single page app (SPA).
I am using ASP.NET Web API/C# on the server.
The SPA is being developed to replace an existing product that was developed in Silverlight so it is constrained to existing system requirements. The SPA also needs to target a broad range of devices (mobile to desktop) and major OSs.
With the help of several online resources (listed below), I have gotten most of my code working. I am using an asynchronous multimedia formatter for byte[]’s from the Byte Rot link below.
http://byterot.blogspot.com/2012/04/aspnet-web-api-series-part-5.html
Returning binary file from controller in ASP.NET Web API
I am using a jpeg converted to a Uint8Array as my test case on the client.
The actual system byte arrays will contain mixed content compacted into predefined data packets. However, I need to be able to handle any valid byte array so an image is a valid test case.
The data is transmitted to the server correctly using the client and server code shown below AND the Byte Rot Formatter (NOT shown but available on their website).
I have verified that the jpeg is received properly on the server as a byte[] along with the string parameter metadata.
I have used Fiddler to verify that the correct response is sent back to the client.
The size is correct
The image is viewable in Fiddler.
My problem is that the server response in the Angular client code shown below is not correct.
By incorrect, I mean the wrong size (~10K versus ~27.5K) and it is not recognized as a valid value for the UintArray constructor. Visual Studio shows JFIF when I place the cursor over the returned “response” shown in the client code below, but there is no other visible indicator of the content.
/********************** Server Code ************************/
Added missing item to code after [FromBody]byte[]
public class ItemUploadController : ApiController{
[AcceptVerbs("Post")]
public HttpResponseMessage Upload(string var1, string var2, [FromBody]byte[] item){
HttpResponseMessage result = new HttpResponseMessage(HttpStatusCode.OK);
var stream = new MemoryStream(item);
result.Content = new StreamContent(stream);
result.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
return result;
}
}
/***************** Example Client Code ********************/
The only thing that I have omitted from the code are the actual variable parameters.
$http({
url: 'api/ItemUpload/Upload',
method: 'POST',
headers: { 'Content-Type': 'application/octet-stream' },// Added per Byte Rot blog...
params: {
// Other params here, including string metadata about uploads
var1: var1,
var2: var2
},
data: new Uint8Array(item),
// arrybuffer must be lowecase. Once changed, it fixed my problem.
responseType: 'arraybuffer',// Added per http://www.html5rocks.com/en/tutorials/file/xhr2/
transformRequest: [],
})
.success((response, status) => {
if (status === 200) {
// The response variable length is about 10K, whereas the correct Fiddler size is ~27.5K.
// The error that I receive here is that the constructor argument in invalid.
// My guess is that I am doing something incorrectly with the AngularJS code, but I
// have implemented everything that I have read about. Any thoughts???
var unsigned8Int = new Uint8Array(response);
// For the test case, I want to convert the byte array back to a base64 encoded string
// before verifying with the original source that was used to generate the byte[] upload.
var b64Encoded = btoa(String.fromCharCode.apply(null, unsigned8Int));
callback(b64Encoded);
}
})
.error((data, status) => {
console.log('[ERROR] Status Code:' + status);
});
/****************************************************************/
Any help or suggestions would be greatly appreciated.
Thanks...
Edited to include more diagnostic data
First, I used the angular.isArray function to determine that the response value is NOT an array, which I think it should be.
Second, I used the following code to interrogate the response, which appears to be an invisible string. The leading characters do not seem to correspond to any valid sequence in the image byte array code.
var buffer = new ArrayBuffer(response.length);
var data = new Uint8Array(buffer);
var len = data.length, i;
for (i = 0; i < len; i++) {
data[i] = response[i].charCodeAt(0);
}
Experiment Results
I ran an experiment by creating byte array values from 0 - 255 on the server, which I downloaded. The AngularJS client received the first 128 bytes correctly (i.e., 0,1,...,126,127), but the remaining values were 65535 in Internet Explorer 11, and 65533 in Chrome and Firefox. Fiddler shows that 256 values were sent over the network, but there are only 217 characters received in the AngularJS client code. If I only use 0-127 as the server values, everything seems to work. I have no idea what can cause this, but the client response seems more in line with signed bytes, which I do not think is possible.
Fiddler Hex data from the server shows 256 bytes with the values ranging from 00,01,...,EF,FF, which is correct. As I mentioned earlier, I can return an image and view it properly in Fiddler, so the Web API server interface works for both POST and GET.
I am trying vanilla XMLHttpRequest to see I can get that working outside of the AngularJS environment.
XMLHttpRequest Testing Update
I have been able to confirm that vanilla XMLHttpRequest works with the server for the GET and is able to return the correct byte codes and the test image.
The good news is that I can hack around AngularJS to get my system working, but the bad news is that I do not like doing this. I would prefer to stay with Angular for all my client-side server communication.
I am going to open up a separate issue on Stack Overflow that only deals with the GET byte[] issues that I am have with AngularJS. If I can get a resolution, I will update this issue with the solution for historical purposes to help others.
Update
Eric Eslinger on Google Groups sent me a small code segment highlighting that responseType should be "arraybuffer", all lower case. I updated the code block above to show the lowercase value and added a note.
Thanks...
I finally received a response from Eric Eslinger on Google Group. He pointed out that he uses
$http.get('http://example.com/bindata.jpg', {responseType: 'arraybuffer'}).
He mentioned that the camelcase was probably significant, which it is. Changed one character and the entire flow is working now.
All credit goes to Eric Eslinger.

Dojo JsonRest appends &name=* to target URL

I try to use the Dojo JsonRest to fill a dijit.form.select Box and I use the following code:
var stateStore = new JsonRest({target: "ip-address/activiti-rest/service/repository/process-definitions?startableByUser=kermit", headers: {"Authorization": "Basic a2VybWl0Omtlcm1pdA=="}});
The Problem is, that the target-url is extended with &name=*
Therefore, the Server sees the following request: ipaddress/activiti-rest/service/repository/process-definitions?startableByUser=kermit&name=*
I can not figure out where the &name=* comes from.
I read this article: http://dojo-toolkit.33424.n3.nabble.com/dojox-data-JsonRestStore-appends-quot-Title-quot-to-my-target-url-why-td2012228.html and used the allowNoTrailingSlash-Attribute without success.
My question is how I can avoid that the target url gets altered. We must avoid that &name=* gets attached at the end of the url.
Thanks abd best regards
It was my fault because I used dijit/form/FilteringSelect instead of dijit/form/Select. FilteringSelect allows you to type in letters. Those letters were appended to the url.

Servlet calling from window.showModalDialog(...)

I am calling another application context from window.showModalDialog but confused with following work. Same code to pass parameter within showModalDialg.
var myArguments = new Object();
myArguments.param1 = "Hello World :)";
window.showModalDialog("java2sTarget.html", myArguments, '');
and i can read these myArguments(parameters) in generated HTML using following code:
<script>
document.write(window.dialogArguments.param1);//Hello World :)
</script>
I can't use query string & i am sending myArguments(parameter) because i want to hide parameter from Application user.
Now i am calling servlet from showModalDialog(..)
onclick="window.showModelDialog('http://localhost:7778/app/servlet/test',myArguments,'');"
onclick="window.showModelDialog('http://localhost:7778/app/servlet/test',myArguments,'');"
But as per my knowledge
Servlet --> Servlet container --> HTML+JS+CSS
so JS will be available at last phase, but i want to use in first phase(Servlet).
Now, i need to make some Decision in servelt code based on myArguments(parameter).
is there any way to read these myArguments(parameters) in servlet code?
Pass it as a request parameter in the query string.
var queryString = "param1=" + encodeURIComponent("Hello World :)");
onclick="window.showModelDialog('http://localhost:7778/app/servlet/test?' + queryString, myArguments, '');"
No, there's no other alternative. The request URL is not visible in the modal dialog anyway.
As main objective is to hide query string from User to avoid misuse of those parameters.
I tried following work around.
Developers send hidden parameters to get relative information form source(e.g.:DataBase). And we also know that we can send hidden information in Window.showModalDialog using dialogArguments
Work Around:
(i) I got relative information from server one-step before calling Window.showModalDialog using jQuery.getJSON()
(ii) i used google-gson API at servlet side to convert JavaBeans into Json strings.Solution 1 Solution 2
(iii) Convert JSON into javascript object using jQuery.parseJSON
var args = jQuery.parseJSON(json);
window.showModalDialog("pages/"+args.pageName, args, '');
i used args.pageName to make things dynamic
Please suggest improvements in this work-around. Thanks

How many times should a URL-as-a-value-in-a-querystring should be encoded?

I'm really confused how many times should I encode a URL when it is set as a value in a querystring 'coz we know browser has their own encoding process. Here's the scenario:
I want to redirect to another location which I want to pass the previous URL:
Note: the current URL is http://localhost:8081/CostMonitoring/MainMenu.aspx?Option=AllCE
Method A (without encodeURIComponent()):
window.location = 'CostEstimateApproval.aspx?CEMID=40' +
'&ToStatus=1CE'+
'&PrevURL=' + window.location;
I get this in the address bar
http://localhost:8081/CostMonitoring/CostEstimateApproval.aspx?CEMID=40&ToStatus=1CE&PrevURL=http://localhost:8081/CostMonitoring/MainMenu.aspx?Option=AllCE
without encodeURIComponent(), everything works fine and the value of Request.Querystring("PrevURL") in the receiving page is
http://localhost:8081/CostMonitoring/MainMenu.aspx?Option=AllCE
which is correct.
Method B (with encodeURIComponent()):
window.location = 'CostEstimateApproval.aspx?CEMID=40' +
'&ToStatus=1CE'+
'&PrevURL=' + encodeURIComponent(window.location);
with this method I get this in the address bar:
http://localhost:8081/CostMonitoring/CostEstimateApproval.aspx?CEMID=40&ToStatus=1CE&PrevURL=http%3A%2F%2Flocalhost%3A8081%2FCostMonitoring%2FMainMenu.aspx%3FOption%3DAllCE
and the value of Request.Querystring("PrevURL") in the receiving page is
http://localhost:8081/CostMonitoring/MainMenu.aspx?Option=AllCE
which is also decoded correctly.
My questions:
Should I encode the URL-as-value? Will it be redundant if I encode it then the browser encode it again?
or should I let the browser encode it for me? If I let the browser, will the receiving page be confused from URL-as-a-value's value to the real URL value? Please consider this example:
http://www.domain.com/newpage.aspx?SameName=DifferentValue&PrevURL=http://www.domain.com/oldpage.aspx?SameName=DifferentValue&PrevURL=http://www.domain.com/anypage.aspx
as you can see, both URL (the real URL and the URL-as-a-value) when not encoded has the same data name which is SameName. How does the receiving side handle this? or the HTTP server?
Thanks in advance!
You should use encodeURIComponent (once), since you're encoding a url parameter.
As you noted at the end of your question, failing to encode the url with encodeURIComponent would be problematic if your url included an &, for example.
Note that your Method A only worked because your example prevUrl is somewhat simply formed, e.g. it doesn't include a second url parameter.

Make an ajax request to get some data, then redirect to a new page, passing the returned data

I want to redirect after a successful ajax request (which I know how to do) but I want to pass along the returned data which will be used to load an iframe on the page I just redirected to.
What's the best way to pass such data along and use it to open and populate an iframe in the page I just redirected to?
EDIT:
I am passing a GET variable but am having to use the following to access it for use in my iframe src attribute:
function $_GET(q,s) {
s = (s) ? s : window.location.search;
var re = new RegExp('&'+q+'=([^&]*)','i');
return (s=s.replace(/^\?/,'&').match(re)) ? s=s[1] : s='';
}
var d = $_GET('thedata');
I assume there isn't really a more straightforward way to access the GET vars?
If it's not too much data, you could pass it as a get parameter in the redirect:
document.location = "/otherpage?somevar=" + urlescape(var)
Remember that urls are limited to 1024 chars, and that special chars must be escaped.
If it is beyond that limit your best move is to use server side sessions. You will use a database on the server to store the necessary information and pass a unique identifier in the url, or as a cookie on the users computer. When the new page loads, it can then pull the information out of the database using the identifier. Sessions are supported in virtually every web framework out of the box.
Another alternative may be to place the data as a hidden attribute in a form which uses the post method (to get around the 1024 char limit), and simulating a submission of the form in javascript to accomplish the redirect, including the data.

Categories