MockJax is not sending response to my AJAX request in JavaScript application - javascript

I am using a jQuery library called MockAjax which allows you to mock/test real AJAX calls.
In my application I can use my live app version of an AJAX request and MockAjax will intercept the AJAX request and respond with a Mock response!
I am also using another library called M<ockJson which is similar but instead allows you to generate a Mock JSON response.
Using both libraries together, my Application makes an AJAX request. MockAjax catches the AJAX request and then MockJson generates and returns a random JSON response.
In my past projects this has worked great with no issues until today...
Now that my application is working pretty good, I decided it;s time to restructure the JavaScript into a more structured version. (putting DOM events, tasks, etc into sections of code).
This is where my problem began....
In my new code,
my App makes an AJAX request.
MockAjax catches the AJAX request.
MockJson is called to get the JSON response
ERRORS this is where it all goes wrong...
At this last step, it should pass the JSON response back to the original AJAX calls Success function. It simply does not!
I get no errors or anything in the console. I set up a simple alert() in my AJAX calls success() function and it does not make it that far to even trigger the alert!
I am not sure if there is some sort of scope issue or what the problem could be. When my app was flat, all variables and functions in the glbal root level with no structure to the app at all...it all worked. As soon as I moved everything into Objects, etc....it all works except this 1 issue of not returning the MockAjax response back to the Real Ajax response!
I removed 95% of the app code and re-built it with just the very minimal to run this example problem. The demo of the problem is here... http://jsbin.com/vugeki/1/edit?js
App flow:
projectTaskModal.init(); is ran on page load
This fires off projectTaskModal.mockAjax.init(); which sets up the MockAjax and MockJson code
Then projectTaskModal.task.openTaskModal(projectTaskModal.cache.taskId); is ran which executes the AJAX request
AJAX POST Request is sent to /gettaskevents
MockAjax catches the request sent to /gettaskevents
MockAjax then calls MockJson to generate the JSON response. projectTaskModal.mockAjax.generateTaskEventsJson(); calls that function and I have it printing the JSON respionse into the console so I can see that it is generating it!
In my MockAjax code, var taskevents holds the JSON response and then set it to this... this.responseText = taskevents; ``this.responseTextI believe is what is supposed to be returned to the Applications originalAJAX` call. It seems that this is where the problem might be! It does not seem to be returning the response back to the original AJAX code that requested it in the first place!
Could this be some sort of scope issue?
var projectTaskModal = {
cache: {
taskId: 1,
projectId: '12345',
},
init: function() {
projectTaskModal.mockAjax.init();
//console.log(projectTaskModal.mockAjax.init.generateTaskEventsJson());
projectTaskModal.task.openTaskModal(projectTaskModal.cache.taskId);
},
task: {
openTaskModal: function(taskId) {
// Load Task Events/Comments Panel from AJAX Request
projectTaskModal.task.loadTaskEventsPanel(taskId);
},
/**
* Load Task Events/Comments from backend Database JSON
* #param {string} jsonServerEndpoint URL for AJAX to Request
* #return {string} Generated HTML of all Task Events built from JSON
*/
loadTaskEventsPanel: function(taskId) {
// Request Task Events and Comments using AJAX Request to Server
$.ajax({
type: 'POST',
contentType: 'application/json; charset=utf-8',
dataType: 'json',
url: '/gettaskevents',
data: {
action: 'load-task-events',
task_id: projectTaskModal.cache.taskId
},
success: function(data) {
alert('TESTING AJAX SUCCESS CALLBACK WAS CALLED!');
console.log('function loadTaskEventsPanel(taskId) DATA: ');
console.log(data);
// Parse JSON data
var taskEventsJson = data;
var task_events = taskEventsJson.task_events;
// Loop over each Task Event record returned
$.each(task_events, function(i, event) {
console.log('commentID: ' + event.commentId);
console.log('create_at DateTime: ' + event.created_at);
});
}
});
},
},
mockAjax: {
init: function(){
// Adding the #EVENT_TYPE keyword for MockJSON Template Usage
$.mockJSON.data.EVENT_TYPE = [
'Comment Created',
'Task Status Changed',
'Task Completed'
];
// Mock AJAX response for AJAX request to /gettaskevents
$.mockjax({
url: '/gettaskevents',
contentType: 'text/json',
responseTime: 2900, // Simulate a network latency of 750ms
response: function(settings) {
console.log('mockJax POST to /gettaskevents :');
//console.log(settings);
//DEBUG('Get Task Events JSON', settings.data);
if(settings.data.value == 'err') {
alert('MockAjax Error');
this.status = 500;
this.responseText = 'Validation error!';
} else {
alert('MockAjax Success');
//var taskevents = generateTaskEventsJson();
var taskevents = projectTaskModal.mockAjax.generateTaskEventsJson();
this.responseText = taskevents;
console.log(taskevents);
}
}
});
},
// Generate Mock JSON Response to load fake Task Evewnt/Comments JSON for Mock AJAX request
//var generateTaskEventsJson = function () {
generateTaskEventsJson: function() {
var mockTaskJson = $.mockJSON.generateFromTemplate({
"task_events|10-14" : [{
"commentId|+1" : 100000000,
"projectId|+1" : 100000000,
"taskId|+1" : 100000000,
"userId|+1" : 100000000,
"created_at" : "#DATE_YYYY-#DATE_MM-#DATE_DD",
"event_type" : "#EVENT_TYPE",
"userName" : "#MALE_FIRST_NAME #LAST_NAME",
"description" : "#LOREM_IPSUM #LOREM_IPSUM"
}]
});
//DEBUG('Generate Mock Task Events JSON', mockTaskJson.task_events);
//console.log(mockTaskJson.task_events);
//return mockTaskJson.task_events;
return mockTaskJson;
}
},
};
$(function() {
projectTaskModal.init();
});

Your JSBin example shows that you are using a very old version of Mockjax (1.5.0-pre). The latest is 1.6.2, released quite recently (I'm the core maintainer now). Below is a link to an updated JSBin where everything appears to be working just fine. The old version of Mockjax that you were running was created before jQuery 2.0 existed, and thus does not support it (1.6.2 does).
http://jsbin.com/qucudeleve/1/
So... update your Mockjax version to use Bower, npm, or just Rawgit (https://rawgit.com/jakerella/jquery-mockjax/master/jquery.mockjax.js) from the primary account (mine) versus your own fork which is extremely out of date!
Good luck.

Related

jQuery is not calling .done() when the ajax call is successful

We're building a node.js application using Express, but are separating our layers. So the browser is running pure jQuery and javascript, the web server and application server are Node.js and Express. We're using REST APIs between them all.
We're using jQuery 1.10.2
Since the application server cannot be open to the public, the browser must make API calls to the web server, which manages making the call to the application server and returning the results. Here is what that looks like...
// Proxy all other API calls to the backend server
var request = require('request');
app.all('/api/*', function (req, res) {
var targeturl = apihost+req.originalUrl;
console.log("Proxy: "+targeturl);
request({
url: targeturl,
method: req.method,
json: req.body
}, function (error, response, data) {
if(error) {
res.send(error);
res.end();
} else {
console.log("SUCCESS...");
console.log(data);
res.send(data);
res.end();
}
});
});
Our data is a two-level hierarchy of Flows and one or more child Milestones on each Flow. When someone clicks the Save link on our page to save a Flow and its Milestones, the Javascript pulls the Flow data from the page, then makes an AJAX call to save the Flow information first, then waits for the "done()" handler before pulling the Milestone information from the page and making a second AJAX call to save that data. Here is the saveFlow() function executed in the browser...
function saveFlow(organization_id, user_id, flow_id) {
var name = $('#Name').val();
var purpose = $('#Purpose').val();
console.log("Trying to save flow: " + flow_id);
if (name && name.length > 0) {
$.ajax({
url: "/api/flow",
type: 'POST',
datatype: 'json',
data: {
flow_id: flow_id,
name: name,
purpose: purpose,
organization_id: organization_id,
user_id: user_id,
timestamp: (new Date())
}
})
.fail(function (error) {
console.log("Could not save the flow: " + error.message);
})
.done(function (flow) {
console.log("Saving milestones for flow "+flow.id+" ["+flow_id+"]");
saveMilestones(flow.id);
});
} else {
console.log("Refusing to save a flow that has no name");
}
}
The proxy code shows me that this call succeeded...
SUCCESS...
{ success: true,
message: 'Flow Updated!',
id: '56de8e346d229b492a0954f9' }
But the proxy code also demonstrates that the next API call never takes place. Moreover, the log statement in the Done block is never executed, nor are any log statements placed inside the saveMilestones() function.
Clearly, the .done() handler is never being called in this case. I use the done() handler in other parts of my code and it works. I've compared the code but don't see any differences. The proxy code we're using is driving every other API call made from our pages successfully, so I don't think that it's failing to return the success status.
I've done a lot of searching on this site and tried every suggestion I could find - adding a explicit "json" datatype parameter, switching from ":success" to ".done()" - but nothing has worked yet. I even tried switching from done() to always() - a bad idea but I wanted to see if it would get called...and it wasn't called.
It's also worth noting that the .fail() handler is also not being executed.
Is it possible that the JSON block we're sending back to indicate success is somehow failing to make jQuery realize that we were successful? Or is it possible that the AJAX call is crashing when it tries to process the successful return? If so, how do I catch that to prove it and fix it?
Hope someone can help.

Dojo tree - delay until server returns data

I have a server function that generates JSON representing part of the file system.
The server function is called once the user has selected an item from a pull-down list.
So far so good.
My question is how do I display the tree ONLY when the JSON data has been returned from the server? Please make your answer as verbose and complete as possible as I'm not a javascript pro by any means!
var serverFunctionComplete = false;
var x = serverFunction();
while(!serverFunctionComplete) {
//just waiting
}
setTimeout(function() {
serverFunctionComplete = true;//if the server doesn't respond
}, 5000);
This should get you started.
You could make ajax request synchronous using the sync : true property in the xhr object. This stops other code from executing until the response is recieved from the server and the callback is done executing.
require(["dojo/request/xhr"], function(xhr){
xhr("example.json", {
handleAs: "json",
sync: true
}).then(function(data){
// Do something with the handled data
}, function(err){
// Handle the error condition
}, function(evt){
// Handle a progress event from the request if the
// browser supports XHR2
});
});
However, this is typically not the best practice as asynchronous loading as one of the great things about javascript and ajax. It would be reccomended to do display your tree in the callback function in the xhr so your script does not get hung up polling for a response.
require(["dojo/request/xhr"], function(xhr){
xhr("example.json", {
handleAs: "json"
}).then(function(data){
// Display your Tree!
}, function(err){
// Handle the error condition
});
});
For general asynchronous thread management, refer to Dojo's Deffered class.

Adding HTTP Basic Authentication Header to Backbone.js Sync Function Prevents Model from Being Updated on Save()

I'm working on a web application that is powered by a restful API written with Python's CherryPy framework. I started out writing the user interface with a combination of jQuery and server side templates, but eventually switched to Backbone.js because the jQuery was getting out of hand.
Unfortunately, I'm having some problems getting my models to sync with the server. Here's a quick example from my code:
$(function() {
var User = Backbone.Model.extend({
defaults: {
id: null,
username: null,
token: null,
token_expires: null,
created: null
},
url: function() {
return '/api/users';
},
parse: function(response, options) {
console.log(response.id);
console.log(response.username);
console.log(response.token);
console.log(response.created);
return response;
}
});
var u = new User();
u.save({'username':'asdf', 'token':'asdf'}, {
wait: true,
success: function(model, response) {
console.log(model.get('id'));
console.log(model.get('username'));
console.log(model.get('token'));
console.log(model.get('created'));
}
});
});
As you can probably tell, the idea here is to register a new user with the service. When I call u.save();, Backbone does indeed send a POST request to the server. Here are the relevant bits:
Request:
Request URL: http://localhost:8080/api/users
Request Method: POST
Request Body: {"username":"asdf","token":"asdf","id":null,"token_expires":null,"created":null}
Response:
Status Code: HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 109
Response Body: {"username": "asdf", "created": "2013-02-07T13:11:09.811507", "token": null, "id": 14, "token_expires": null}
As you can see, the server successfully processes the request and sends back an id and a value for created. But for some reason, when my code calls console.log(u.id);, I get null, and when my code calls console.log(u.created);, I get undefined.
tl;dr: Why isn't Backbone.js persisting changes to my objects after a call to save()?
Edit:
I've modified the above code so that the model properties are accessed using the get function in a success callback. This should solve any concurrency problems with the original code.
I've also added some console logging in the model's parse function. Oddly enough, each of these is undefined... Does that mean that Backbone.js is failing to parse my response JSON?
Edit 2:
A few days ago, I found out that issue was actually a custom header that I was adding to every request to enable HTTP Basic Authentication. See this answer for details.
This code:
u.save();
console.log(u.id);
console.log(u.username);
console.log(u.token);
console.log(u.created);
Runs immediately... after that there is nothing to run and the queued ajax request begins. The response then comes a bit later and only at that point have the values changed.
It also seems that those properties are not directly on the object, but the asynchronous processing of the save still holds in that you wouldn't get expected results even if you corrected that code to have console.log(u.get("id")) etc.
I figured out the issue, although I'm still at a loss to explain why it's an issue at all. The web app that I'm building has an authentication process that requires an HTTP basic Authentication header to be passed with all requests.
In order to make this work with Backbone, I overrode the Backbone.sync function and changed line 1398 to add the header.
Original Code:
var params = {type: type, dataType: 'json'};
Modified Code:
var params = {
type: type,
dataType: 'json',
headers: {
'Authorization': getAuthHash()
}
};
The function getAuthHash() just returns a Base64 string that represents the appropriate authentication information.
For some reason, the addition of this header makes the sync/save functions fail. If I take it out, everything works as you might expect it to.
The bounty is still open, and I'll happily reward it to anybody who can explain why this is, as well as provide a solution to the problem.
Edit:
It looks like the problem was the way that I was adding the header to the request. There's a nice little JavaScript library available on Github that solves this problem by correctly adding the HTTP Basic Auth header.
i have tested your code, its works fine for me.
See Demo here, jsfiddle.net/yxkUD/
Try to add a custom beforeSend method to the ajax request to add the custom header.
For example:
https://github.com/documentcloud/backbone/blob/master/backbone.js#L1424

AD FS 2.0 Authentication and AJAX

I have a web site that is trying to call an MVC controller action on another web site. These sites are both setup as relying party trusts in AD FS 2.0. Everything authenticates and works fine when opening pages in the browser window between the two sites. However, when trying to call a controller action from JavaScript using the jQuery AJAX method it always fails. Here is a code snippet of what I'm trying to do...
$.ajax({
url: "relyingPartySite/Controller/Action",
data: { foobar },
dataType: "json",
type: "POST",
async: false,
cache: false,
success: function (data) {
// do something here
},
error: function (data, status) {
alert(status);
}
});
The issue is that AD FS uses JavaScript to post a hidden html form to the relying party.
When tracing with Fiddler I can see it get to the AD FS site and return this html form which should post and redirect to the controller action authenticated. The problem is this form is coming back as the result of the ajax request and obviously going to fail with a parser error since the ajax request expects json from the controller action. It seems like this would be a common scenario, so what is the proper way to communicate with AD FS from AJAX and handle this redirection?
You have two options.
More info here.
The first is to share a session cookie between an entry application (one that is HTML based) and your API solutions. You configure both applications to use the same WIF cookie. This only works if both applications are on the same root domain.
See the above post or this stackoverflow question.
The other option is to disable the passiveRedirect for AJAX requests (as Gutek's answer). This will return a http status code of 401 which you can handle in Javascript.
When you detect the 401, you load a dummy page (or a "Authenticating" dialog which could double as a login dialog if credentials need to be given again) in an iFrame. When the iFrame has completed you then attempt the call again. This time the session cookie will be present on the call and it should succeed.
//Requires Jquery 1.9+
var webAPIHtmlPage = "http://webapi.somedomain/preauth.html"
function authenticate() {
return $.Deferred(function (d) {
//Potentially could make this into a little popup layer
//that shows we are authenticating, and allows for re-authentication if needed
var iFrame = $("<iframe></iframe>");
iFrame.hide();
iFrame.appendTo("body");
iFrame.attr('src', webAPIHtmlPage);
iFrame.load(function () {
iFrame.remove();
d.resolve();
});
});
};
function makeCall() {
return $.getJSON(uri)
.then(function(data) {
return $.Deferred(function(d) { d.resolve(data); });
},
function(error) {
if (error.status == 401) {
//Authenticating,
//TODO:should add a check to prevnet infinite loop
return authenticate().then(function() {
//Making the call again
return makeCall();
});
} else {
return $.Deferred(function(d) {
d.reject(error);
});
}
});
}
If you do not want to receive HTML with the link you can handle AuthorizationFailed on WSFederationAuthenticationModule and set RedirectToIdentityProvider to false on Ajax calls only.
for example:
FederatedAuthentication.WSFederationAuthenticationModule.AuthorizationFailed += (sender, e) =>
{
if (Context.Request.RequestContext.HttpContext.Request.IsAjaxRequest())
{
e.RedirectToIdentityProvider = false;
}
};
This with Authorize attribute will return you status code 401 and if you want to have something different, then you can implement own Authorize attribute and write special code on Ajax Request.
In the project which I currently work with, we had the same issue with SAML token expiration on the clientside and causing issues with ajax calls. In our particular case we needed all requests to be enqueud after the first 401 is encountered and after successful authentication all of them could be resent. The authentication uses the iframe solution suggested by Adam Mills, but also goes a little further in case user credentials need to be entered, which is done by displaying a dialog informing the user to login on an external view (since ADFS does not allow displaying login page in an iframe atleast not default configuration) during which waiting request are waiting to be finished but the user needs to login on from an external page. The waiting requests can also be rejected if user chooses to Cancel and in those cases jquery error will be called for each request.
Here's a link to a gist with the example code:
https://gist.github.com/kavhad/bb0d8e4a446496a6c05a
Note my code is based on usage of jquery for handling all ajax request. If your ajax request are being handled by vanilla javascript, other libraries or frameworks then you can perhaps find some inspiration in this example. The usage of jquery ui is only because of the dialog and stands for a small portion of the code which could easly be swapped out.
Update
Sorry I changed my github account name and that's why link did not work. It should work now.
First of all you say you are trying to make an ajax call to another website, does your call conforms to same origin policy of web browsers? If it does then you are expecting html as a response from your server, changedatatype of the ajax call to dataType: "html", then insert the form into your DOM.
Perhaps the 2 first posts of this serie will help you. They consider ADFS and AJAX requests
What I think I would try to do is to see why the authentication cookies are not transmitted through ajax, and find a mean to send them with my request. Or wrap the ajax call in a function that pre authenticate by retrieving the html form, appending it hidden to the DOM, submitting it (it will hopefully set the good cookies) then send the appropriate request you wanted to send originally
You can do only this type of datatype
"xml": Treat the response as an XML document that can be processed via jQuery.
"html": Treat the response as HTML (plain text); included script tags are evaluated.
"script": Evaluates the response as JavaScript and evaluates it.
"json": Evaluates the response as JSON and sends a JavaScript Object to the success callback.
If you can see in your fiddler that is returning only html then change your data type to html or if that only a script code then you can use script.
You should create a file anyname like json.php and then put the connection to the relayparty website this should works
$.ajax({
url: "json.php",
data: { foobar },
dataType: "json",
type: "POST",
async: false,
cache: false,
success: function (data) {
// do something here
},
error: function (data, status) {
alert(status);
}
});

javascript: handling client side initiated objects that the server hasn't assigned an id to

I'm developing a javascript application and I'm trying to make server side syncing as automagic and unobtrusive as possible
The problem I'm facing is that when creating an object client side I can immediately send the create ajax request and while I'm waiting for it to return add the object to the ui where necessary
However since the object has no id until the server responds I can't perform any update or destroy actions on it until the server responds and updates its id
What is the best way of dealing with this problem?
Some code if it helps:
create_object = function() {
return {
save: function() {
if (this.isNew()) {
this.create();
} else {
this.update();
}
},
isNew: function() {
return (this.id === undefined || this.id === null);
},
update: function () {
$.ajax({
url: '/update_object/'+this.id+'.json',
type: 'post',
dataType: 'json',
data: this
});
},
create: function () {
var object = this;
$.ajax({
url: '/create_object',
type: 'post',
dataType: 'json',
data: this,
success: function(data) {
object.id = data.id;
}
});
},
destory: function () {
$.ajax({
url: '/destroy_object/'+this.id+'.json',
type: 'get',
dataType: 'json'
});
}
};
};
var object = create_object();
object.message = "Foo!";
object.save();
// this will create a new object until object.save has responded
object.message = "Bar!";
object.save();
// this wont work until object.save has responded
object.destroy();
This is not really a problem. AJAX is asynchronous by nature (Asynchronous Javascript and XML). You will have to wait until you get a response from the server to update your UI.
If you must update the UI immediately, you can create an element with a placeholder ID that you can then update with the actual ID once the server responds.
UPDATE
This is in response to your comment. Webapps are not purely client-side applications. They fit into the client-server paradigm. You can try using a synchronous post, but this has the side-effect of tying up the browser until the server responds (this is due to the fact that Javascript is single-threaded).
That being said, the response from the server should be pretty quick unless your network is slow or if whatever post your sending to the server results in some computationally-intensive operation.
Going by the placeholder route will (as you have realized) result in all sorts of new problems that you have to deal with. You have to ensure that they are unique. Now what if they perform some operation on the client-side element before the server responds with an id? The server won't know what the placeholder id means. There are ways around this (queuing requests is one way), but it will make your code a whole lot more complicated. I don't think the tradeoff is worth it just to make the UI update faster.
An option is to provide some visual feedback to the user that the app is "working" (i.e., while you are waiting for a response from the server). A spinner, for example.
As far as stacking the AJAX requests, that is one approach that you can take using the jQuery message-queuing plugin.

Categories

Resources