Should be really simple Jquery jsonp - javascript

Why does this not work? anybody:
In my code I have:
$.getJSON("http://isp123.co.uk/cw/NorthWales/Test.txt?jsoncallback=?",
function(data){
//This never gets executed
alert('here');
});
The text file can be viewed here:
http://isp123.co.uk/cw/NorthWales/Test.txt

This is not a JSONP response:
({"name" : "hello world"});
If you had a proper JSONP response, then your code should work.
The question mark in the "callback=?" part of the URL is changed by jQuery before making the request, your JSONP server needs to be able to dynamically create the JSONP "function" in response to the unique jQuery request. If you can't dynamically create your JSONP, perhaps you could use YQL/Yahoo pipes to turn it into JSONP?
This pipe should do the trick, to see if it works, use this URL instead in your getJSON function: http://pipes.yahoo.com/pipes/pipe.run?u=http%3A%2F%2Fisp123.co.uk%2Fcw%2FNorthWales%2FTest.txt&_id=332d9216d8910ba39e6c2577fd321a6a&_render=json&_callback=?
I just tried this and it worked:
$.getJSON("http://pipes.yahoo.com/pipes/pipe.run?u=http%3A%2F%2Fisp123.co.uk%2Fcw%2FNorthWales%2FTest.txt&_id=332d9216d8910ba39e6c2577fd321a6a&_render=json&_callback=?", function(data){
//This always gets executed!!!
alert('here');
});

I don't know if you know enough about JSONP but this is not JSONP
?({"name" : "hello world"});
It really should be something like this http://isp123.co.uk/cw/NorthWales/Test.txt?jsoncallback=foo
foo({"name" : "hello world"});

From the jQuery.getJson manual page:
Important: As of jQuery 1.4, if the JSON file contains a syntax error, the request will usually fail silently. Avoid frequent hand-editing of JSON data for this reason. JSON is a data-interchange format with syntax rules that are stricter than those of JavaScript's object literal notation. For example, all strings represented in JSON, whether they are properties or values, must be enclosed in double-quotes. For details on the JSON format, see http://json.org/.
Your JSON is invalid according to http://jsonlint.com/

Here Clearly mentioned
As of jQuery 1.5, setting the jsonp
option to false prevents jQuery from
adding the "?callback" string to the
URL or attempting to use "=?" for
transformation. In this case, you
should also explicitly set the
jsonpCallback setting
and read jsonpCallback section
jsonpCallback,
Specify the callback function name for
a JSONP request. This value will be
used instead of the random name
automatically generated by jQuery. It
is preferable to let jQuery generate a
unique name as it'll make it easier to
manage the requests and provide
callbacks and error handling. You may
want to specify the callback when you
want to enable better browser caching
of GET requests. As of jQuery 1.5, you
can also use a function for this
setting, in which case the value of
jsonpCallback is set to the return
value of that function

Probably worth using jQuery.ajax() - http://api.jquery.com/jQuery.ajax/
You can pass in the dataType as "jsonp" and then jQuery takes care of all the callback business, but more importantly you can specify a function to run when there's an error, which may help you:
$.ajax({
dataType: "jsonp",
success: function(d) {console.log(d);},
error: function() { console.log("error") } //do your debugging in here
//add other parameters such as URL, etc
});
The error function you define can be passed 3 variables, read up on it on the ajax() page on the jQuery docs (linked at the beginning of my post) to find out more about that and how to use them.

Your problem lies with how your server is outputting the information. In the link you've supplied, the assumption is that any name placed in the ?jsonpcallback should result in wrapping the JSONP code in a function with that same name. It, however, is not the case.
So the next option is this: use a static function name in your server file and wrap the code. (e.g. use foo(<jsonp>) and stick with it) Then, you have to explicitly tell jQuery that we are going to use a specific function name (leave jQuery with the assumption it's supplying (and thus receiving) that name back, when in-fact you're just supplying it server side and filling in the blanks).
Once you have your file setup, use something like the following:
$.ajax({
// setup the request
url: 'http://isp123.co.uk/cw/NorthWales/Test.txt',
crossDomain: true,
dataType: 'jsonp',
jsonp: false,
jsonpCallback: 'foo', // "supply" the jsonp function (pseudo-defined)
// function to call when completed
complete: function(data){
alert(data);
}
// just in case, catch the error
error: function(j,t,e){
alert('AJAX Error');
}
});
So now when jQuery makes the call and it thinks it's supplying the callback, it's really just getting the server-defined callback in return. So, for the above to work, your text file should look something like this:
foo({name:"Hello, World!"});
Also, if you can, change your header to application/javascript, though this is some-what optional.

