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/
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With