Setting a global REST root url in a Backbone.js ap

2019-04-07 07:55发布

问题:

In backbone.js, you have to set the rooturl of every model manually. Is there a way we can set this in a single location once and all models will use it?

For eg. api.site.com will be the REST service, but for testing purpose, it may be at localhost:1000 I need to be able to change the root url of the service easily and not have them all over the place in the many models that exist in the application.

回答1:

Not sure what exactly you mean setting urlRoot for every model manually. Presumably you do

var MyModel = Backbone.Model.extend({

  urlRoot: '/mymodel',
  ...

});

right? Then each model instance naturally has the same urlRoot.Any other model instance of say MyOtherModel would have some different urlRoot.

If for some reason you need to be using the same urlRoot, I would guess it would be because the models share attributes. This should hint to inheritance or extension so you could do:

var BaseModel = Backbone.Model.extend({
  urlRoot: '/mymodel'
});

var MyModel = BaseModel.extend({
  ...
});


回答2:

I've been playing around with the most elegant way to do this when using Backbone with AMD (require.js). This is what I've come up with, and like best [so far].

First, bootstrap the data in, as described here.

index.htm (application entry point)

<!DOCTYPE html>
<head>
  <meta charset="utf-8">

  <script>
    var require = {
      config: {
        // key name is module, value is data to give module
        'models/Base': {
          apiUrl: '/some/path/to/api',
        }
      }
    }
  </script>
<script data-main="js/main" src="vendor/require/require-jquery.js"></script>
</head>
<body></body>
</html>

Next, define a base class for Backbone models, like this:

js/models/Base.js (notice this module name matches the module name in config above)

define(['module', 'backbone'],
  function(module, Backbone){

    return Backbone.Model.extend({
      getApiUrl: function(){
        return module.config().apiUrl;
      }
    });

  }
);

Finally, extend all of your Backbone models from that base, like this:

js/models/User.js

define(['models/Base'],
  function(BaseModel){

    return BaseModel.extend({
      urlRoot: function(){
        return this.getApiUrl() + '/user';
      }
      // ... more cool stuff here
    });

  }
);

This is tangentially related to an earlier question I had regarding Backbone and Require.JS.

Related reading: http://requirejs.org/docs/api.html#config-moduleconfig



回答3:

What I understand is :

You have several models: model1 and model2

The root URL can be either http://api.site.com or http://localhost:8080 depending on whether you are working locally or externally. And someday it could be http://api.anothersite.com

I would do somthing like this then :

// Global var before any other JS code, you comment line depending on which one applies
var ROOT = 'http://api.site.com'
// var ROOT = 'http://localhost:8080'

var Model1 = Backbone.Model.extend({
urlRoot: ROOT+'/model1',
...
});

var Model2 = Backbone.Model.extend({
urlRoot: ROOT+'/model2',
...
});

But be careful with this, you may forget to switch from one url to the other one when commiting. Better is to handle relative paths if they'd apply.



回答4:

In my opinion the best way to achieve this is by overriding the Backbone.sync method, and prepend your root url there. This because it's not the responsibility of the model to know the API domain.

// Cache Backbone.sync for later use
var sync = Backbone.sync;
Backbone.sync = function(method, model, options){
  options.beforeSend = function(){
    this.url = API_ROOT_URL + this.url;
  };
  return sync.call(this, method, model, options);
};

And then you can create your models as you would normally do:

var Resource = Backbone.Model.extend({
  urlRoot: '/resources',
});

I use this same method to append an .json extension to all URL's to prevent JSON flashes through browser cache.



回答5:

Adapted from https://coderwall.com/p/8ldaqw/add-a-root-url-to-all-backbone-api-request

var backboneSync = Backbone.sync;

Backbone.sync = function (method, model, options) {
    /*
     * Change the `url` property of options to begin
     * with the URL from settings
     * This works because the options object gets sent as
     * the jQuery ajax options, which includes the `url` property
     */
    options = _.extend(options, {
        url: API_ROOT + _.result(model, 'url')
    });

    /*
     *  Call the stored original Backbone.sync
     * method with the new url property
     */
    backboneSync(method, model, options);
};


回答6:

Just start your url from '/'. Thus you'll be not depend on either localhost or real domain.



回答7:

This is really old post, but I think that my version can be useful for someone. I use backbone with require.js and my server-side and client-side completely separated. I give to know for the client app where is API root at the same time, when I declare requirejs script:

<script 
     data-main="js/main" 
     data-api-url="https://api/v1/" 
     src="js/require.js">
</script>

and then i get in my backbone module:

var $scriptEl = $('script[data-main]'),
      base = $scriptEl.attr('data-api-url');

var BaseModel = Backbone.Model.extend({
    urlRoot: base
});

var model = BaseModel.extend({});

This is the most elegant way which I could find..



标签: backbone.js