Related

Why can't I pass a json array as data when doing GET request with jquery ajax?

If I run a get request like this:
$.ajax({
url: 'http://localhost:8000/api/points/',
contentType:"application/json",
dataType: "json",
data: JSON.stringify({"content_type":content_type,"object_id":object_id}),
type: 'GET',
}).error(function(r){ $(output).text('error') })
.success(function(r){
$(output).text(r.count);
})
Its request goes to:
http://localhost:8000/api/points/?{%22content_type%22:8,%22object_id%22:40}
Obviously that's bad. It works okay if I don't do JSON.stringify(), but why is that?
Curiously if I do a POST request it's the opposite! I have to stringify the data or it won't work. So why the difference?
First of all let's fix your request:
var req = $.ajax({
method: "GET",
url: "http://localhost:8000/api/points/",
dataType: "json", // is you telling jQuery what kind of response to expect.
data : {id : '12345'}
});
req.done(function(data){
// do something with the data
});
req.fail(function(jqXHR, status, err){
// do something in case of failure
throw err;
});
Next get to know what you are dealing with :
data : PlainObject or String or Array
Data to be sent to the server. It is converted to a query string, if not already a string. It's appended to the url for GET-requests. By default, data passed in to the data option as an object (technically, anything other than a string) will be processed and transformed into a query string, fitting to the default content-type "application/x-www-form-urlencoded".
Note: The jqXHR.success(), jqXHR.error(), and jqXHR.complete() callbacks will be deprecated in jQuery 1.8. To prepare your code for their eventual removal, use jqXHR.done(), jqXHR.fail(), and jqXHR.always() instead.
At last:
No need for : JSON.stringify({"content_type":content_type,"object_id":object_id}) as it is a wrong way to do it with JSON.stringify just :
{ 'content_type' : 'type', 'object_id' : 'id' }
Urahara's answer contains some useful suggestions, but does not seem to address your question.
Well, it seems that your server side code is expecting to find a json object in the body of the POST request. Why would that be? Because you specify that the content type be "application/json". So in the case of a PUT case, when you use JSON.stringify your data in the $.ajax call is a string that happens to represent a JSON object. This is passed as is and so this works. You end up sending something like
{"content_type":8, "object_id":40}
as the body of your POST request and your server side code is happy to process this. Not sure exactly what your server-side technology is, but presumably it also binds content_type to 8 and object_id to 40, which makes you happy :-)
But when you do not use JSON.stringify to turn the object into a JSON string, you end up passing the object itself to $.ajax having it turn it into a string in the way it knows how. Well, it only knows only one: the good old url-encoding way. So the server, would still be expecting a JSON object but would instead be getting
content_type=8&object_id=40
as the body of the PUT request. It will not be happy. This is not a JSON object like your content-type promised! :-)
Now, moving on to the case of a GET request. Here the content-type is pretty much irrelevant as the message body will be empty. If you use JSON.stringify what you pass as the request data is a weird JSON-object representing string and the monstrosity that you get as a URL
http://localhost:8000/api/points/?{%22content_type%22:8,%22object_id%22:40}
is as you 'd expect not very well received by the server. What the server is happy with is something like
http://localhost:8000/api/points/?content_type=8&object_id=40
Which is exactly what $.ajax produces when you do not use JSON.stringify but rather pass it a nice pair of attribute-value pairs. It just url-encodes them, exactly like it did in the PUT case, and the server is happy.
So $.ajax always url-encodes objects consisting of attribute-value pairs. This is fine in most cases. One way to fix your code to do a POST without using JSON.stringify would be to simply not put a content type parameter in the $.ajax call (in which case the default, 'application/x-www-form-urlencoded; charset=UTF-8' will be used).
But if you want to pass more complex, deeply hierarchical objects, using some object serialisation format you have to set the appropriate content type and pass the data as a string using the corresponding encoding (JSON, XML, etc.) like you did here.
Hope that answers your question :-)
Please also have a look at http://api.jquery.com/jquery.ajax/

Synchronous jQuery calls, without putting everything in the callback

