Should I validate response data in jQuery's ajax success handler? - javascript

Let's assume I have a post AJAX call and I want to put returned data into some HTML elements.
$.post(settings.url, function(data) {
$('#someElement').text(data.someData1);
$('#someElement2').text(data.someData2);
});
I'm a back-end developer and it's natural for me that I have to do server-side validation of any piece of data coming from user. Although it's the opposite situation, the code above feels a little bit wrong for me (not validated outside data). But on the other hand, I know what I'm returning from the server.
The question is if is it fine to trust that data returned from (also mine) back-end application will have expected structure, or should I validate somehow every data coming server?
Additional question is if there is some nice method to do such validation? Manual validating of existence of every piece of data seems to be a pain in the neck. Especially for more complex data structure.
Just during writing this question an idea came to my mind. I could use $.extend() just like it's commonly used for setting default options while writing modules/plugins. Something like:
$.post(settings.url, function(data) {
var trustedStructure = $.extend({
someData1: $('#someElement').text(),
someData2: $('#someElement2').text(),
}, data);
$('#someElement').text(trustedStructure .someData1);
$('#someElement2').text(trustedStructure .someData2);
});
That way I could have trusted data with additionally current data as default or any other if I want.
Edit:
Forgot to note. I'm talking about pure JSON data responses. No HTML etc included.

Generally you don't validate the response data, as you said before, the data is returned from your own back-end. What you really need to do is to ensure that you have a proper way to handle exceptions or errors with the information coming from the server.
If you're returning an exception from the server you should have a way in the client-side to figure out that if an error or not.
i.e. returning a specific code like a Rest API or having a JSON structure like this:
// Success
{
"error": false,
"data": {
...
}
}
// Exception
{
"error": true,
"message": "Username already taken",
"type": "warning"
}
If you always return a 200 OK status code:
$.ajax({
...
success: function(response) {
if (response.error) {
alert(response.error.message);
} else {
document.querySelector('#field').value = response.data.text;
}
}
});
The HTML Response Codes are useful when you use promises, you can return a 200 OK for the primary flow (success, done), and 4XX or 5XX if something unusual happen (fail):
$.ajax({
url: 'example.php',
...
})
.done(function(response) { alert(response.data); })
.fail(function(error) { alert(error.message); })
.always(function() { clearFields(); });

Does the data returned from your server contain DOM Elements?
If it doesn't and is a pure text return, you can use a textarea to parse incoming data like this:
var textArea = document.createElement('textarea');
textArea.innerHTML = data;
data = textArea.textContent;
Just try it out and let the server send some <p>, <img> or <script> Elements

Related

How to get a JSON string result from a database for later use

