I use JSONP on a client to get data from a server using a WCF service that can return results using HTTP GET (It gets a 'callback' parameter which is a 'function name' and returns callback({data}), you know... JSONP).
Everything works, but if I enable caching (using 'AspNetCacheProfile')on one of the service's operations - then something is wrong, and I'm not sure what...
The way I get the JSONP is by using a function I picked up some time ago from a question here on SO (http://stackoverflow.com/questions/2499567/how-to-make-a-json-call-to-a-url)
function getJSONP(url, success) {
var ud = 'fc_' + + Math.floor(Math.random()*1000000),
script = document.createElement('script'),
head = document.getElementsByTagName('head')[0]
|| document.documentElement;
window[ud] = function(data) {
head.removeChild(script);
success && success(data);
};
script.src = url.replace('callback=?', 'callback=' + ud);
head.appendChild(script);
}
This creates a random id ('fc_xxxx') then assigns it as a function inside the window object, then use it as the 'callback' parameter for the url of the dynamic javascript that is injected to the document, and then the 'ud' function runs, removes the script and calls the 'success' callback function with the received data.
When using it on normal uncached operations from the service, it usually takes about 200ms to get back the response, and it works ok. The cached responses takes ~10ms -
and I get an error that the 'fc_XXXXX' function is undefined.
It's as if the response is "too fast" for it.
I also tried using jQuery.getJSON() - and, again the callback doesn't trigger.
In all cases when I look at the network traffic in Firebug - I can see the GET request, and I can see that the right data does in fact gets returned.
Does anybody have an idea how can I make it work right with the cached responses...?
I got it! The name of the response-function is different on each call (on both my manual jsonp implementation and that of jQuery).
The name of the function is part of the response from the server (as that's part of how jsonp works...).
So, if the response is a cached response - it will actually return the old name of the function (which will no longer exist on the client's context).
So I just need to give a constant name for the callback function in this case and it should bee fine. :)
Related
$http.post('#Url.Action("donePressed")',
{
id: $scope.id,
}).then(
function (response) // success callback
{
window.location = '#Url.Action("PdfCreator", "someController")?Id=' + $scope.id;
window.location='#Url.Action("Index","AnotherController")';
},
function (response) // failure callback
{
alert(response.statusText);
});
Hi, I guess I am doing somehting wrong, I want to call to a function the sends me a file as a response, and afterwords I want to leave the page and go somewhere else.
the problem is, because this is a sync I don't get my download.
How can I make this synced?
Async has nothing to do with it. Once you're inside the success callback, the async part is already done. The problem is that you're changing the window location again before the first change has had time to load. In other words, it's the exact opposite of an async problem; the problem is that this code is synchronous and runs too fast.
However, the approach here is flawed to begin with. It might work if the browser was forced to download the file, as the then the first change to window.location would not itself cause the browser view to change. Since PDF is typically a browser-viewable type, this is not guaranteed, though. Regardless, you still have the same issue of need to delay the second call until the first has gotten a response, which is basically impossible. There's no built in event for this type of thing, so the best you'd could do would be to is use setTimeout with a 1-2 second delay, and just hope that that is enough time to get the first response. Even then, if it ever took longer, your code would break again. In other words, it's going to be extremely brittle.
The simple fact is that this is just simply not how HTTP works. You're basically trying to return two responses for a single request, which is not possible. This is a clever way to try to skirt around inherent restrictions in the protocol, I'll give you that, but it's ultimately still insufficient.
All that said, you can actually make this happen via the HTML5 File API and AJAX, but your solution then will only be compatible with modern browsers (basically everything except IE 10 and under). If you do not need to support lesser versions of IE, then you can use the following code instead:
function (response) // success callback
{
$http.get('#Url.Action("PdfCreator", "someController")?Id=' + $scope.id').then(
function (response) // success callback
{
var a = document.createElement('a');
var url = window.URL.createObjectURL(response.data);
a.href = url;
a.download = 'myfile.pdf';
a.click();
window.URL.revokeObjectURL(url);
window.location = '#Url.Action("Index","AnotherController")';
},
function (response) // failure callback
{
alert(response.statusText);
}
);
},
The secret sauce is in fetching the PDF via AJAX and then creating an object URL out of the PDF data. You can then use that to create an anchor element in the DOM and "click" it dynamically to prompt the download. The caveat, though, is that I haven't tried to do this with Angular, so I'm unsure if $http supports getting a binary response. I know with jQuery, you just have to tell it that the XHR object's response type is 'blob', but I'm not sure if you can or how you would do the same thing with Angular. As an alternative, you can simply use XMLHttpRequest directly for this particular AJAX, and simply set xhr.responseType = 'blob'.
I use Delphi XE7. When my Javascript calls my server function that need around 800ms to read sensor and return data, The browser is unresponsive from the moment I click the button to invoke the Javascript until it finally response returns. I'm using the default Javascript generated by the proxy var serverMethods().getChannel(i); to call into my server function.
Javascript call look like this:
var s = serverMethods().getChannel(i);
serial[i].$sensorlValue.text(s.result.fields.sensorString);
serial[i].$sensorlRealValue.text(s.result.fields.sensor);
serial[i].$sensorStatus.text(s.result.fields.sensorStatus+' '+s.result.fields.name);
serial[i].$sensorError.text(s.result.fields.sensorError);
serial[i].$AVString.text(s.result.fields.AVString);
serial[i].$AVError.text(s.result.fields.AVError);
So by default example there are no Javascript callbacks or promise, so embaracaderom manage somehow to block Javascript from executing until response is back and variable a receive values?
I think about try using jQuery Ajax call on URL, but is there any other solution?
Because serverMethods are generated from proxy but for $ajax I need to manually set each of them. Or maybe I do something wrong here and serverMethods can be used without blocking ?
Thanks.
I found the solution to this problem after researching execution path in ServerFunctionExecutor.js that is called on serverMethods().SOMEAPIFUNCTION()
1. Help and documentation are 0, and google + XE7 questions are 0. So if someone from embaracadero read this PLS MAKE DECENT DOCUMENTATION.
ServerFunctionExecutor.js had on line 263
//async is only true if there is a callback that can be notified on completion
var useCallback = (callback != null);
request.open(requestType, url, useCallback);
if (useCallback)
{
request.onreadystatechange = function() {
if (request.readyState == 4)
{
//the callback will be notified the execution finished even if there is no expected result
JSONResult = hasResult ? parseHTTPResponse(request) : null;
callback(JSONResult, request.status, owner);
}
};
}
So it is posible and NOT DOCUMENTED to use callback for unblocking GUI.
Use it as:
serverMethods().SOMEAPIFUNCTION(par1,par2,.... callback)
If you have Server method defined in delphi code with for example 3 parameters in js 4th parameter is callback:
For this example code now look like this:
serverMethods().getChannel(i,function(a,b,c){
serial.$sensorlValue.text(a.result[0].fields.sensorString);
serial.$sensorlRealValue.text(a.result[0].fields.sensor);
serial.$sensorStatus.text(a.result[0].fields.sensorStatus+' '+s.result.fields.name);
serial[i].$sensorError.text(a.result[0].fields.sensorError);
serial[i].$AVString.text(a.result[0].fields.AVString);
serial[i].$AVError.text(a.result[0].fields.AVError);
});
a is JSON reponse
b is Request status as number 200 or somethin else
c is owner usuali undefined
What does the &callback parameter in a URL do? The reason I am asking is because I am running a query with jquery's $.ajax function and when I include &callback=? in the end of the url I get a 400 (bad request) error.
Here is my javascript code:
var energy_query_array = [];
var masterEnergyTable_tableid = '145TOXaanydD63tvgVMmCVH0hei0NXNCEK8ekZBg';
var current_date = 'Jan-12';
var energy_query = 'SELECT * FROM ' + masterEnergyTable_tableid;
var encoded_energy_query = encodeURIComponent(energy_query);
var energy_url = ['https://www.googleapis.com/fusiontables/v1/query'];
energy_url.push('?sql=' + encoded_energy_query);
energy_url.push('&key=' + apiKey);
//energy_url.push('&callback=?');
$.ajax({
url: energy_url.join(''),
datatype: 'jsonp',
success: function(data) {
var name_array = data['columns'];
var data_array = data['rows'][0];
for(var i = 1; i < data['columns'].length; i++) {
energy_query_array[name_array[i]] = data_array[i];
}
}
});
console.log(energy_query_array);
I have commented out the line where I add &callback=? to the url because when I leave it in the query returns the error:
"message":"Invalid value for parameter callback: ?"
However, earlier in the code I performed a similar query to google fusion tables using the same parameter for callback.
The console.log call at the end of the code displays an empty array to the console.
Callback is used for JSONP requests which is mean't to be used when doing a cross domain request to fetch JSON data. Instead of using the XHR wrapper, it uses a script tag which then passes a callback querystring parameter which points to the function that is used to execute passing in all the JSON data inside. This way you can have a datapoint which has no idea of how your application works, but when you pass in a callback then it will fire a function with the data. This allows you to use it's API from any browser.
Like any other piece of data in a query string (or anywhere else between the hostname and the fragment id), it provides some data to the server. The meaning is entirely determined by the software running on that server.
There is a convention to use callback as the key for a value that will be used as the name of the function to call in a JSON-P response in a RESTful API. JSON-P is a means to provide a web service accessible across origins for browsers which do not support CORS.
e.g.
http://example.com/foo?callback=asdf
would give you
asdf([1,2,3]);
JSON-P, obviously, requires that the server support the sending of JSON-P responses. If you try to perform a cross-origin request with jQuery it will (by default) modify the request on the assumption that the server will support JSON-P with the usual conventions.
Read about Query Strings
They start with a ?, and additional parameters get concatnated with &. The ? should never be anywhere but the beginning of the string unless it's encoded.
The key value pairs then are available to the server, as this is how a standard GET request is performed.
For example:
http://myserver.com?name=josh&age=24&state=il
Your web server is probably choking on the query string somewhere.
i am really banging my head here for more then a day, i am trying to send a request and get the response from another site. i'm doing it with jsonp (from the obvious reason). but the response is not a JavaScript function definition, so it keeps failing.
can anyone in this planet help me get the response the right way.
i attached the code i wrote, again: because the response is not in json it's not working. (try to run it yourself and you'll see).
any suggestions?
<script>
function test()
{
$.ajax({
dataType: 'jsonp',
jsonp: 'jsonp_callback',
url: 'https://www.facebook.com/ajax/typeahead/first_degree.php?viewer=1000009843914&token=1-1&filter[0]=user&options[0]=pending_request&lazy=1&token=v7&stale_ok=1&__a=1&__user=1000009843914& viewer=1000009843914',
});
}
function jsonp_callback(data)
{
var val=JSON.stringify(data);
myString = val.slice( 11 );
$('#container').html(myString);
/*for (;;);*/
}
test();
</script>
The server must be programmed to include the JSONP callback within its script file. If it only knows to return JSON, it will have no effect when the dynamic script tag is inserted into the page since JSON can at most provide an object--but it won't go anywhere unless the same file calls the function. In this way, it is different from Ajax, since a dynamically inserted script tag can only interact with your own code if it knows to call one of your functions. Just as an example, it might return:
jsonp_callback({facebooKData:[...]});
You should investigate how the Facebook API supports JSONP (not just JSON) for whatever you are trying to do. Typically APIs will accept a "callback" variable to determine which callback function it should use (which jQuery handles for you).
I'm writing some JavaScript/AJAX code.
Is there anyway to ensure that the server receives the XML requests in the order that they are sent?
If not with plain Ajax, do I get this guarantee if I send everything over a single WebSocket?
Thanks!
If it is of utmost importance that they're received in the proper order, and attaching an iterating id to the form isn't enough:
msg_number = 1; sendAJAX(msg_number); msg_number++;
Then I'd suggest building your own queue-system, and send each subsequent file as the callback of the previous one.
Rather than each element having its own AJAX-access, create one centralized spot in your application to handle that.
Your different AJAX-enabled sections don't even need to know that it is a queue:
AJAX.send({ url : "......", method : "post", success : func(){}, syncronous : true });
On the other side of that, you could have something like:
AJAX.send = function (obj) {
if (obj.synchronous) {
addToSyncQueue(obj); checkQueue();
} else { fireRequest(); }
};
Inside of your sync queue, all you'd need to do is wrap a new function around the old callback:
callback = (function (old_cb) {
return function (response) {
checkQueue();
old_cb(response);
};
}(obj.success));
obj.success = callback;
AJAX.call(obj);
Inside of checkQueue, you'd just need to see if it was empty, and if it wasn't, use
nextObj = queue.shift(); (if you're .push()-ing objects onto the queue -- so first-in, first-out, like you wanted).
A couple of options come to mind:
Send them synchronously, by waiting for a successful response from the server after each XML request is received (i.e. make a queue).
If you know the number of requests you'll be sending beforehand, send the request number as a tag with each request, e.g. <requestNum>1</requestNum><numRequests>5</numRequests>. This doesn't guarantee the order that they're received in, but guarantees that they can be put back in order afterwards, and has the added benefit of being sure that you have all the data.
At my company we use this little ajaxQueue plugin, written by one of the core jQuery contributors:
http://gnarf.net/2011/06/21/jquery-ajaxqueue/