Is it possible to get a JSON in jQuery synchronously without using the async: false option (which apparently has been deprecated)? I also would prefer not to put everything into the success function.
No, there is no other option than async: false. I wouldn't recommend it if there were.
For the success function, you can simply pass another function that will be executed.
For example:
$.ajax( {
url: myUrl,
type: 'POST',
success: myFunc
} );
function myFunc( datas ) {
// do what you should do in a success function
}
Since ajax methods on jQuery return a Deferred Object (jQuery 1.5+) you could also write
$.ajax({
url: myUrl,
type: 'POST'
}).done(function() {
// do what you should do in a success function
});
Not reasonably. Trying to force the request to be Synchronous without using asyc:false is possible, but a waste of effort in my opinion. In pretty much every scenario you want to use async:true, you will be able to accomplish all of the same things with async:true as you can with async:false. It's just a matter of how you structure your code.
If you look at the current version of the source file that executes the AJAX request:
https://github.com/jquery/jquery/blob/master/src/ajax/xhr.js
You will see it still uses the async field on the settings object, this field is passed directly into the Open method of the XMLHttpRequest object. So using the default implementation all you can do, is set "async: false".
if ( s.username ) {
xhr.open( s.type, s.url, s.async, s.username, s.password );
} else {
xhr.open( s.type, s.url, s.async );
}
Now assuming you are really stubborn and want to do this without setting "async:false", you could get really bold and write a custom ajaxTransport and register it with a custom data type.
Here is an example I wrote that creates a custom transport object with a send and abort method, and registers it with the dataType 'mine'. So when you specify dataType 'mine' in your ajaxSettings object it will use this custom transport instead of the one built into jQuery. http://jsfiddle.net/xrzc7/ Notice there are two ajax requests one with the 'mine' dataType that show the alert, and one without the data type that does not show the alert. My ajaxTransport in this example isn't fully functional, its just to illustrate that you can swap in your own send function.
I wouldn't advise writing your own ajaxTransport for jQuery because it really isn't neccessary in my opinion, but to answer your question I'm suggesting it as an option.
You should pay close attention to how the default ajaxTransport in jquery is written, and how it interacts with the settings object and callback methods when writing your custom ajaxTransport. Your send function would obviously force a "false" value as the async parameter of the XMLHttpRequest.open method.
https://github.com/jquery/jquery/blob/master/src/ajax/xhr.js - this is the current source code for the default ajaxTransport mechanism.
Hope this helps, or perhaps persuades you to always use async:true. :-D

Why is 'jQuery.parseJSON' not necessary?