I am working on the backend for a webpage that displays EPG information for TV channels from a SQlite3 database. The data is provided by a PHP script echoing a JSON string. This itself works, executing the php program manually creates a JSON string of this format
[{"id":"0001","name":"RTL","frequency":"626000000"},{"id":...
I want to use these objects later to create HTML elements but the ajax function to get the string doesn't work. I have looked at multiple examples and tutorials but they all seemed to be focused more on having PHP return self contained HTML elements. The relevant js on my page is this:
var channelList;
$(document).ready(function() {
$.ajax({
url: 'channellookup.php',
dataType: "json",
success: function(data) {
console.log(data.success);
channelList = data;
}
});
});
However the channelList variable remains empty when inspected via console.
What am I doing wrong?
Please ensure that your PHP echoing the correct type of content.
To echo the JSON, please add the content-type in response header.
<?php
header(‘Content-type:text/json’); // To ensure output json type.
echo $your_json;
?>
It's because the variable is empty when the program runs. It is only populated once AJAX runs, and isn't updating the DOM when the variable is updated. You should use a callback and pass in the data from success() and use it where you need to.
Wrap the AJAX call in a function with a callback argument. Something like this:
function getChannels(callback){
$.ajax({
url: 'channellookup.php',
dataType: "json",
success: function(data) {
console.log(data);
if (typeof(callback) === 'function') {
callback(data);
}
},
error: function(data) {
if (typeof(callback) === 'function') {
callback(data);
}
}
});
}
Then use it when it becomes available. You should also use error() to help debug and it will quickly tell you if the error is on the client or server. This is slightly verbose because I'm checking to make sure callback is a function, but it's good practice to always check and fail gracefully.
getChannels(function(channels){
$('.channelDiv').html(channels.name);
$('.channelDiv2').html(channels.someOtherProperty);
});
I didn't test this, but this is how the flow should go. This SO post may be helpful.
EDIT: This is why frameworks like Angular are great, because you can quickly set watchers that will handle updating for you.

AJAX and JSON error handling

I have a form which submits data via AJAX to an external server.
The data which gets sent is then validated and if correct the user can then advance onto the next step of the form.
If the data is not valid, then the server returns an error which is outputted as a JSON object.
I can see the JSON object in FIDDLER.
My aim is to grab that JSON data and output it on the page and notify the user.
Ideally, i would do this as part of an error handler on the AJAX request(found below).
Is this achievable?
PS:
Unfortunately, I can't set up a demo because the link that the data is posted to is only available on my network.
It is also worth pointing out that the error that the back-end script outputs is actually stored in the link that the data is posted to.
AJAX REQUEST:
var setUpVrmData = function() {
$("#getvrmdata").click(function () {
var p_vrm = $('#p_vrm').val();
$.ajax({
dataType: "JSON",
type: "POST",
url: "http://217.35.33.226:8888/l4_site/public/api/v1/BC8F9D383321AACD64C4BD638897A/vdata",
data: {
vrm: p_vrm,
},
success: function(data) {
//Empty the dropdown box first.
$("#p_model").empty();
appendString = "<option value='none'>-- Select your model --</option>";
$.each(data['vehiclemodel'], function (k, v) {
// += concatenate the string
appendString += "<option value='" + k + "'>" + v + "</option>";
});
$("#p_model, #ent_mileage").show();
$('.js-find-my-car').hide();
$('.js-get-price').show();
$("#p_model").append(appendString);
$("#p_model").prop("disabled", false);
$('#skey').val(data['skey']);
},
error: function() {
console.log("We return error!");
}
});
});
The Error function will return an XHR object that you may be able to parse to get the message you want. I don't know what is serving the data so depending on how that's setup your mileage may vary. I've done this using PHP as well as C# and writing to Console, but in both cases I was able to control the returned data.
I used this article : http://encosia.com/use-jquery-to-catch-and-display-aspnet-ajax-service-errors/ as a starting point.
You'll need to update:
error: function() {
console.log("We return error!");
}
to
error: function(xhr, status, error) {
console.log("We return error!");
}
Set a break point there in Firebug to check if an XHR object is passed, if not you'll need to find a way to get it.. You mention you can see the JSON in fiddler, it should be available to you. If it is, just use the eval posed in the article and you should be okay. If not you'll have to go and figure out how to get it, depending on your platform difficulty will vary.
A few things to note, eval is messy and can get you into trouble. In the cases I've done this, I removed the eval in production.
Also as of jQuery 1.8 success error and complete are deprecated. Use done fail and always if you plan on updating jQuery in the future.
jQuery API reference, for reference.
http://api.jquery.com/jquery.ajax/

jquery/ajax syntax help needed

I have two forms ('table' and 'fields'). The 'fields' form is supposed to pre-populate with options depending on the choice made in 'table', by making an Ajax request.
The data is returning perfectly and actually prepopulates the second form (like it should) if I pass a cut-and-paste example of some returned data to a local variable (see commented line).But for some reason it won't work on the returned object??
Any advice would be appreciated as I am very new to JavaScript and am probably missing something blatantly obvious! I am using the following code:
$(document).ready(function() {
$('select#table').change(function(){
$.getJSON("/ajax_get",{id: $(this).val(), ajax: 'true'}, function(data) {
//var data = [{"optionValue":"address", "optionDisplay": "address"},{"optionValue":"latitude", "optionDisplay": "latitude"},{"optionValue":"longitude", "optionDisplay": "longitude"},];
var $persons = $('#fields').empty();
$.each(data, function() {
$persons.append("<option value=" + this.optionValue + ">" + this.optionDisplay + "</option>");
});
});
});
});
Here's a simplified version of your call that should help you figure it out quickly:
$.getJSON("/ajax_get",{id: $(this).val(), ajax: 'true'}, function(data) {
try {
typeof(data.somethingYouExpect);
/* do your on success work here */
} catch (e) {
alert('There is a good chance the response was not JSON');
}
});
Even when using the regular jQuery $.ajax call, it's important to check to be sure the returned response is in the form you expect. This is as simple as setting a variable like success in your response as true. If you did that, the above example becomes something like this:
var jqxhr = $.getJSON("/ajax_get",{id: $(this).val(), ajax: 'true'}, function(data) {
try {
typeof(data.success); // Will throw if success is not present
if (success == true) {
/* handle success */
} else {
/* handle a request that worked, but the server said no */
}
} catch (e) {
/* The actual HTTP request worked, but rubbish was returned */
alert('There is a good chance the response was not JSON');
console.dir(jqxhr.textResponse);
}
});
Here, we remember the object returned by the $.getJSON call (which is just a shortcut to $.ajax), which allows us to view the actual response sent by the server. I'm willing to bet it's a 404, parser error or something of that sort.
For most things, I usually just use $.ajax mostly out of personal preference, where the error callback passes the xhr object to a common function to examine (did the request return 200? etc). If something explodes, I know exactly what went wrong by briefly looking at the console and can disable debug output in one place.

What is the best design pattern for asynchronous message passing in a Chrome extension?

I have a background script that is responsible for getting and setting data to a localStorage database. My content scripts must communicate with the background script to send and receive data.
Right now I send a JSON object to a function that contains the command and the data. So if I'm trying to add an object to the database Ill create JSON that has a command attribute that is addObject and another object that is the data. Once this is completed the background scripts sends a response back stating that it was successful.
Another use case of the function would be to ask for data in which case it would send an object back rather than a success/fail.
The code gets kind of hacky once I start trying to retrieve the returned object from the background script.
It seems like there is probably a simple design problem to follow here that I'm not familiar with. Some people have suggested future/promise design problems but I haven't found a very good example.
Content Script
function sendCommand(cmdJson){
chrome.extension.sendRequest(cmdJson, function(response){
//figure out what to do with response
});
}
Background script
if (request.command == "addObject"){
db[request.id]= JSON.stringify(request.data);
sendResponse("success");
}
else if(request.command == "getKeystroke"){
var keystroke = db[request.id];
sendResponse(keystroke);
}
Your system looks OK and here are some minor improvements.
For each remote command send back the same type of object (with possibly empty fields):
var response = {
success: true, // or false
data: {},
errors: [],
callback: ''
}
Also, if you have multiple different commands which send back data, you may replace if-else with an object lookup:
var commands = {
addObject: function () { /* ... */ },
getKeystroke: function (request, response) {
response.data = db[request.id]
}
}
Then if you have any data to response with, just add it to the object. And send the same object for any command:
var fn = commands[request.commands]
fn(request, response)
As for figuring out what to do with response, I'd pass a callback into the sendCommand function and let the content scripts request and process the response data as they see fit:
function sendCommand(cmdJson, callback){
chrome.extension.sendRequest(cmdJson, callback)
}

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.

Categories

Resources