Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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.

like image 949
MusikPolice Avatar asked Feb 07 '13 13:02

MusikPolice


3 Answers

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.

like image 54
Esailija Avatar answered Oct 16 '22 13:10

Esailija


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.

like image 3
MusikPolice Avatar answered Oct 16 '22 12:10

MusikPolice


i have tested your code, its works fine for me.

See Demo here, jsfiddle.net/yxkUD/

like image 2
Array out of bound Avatar answered Oct 16 '22 14:10

Array out of bound