i'm doing an ajax request with query and wondering why my response is already a JS object.
If i do a
var obj = jQuery.parseJSON(response);
'obj' is null, but i can use 'response' as an array of js objects.
This is not really a problem, but i would like to understand this behavior.
Thanks
This happens when you make an AJAX call and specify the dataType JSON jQuery calls jQuery.parseJSON on the response for you. In fact you can specify what function to call depending on the dataType as you can se from the documentation
converters(added 1.5)
Map Default: {"* text": window.String, "text
html": true, "text json": jQuery.parseJSON, "text xml":
jQuery.parseXML} A map of dataType-to-dataType converters. Each
converter's value is a function that returns the transformed value of
the response
So if you make a call like this
$.ajax({
url: yoururl,
dataType: "json",
success: function(data){
//data is already a json
}
If you don't specify a dataType jQuery tries to guess it
dataTypeString Default: Intelligent Guess (xml, json, script, or
html)
The type of data that you're expecting back from the server. If none
is specified, jQuery will try to infer it based on the MIME type of
the response (an XML MIME type will yield XML, in 1.4 JSON will yield
a JavaScript object, in 1.4 script will execute the script, and
anything else will be returned as a string). The available types (and
the result passed as the first argument to your success callback) are:
"xml": Returns a XML document that can be processed via jQuery.
"html": Returns HTML as plain text; included script tags are evaluated
when inserted in the DOM. "script": Evaluates the response as
JavaScript and returns it as plain text. Disables caching by appending
a query string parameter, "=[TIMESTAMP]", to the URL unless the cache
option is set to true. Note: This will turn POSTs into GETs for
remote-domain requests. "json": Evaluates the response as JSON and
returns a JavaScript object. In jQuery 1.4 the JSON data is parsed in
a strict manner; any malformed JSON is rejected and a parse error is
thrown. (See json.org for more information on proper JSON formatting.)
"jsonp": Loads in a JSON block using JSONP. Adds an extra
"?callback=?" to the end of your URL to specify the callback. Disables
caching by appending a query string parameter, "=[TIMESTAMP]", to the
URL unless the cache option is set to true.
"text": A plain text
string. multiple, space-separated values:
As of jQuery 1.5, jQuery can
convert a dataType from what it received in the Content-Type header to
what you require. For example, if you want a text response to be
treated as XML, use "text xml" for the dataType. You can also make a
JSONP request, have it received as text, and interpreted by jQuery as
XML: "jsonp text xml." Similarly, a shorthand string such as "jsonp
xml" will first attempt to convert from jsonp to xml, and, failing
that, convert from jsonp to text, and then from text to xml.
It pretty much depends which dataType you pass into your jQuery ajax request. This might happen implict by calling .getJSON() or directly using $.ajax().
However, if you omit the dataType, jQuery trys to do some magic and guesses which data was received. As for JSON data, it uses a simple regular expression to check if a response looks like a JSON-string and if so, it automatically parses it for you.
jQuery will try to infer it based on the MIME type of the response.
So always be precise and tell jQuery which type of data you expect.
The default behaviour of jQuery's ajax method is to analyse the response and return it as the most appropriate data type. If your response looks like JSON, therefore, it will be converted to a JavaScript object/array.
You can override this behaviour by setting the dataType attribute in the ajax settings.
if you specify the dataType as json the jquery parses the response for you like
$.ajax({
...
dataType:'json',
...
});
same is the case with jQuery.getJSON()
this is how the source code for getJSON looks like
getJSON: function( url, data, callback ) {
return jQuery.get( url, data, callback, "json" );
},
https://github.com/jquery/jquery/blob/master/src/ajax.js#L283
Because
jQuery.ajaxSettings.converters["text json"] === jQuery.parseJSON
I.E it will run the function everytime json response is detected automatically or explicitly set by yourself

prevent jquery ajax to execute javascript from script or html response

According to documentation:
If html is specified, any embedded JavaScript inside the retrieved
data is executed before the HTML is returned as a string. Similarly,
script will execute the JavaScript that is pulled back from the
server, then return nothing.
How to prevent this?
I have js that shall modify the content that is obtained through ajax. Executing it before the html is returned makes no sense as it does not have content to work on (at least in my case).
my code:
function do_ajax(url) {
$.ajax({
cache: false,
url : url,
success: function(response, status, xhr) {
var ct = xhr.getResponseHeader("content-type") || "";
if (ct.indexOf('script') > -1) {
try {
eval(response);
}
catch(error) {}
} else {
var edit_dialog = $('<div class="edit_dialog" style="display:hidden"></div>').appendTo('body');
edit_dialog.html(response);
edit_dialog.dialog({ modal:true, close: function(event, ui) { $(this).dialog('destroy').remove(); } });
}
},
error: function (xhr, ajaxOptions, thrownError) {
alert(xhr.status);
alert(thrownError);
}
});
}
the script received by ajax is executed twice. First by me in the eval(response), then jquery execute it again (as described in the documentation)
Lee's answer already adequately addresses the case of HTML responses - scripts embedded in these are not in fact executed automatically unless you add the HTML to the DOM, contrary to the erroneous documentation you quoted.
That leaves the other case asked about in your question title - preventing script responses from being executed automatically when received. You can do this easily using the dataType setting.
$.ajax('myscript.js', {
dataType: 'text',
success: function (response) {
// Do something with the response
}
})
Setting dataType to 'text' will cause jQuery to disregard the Content-Type header returned from the server and treat the response like plain text, thus preventing the default behaviour for JavaScript responses (which is to execute them). From the (recently corrected) docs:
The type of pre-processing depends by default upon the Content-Type of the response, but can be set explicitly using the dataType option. If the dataType option is provided, the Content-Type header of the response will be disregarded.
...
If text or html is specified, no pre-processing occurs. The data is simply passed on to the success handler, and made available through the responseText property of the jqXHR object.
jQuery.ajax does not evaluate scripts on return when requesting HTML. The passage you quoted in the question was in fact a long-standing error in the documentation, fixed as of April 2014. The new docs have this to say (emphasis mine):
"html": Returns HTML as plain text; included script tags are evaluated when inserted in the DOM.
...
If text or html is specified, no pre-processing occurs. The data is simply passed on to the success handler, and made available through the responseText property of the jqXHR object.
The scripts are evaluated in this case when you call
edit_dialog.html(response);
If you don't want to evaluate the scripts before inserting your response in to the DOM, you should be able to do something like:
edit_dialog.html($($.parseHTML(response)));
parseHTML is the key in that by default it removes script tags. However, be aware that parseHTML is NOT XXS safe and if your source is unknown this is still a security concern.
The documentation states that any embedded Javascript inside the retrieved data will be executed before the HTML is returned as a string. If you want to then alter whatever you have retrieved using your ajax call, you can do so within the succes property:
$.ajax({
url: "example.html",
type: "GET",
dataType: "html",
succes: function(data){
// Example: alert(data);
// Do whatever you want with the returned data using JS or jQuery methods
}
});
That's one of the really annoying things about jQuery that it executes javascript on response.
Other frameworks like MooTools disable script execution from responses unless you specifically set that you want them executed which is a much better approach.
The only way I could figure to prevent scripts being executed is to add a custom dataFilter
Its easy enough but I think it should be default and an ajax option to enable script execution if you want it (I've never had a use for it and other frameworks disable by default for security etc.)
Example
$.ajax('uri',{
dataFilter: function(data, type)
{
type = type || 'text';
if(type=='html'||type=='text'){
/*return data.replace(/<script.*>.*?<\/script>/gi, '');*/
return data.replace(/<script.*?>([\w\W\d\D\s\S\0\n\f\r\t\v\b\B]*?)<\/script>/gi, '');
}
return data;
}
, success: function(data)
{
// whatever
}
});
** UPDATED **
Needs that crazy regex to cover more script tag instances
NOTE
if dataType hasnt been set in options it will be undefined in dataFilter so I just default it to text for the filter - if you remove that line then it will only work if dataType is explicitly set.

jQuery: getJSON + SunlightLabs API help requested

I'm having trouble pulling a specific element from an API call using jQuery's getJSON function. I'm trying to use SunlightLab's congress API to pull specific info about legislators. In the example below I'm trying to pull a legislator's website:
$.getJSON("http://services.sunlightlabs.com/api/legislators.get.json?apikey=[api key]&lastname=Weiner&jsonp=?" , function(data) {
alert("hello from sunlight");
alert(data.response.legislator.website);
});
Using the above code, the first alert shows up but the second alert does not even occur. I understand that getJSON should be using JSONP in this instance, and I think I have that set up correctly, ending my URL with '&jsonp=?'.
Putting the URL in my getJSON function into a web browser gives me this:
?({"response": {"legislator":
{"website":
"http://weiner.house.gov/", "fax":
"202-226-7253", ... etc.
I'm a little thrown by the '?' showing up at the beginning of this, but if the first alert is showing up then the request must be succeeding...
The URL you're using is setting the JSONP callback to be equal to ?, which means its injecting a JavaScript function called ? with an argument of a JavaScript object. This is invalid. So, the request is succeeding, but the wrapper function you've defined isn't being called (and isn't valid).
You could change the URL so that its jsonp=callback (or some other handler function name), and then define a function called callback that handles an argument that expects the object.
One way to (automatically) trigger JSONP support in jQuery is to switch the key to be called 'callback' so that it signals to jQuery that you're doing a JSONP call. ie, callback=callback.
EDIT: As Drackir points out, jQuery provides a setting in $.ajax for letting it define it's own callback function name, but you need to signal to it that its a JSONP call by setting dataType: 'jsonp' in the $.ajax call.
The question mark is there because you specified the JSONP callback function to be ? in your query string (ie. &jsonp=?). Due to security concerns (specifically the same-origin policy) you cannot do an AJAX request to a site outside the same domain as the page you're on. To get around this, JSONP works by creating a script tag, with the SRC set to the URL of the script on the other site. This will load the external JavaScript file and run whatever code is there. Now, in order to link that external code with your JavaScript, the external API takes the name of a function to call (&jsonp=functionnametocall). The returned JavaScript calls that function and passes in the data it's trying to return as a JSON object as the first argument.
So, the reason you see the question mark when you go there is because you're passing a question mark to the jsonp query string parameter. jQuery will automatically convert the question mark in a url such as http://www.test.com/api/apikey=292929&callback=? to a uniquely named function. This is handled in the background by jQuery so you don't have to think about it.
Now, that said, I don't know if jQuery detects if the name of the callback parameter as being something other than callback=?. $.getJSON() however is a short form for the longer:
$.ajax({
url: url,
dataType: 'json',
data: data,
success: callback
});
I suggest you try using $.ajax() directly and set the jsonp setting equal to "jsonp". This tells the $.ajax() method that the query string parameter is called jsonp and not callback. So like this essentially:
$.ajax({
url: url,
dataType: 'json',
data: data,
success: callback,
jsonp:"jsonp"
});
More information: $.getJSON | $.ajax()
OK, OK, so it was a rather simple fix, adding a line to the the example given by #Drackir. The missing piece was 'cache: true' within the ajax settings. The final working code looked like this:
$.ajax({
url: 'http://services.sunlightlabs.com/api/legislators.get.json?apikey=[api key]7&lastname=Weiner',
dataType: 'jsonp',
cache: true,
jsonp: 'jsonp',
success: function(data) {
alert("hello from ajax") ;
alert(data.response.legislator.website);
}
});
I'm not sure why 'cache: true' is needed in this case. Thanks for the help.

Categories

Resources