6

I'm creating a backbone app that's connecting to a RESTful backend. When I call save() on a model, it sends the post data as stringified JSON:

{"firstName":"first","lastName":"last","Email":"email@gmail.com"}

but my server expects it to be formatted like a querystring:

firstName=first&lastName=last&Email=email@gmail.com

is there a way to have backbone send it differently?

Adam Langsner
  • 1,276
  • 1
  • 15
  • 23

6 Answers6

8

Backbone doesn't provide anything like this out of the box.
But is easy to override and customize it to your needs.

Have a look to the source code: http://documentcloud.github.com/backbone/docs/backbone.html

and check out that calling save, it will trigger a sync call in the background.

So what you need is to override Backbone.sync function with your own.

I would modify the part of:

if (!options.data && model && (method == 'create' || method == 'update')) {
      params.contentType = 'application/json'; 
      params.data = JSON.stringify(model.toJSON());
}

with

if (!options.data && model && (method == 'create' || method == 'update')) {
      params.contentType = 'application/json';
      params.data = $.param(model); // <-- CHANGED 
}

Notice I'm using jQuery param

If you want to use a custom function, check this question: Query-string encoding of a Javascript Object

[Update.]
No need to modify directly. Better override it with your own function 'Backbone.sync' Check the "TODO" example of the Backbone repository. It has a localStorage.js file that overrides Backbone.sync function https://github.com/documentcloud/backbone/tree/master/examples

Community
  • 1
  • 1
corbacho
  • 8,762
  • 1
  • 26
  • 23
  • I thought about doing this but I want to avoid editing the backbone source, I guess I could replace the Backbone.sync function with a modified one when my app starts, rather than directly editing the backbone.js file. Is it common to 'override' backbone methods like this? – Adam Langsner Jul 10 '12 at 23:22
  • 1
    I just tried overriding the Backbone.sync function when my app loads, it doesn't work because the methodMap array that sync uses is defined locally in the Backbone module, so my new sync function doesnt have access to it. To fix this Id have to edit backbone.js and set it on the Backbone object so that I could reference it outside of backbone.js: `var methodMap=[...]; Backbone.methodMap = methodMap;`, so I might as well change sync in there. But I dont want to touch backbone.js, it just seems wrong. e.g. it'd make it hard to upgrade backbone, would have to find all domain specific code first. – Adam Langsner Jul 10 '12 at 23:50
  • I updated the answer. Maybe you try to override Backbone before is loaded? – corbacho Jul 11 '12 at 07:08
  • 1
    You definitely shouldn't continue to send the "application/json" header if you're not actually sending JSON. The data that's being sent is "application/x-www-form-urlencoded". – Michael McTiernan Oct 20 '13 at 22:56
2

I ran into this problem at work and the Backbone.emulateJSON didn't work for me either. With some help I was able to come up with this workaround. We overrode the Backbone.ajax function and changed the contentType to "application/x-www-form-urlencoded" and used $.param to properly serialize the data.

Backbone.ajax = function() {
    if(arguments[0].data && arguments[0].contentType == "application/json"){
        arguments[0].data = $.param(JSON.parse(arguments[0].data));
        arguments[0].contentType = "application/x-www-form-urlencoded";
    }
    return Backbone.$.ajax.apply(Backbone.$, arguments);
}
Alex
  • 330
  • 3
  • 5
Dan
  • 21
  • 1
0

maybe this can help you,try: http://backbonejs.org/#Sync-emulateJSON

Justin wong
  • 638
  • 6
  • 15
  • 2
    Not really what I want, using emulateJSON will send post data like this: `model="{'firstName':'first'}"` this will just stick the JSON in a quesrystring, rather than encoding all values as a querystring. – Adam Langsner Jul 10 '12 at 16:17
0

I have done this by overriding model's sync function:

var MyModel = Backbone.Model.extend({ 
    "sync": function(method, model, options) {
        if (method == "update" || method == "create") {
            options = options ? _.clone(options) : {};
            options['data'] = $.param(this['attributes']);
        }
        var arguments = [method, model, options];
        return Backbone.sync.apply(this, arguments);
    }
});
Kostya Shkryob
  • 2,344
  • 17
  • 24
0

I find solutions, see :

  1. I use

    Backbone.emulateJSON = true;

  2. I write the "update" case:

    options.url = "/user/"+Math.random(1, 1000);
    options.type = "POST";
    //.1/2 WORK
    //options.data = (model instanceof Backbone.Model)?model.toJSON():{};
    options.data = model.toJSON();
    break;
    
Nunser
  • 4,512
  • 8
  • 25
  • 37
0

Backbone.sync uses the jQuery.ajax function, so we can modify the jqXHR or data that is sended to the server (via beforeSend).

var oldSync = Backbone.Model.prototype.sync;
var SomeModel = Backbone.Model.extend({
    url: 'test.json',

    defaults: {
        id: 1,
        foo: 'test'
    },

    sync: function (method, model, options) {
        // options are passed to the jQuery.ajax
        _.extend(options, {
            emulateHTTP: true,
            emulateJSON: false,
            beforeSend: function(xhr, settings) {
                // settings.data is a body of our request.
                if (_.isString(settings.data)) {
                    // settings.data is a JSON-string like '{"id":1, "foo":"test"}'
                    settings.data = Backbone.$.parseJSON(settings.data);
                }

                settings.data = Backbone.$.param(settings.data);
                // settings.data is 'id=1&foo=test'
            }
        });

        oldSync.apply(this, arguments);
    }
});

var model = new SomeModel();
model.save();

Actually we can create a mixin! :)

arz.freezy
  • 648
  • 1
  • 6
  • 12
  • 3
    Code only answers [are usually frowned upon](http://meta.stackoverflow.com/q/258673/2596334). Try adding [an explination or code comments](http://meta.stackoverflow.com/q/269981/2596334). Thanks. – Scott Solmer Sep 17 '14 at 14:02
  • Or, put another way: Good answers accompany code samples with an explanation for future readers. While the person asking this question may understand your answer, explaining how you arrived at it could help countless others. – Stonz2 Sep 17 '14 at 15:44
  • Sorry, guys. I've updated the answer. My English is poor, i know it, but i do my best :) – arz.freezy Sep 26 '14 at 07:35