In my REST server, it's requiring the access_token to be present in every request. i.e. in POSTing data, access_token needs to be submitted together with the attributes.
How do I configure backbone.js to add access_token to every GET, PUT, POST and DELETE request?
Thanks.
Okay, I think I found a way how to do it in jQuery.
$.ajaxSetup (
{
data: { access_token: 'my_access_token' }
}
);
Backbone uses jQuery/Zepto for AJAX requests, so you can use the functionality available in those libraries.
To add custom headers to all XHR calls made by jQuery, you can use the jQuery.ajaxSend event, which is triggered before every ajax request, and modify the jqXHR it receives as an argument.
Edit based on OP's comments:
Probably the simplest way to modify the sent data is to override the Backbone.sync function. You could wrap the native implementation, and add the required property there:
var nativeSync = Backbone.sync;
Backbone.sync = function (method, model, options) {
//for POST/PUT requests, add access token to the request
if(model && (method === 'create' || method === 'update')) {
var data = _.extend(model.toJSON(), {
access_token: 'token'
});
options.data = JSON.stringify(data);
}
//call the native Backbone.sync implementation
nativeSync(method, model, options);
};
Related
Is there a way to change the query string of JavaScript-induced requests? I want to add "&myParam=myValue" to any request sent by my HTML/JS application.
I don't think there's anything built in that lets you do that.
In my apps, I always have a central function XHR goes through so I have a single point to do things like this. If you don't have that or need to intercept calls from 3rd party libs:
You could wrap XMLHttpRequest.open to handle the XHR ones:
var originalOpen = XMLHttpRequest.open;
XMLHttpRequest.open = function() {
var args = Array.prototype.slice.call(arguments);
args[0] += (args[0].indexOf("?") == -1 ? "?" : "&") + "myParam=" + encodeURIComponent("myValue");
return originalOpen.apply(this, args);
};
...and then similar for fetch. But it seems brittle.
Alternately, you might look at using a cookie for the parameter, as the browser will add the cookie to the requests. (That assumes the requests are going to an origina you can add cookies for in your code.)
You could use partial application to lock in defaults when you declare your fetch function and essentially decorate the standard call that will merge your defaults and the passed params.
const fetchFactory = defaults => (url, data) => {
// make a copy of the defaults
const params = Object.assign({}, defaults)
// assign the passed in data with the defaults
params.body = JSON.stringify(Object.assign(params.body, data))
// call fetch with the params
return fetch(url, params)
}
// create a default for POST using the factory
const postFetch = fetchFactory({
method: 'post',
headers: {
'x-requested-with': 'fetch',
'Authorization': 'basic:' + btoa('a secret')
},
body: {
myParam: 'value'
}
})
// now you can call your function
postFetch('http://somewhere.com', {
one: 1,
two: 2
})
.then(respone => response.json())
It seems to me that you are asking how to set/edit URL parameters in http requests. Something quite similar has been asked here: here
If you are using XMLHttpRequest then the accepted answer in the link should work perfectly. The two key things the note are
the url parameters are simply a javascript object that you convert
into a JSON string. This happens through JSON.stringify({ myParam:
'hi'});
the question/answer linked are making post requests but you
may not want to make that request as well, I suggest doing some
research about which HTTP request method you want -
https://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html
I'm using jQuery (v.3.0.0) and I need ajaxSend() to check if a value is present in localStorage, to add it in the outgoing request headers.
If the value is not present in localStorage, ajaxSend() should get this value with another Ajax request and then send the original request with the correct value in the headers.
This must be a global handler, that applies to all jQuery Ajax requests that are sent out.
Let's make a code example.
$(document).ajaxSend(function (ev, req, opts) {
// Before sending any jQuery Ajax request
var original = req;
if (localStorage.foo) {
// If value "foo" is available, add it to the headers
req.setRequestHeader('foo', localStorage.foo);
} else {
// Otherwise get it first, and add it to the headers
$.get({url: '/foo', global: false})
.done(function (data, textStatus, req) {
// "foo" is received and added to the original request headers
localStorage.foo = data;
original.setRequestHeader('foo', data);
// Now the original request is ready to be sent
});
}
});
When foo is not available, the problem with the code above is that obviously the original request is sent out before the value of foo is retrieved.
Is there any way to fix this and get it to work properly?
Thanks!!
So far, this is the best solution that I could find. It's not perfect, and it does not exactly answer the original question, but it's a good workaround and gets very close to what I was trying to achieve.
The main difference is that instead of making the original request wait, it cancels it, it gets the desired value, and then it creates a new request with the same settings as the original one.
$(document).ajaxSend(function (ev, req, opts) {
if (localStorage.foo) {
// If value "foo" is available, add it to the headers
req.setRequestHeader('foo', localStorage.foo);
} else {
// Otherwise cancel the original request, then get "foo",
// and create a new request with the same settings as the original one
req.abort();
$.get({url: '/foo', global: false})
.done(function (data, textStatus, req) {
// "foo" is received
localStorage.foo = data;
})
.then(function () {
$.ajax(opts);
});
}
});
It works perfectly. If someone finds out a better solution that allows to directly use the original request I'll be happy to edit this answer and improve it.
Thanks!
I think you need to use the beforeSend option of $.ajax, you can return false from there if the value is present in localStorage in order to stop the request.
This will mean you always have to use the low-level $.ajax method, perhaps you could make your own set of wrappers for $.get, $.post, $.getJSON etc.
I found a solution that does not need to abort the original request as in Pensierinmusica's answer. Instead the send method of the xhr object of the original AJAX call is overridden, and the original send with arguments can then be invoked later when e.g. another AJAX request completes. See this answer: https://stackoverflow.com/a/30263533
Backbone.js handles posting data to server under the hood, so there is no easy way to insert a CSRF token in the payload. How can I protect my site against CSRF in this situation?
In this SO answer: https://stackoverflow.com/a/10386412/954376, the suggestion is to verify the x-Requested-By header to be XMLHTTPRequest. Is this enough to block all CSRF attempts?
In Django docs, the suggestion is to add CSRF token in another custom header in every AJAX request: https://docs.djangoproject.com/en/1.5/ref/contrib/csrf/#ajax. Is this necessary?
I understand if the attack uses hidden form, I am safe by just assuring the request is from XMLHTTPRequest. But is there any CSRF attack tricks that can forge the header?
Setting a global CSRF-token for all jQuery.ajax calls:
$(function(){
$.ajaxSetup({
headers: {'X-CSRFToken': CSRF_TOKEN}
});
})
Setting the token just for Backbone by overriding Backbone.sync:
var oldSync = Backbone.sync;
Backbone.sync = function(method, model, options){
options.beforeSend = function(xhr){
xhr.setRequestHeader('X-CSRFToken', CSRF_TOKEN);
};
return oldSync(method, model, options);
};
EDIT: Fixed a typo Kadam points at in comments
You can use a prefilter to add the token to all requests:
$.ajaxPrefilter(function(opts) {
if (opts.data) {
opts.data += "&";
}
opts.data += "csrfToken=" + token;
});
You may need to add additional logic if you don't always send the token.
Here's an updated version, based in Django 1.7 (using the jQuery cookie plugin)
oldSync = Backbone.sync
Backbone.sync = (method, model, options) ->
csrfSafeMethod = (method) ->
# these HTTP methods do not require CSRF protection
/^(GET|HEAD|OPTIONS|TRACE)$/.test method
options.beforeSend = (xhr, settings) ->
if !csrfSafeMethod(settings.type) and !#crossDomain
xhr.setRequestHeader 'X-CSRFToken', $.cookie('csrftoken')
return
oldSync method, model, options
I know it's a bit old question, but I'll leave a link to the github repo of AMD module just for this:
https://github.com/kuc2477/backbone.csrf.git (disclaimer: I'm the author of the module)
I'm having some trouble correctly forming a Backbone.Model.save call. The web service I'm calling consumes URL parameters, but what I have in Javascript is an object of changed fields. For example, I have the object {foo: 'bar', yar: 'har'}, and I want Backbone.Model.save to send a patch request to a URL like http://server/path/to/service?foo=bar&yar=har
Sounds simple, right? It's giving me a bunch of trouble anyway. Here's what I've got so far (which doesn't work; I have success/error callbacks, too, but I don't think those are important for the question):
object =
foo: 'bar',
yar: 'har'
model.save object,
patch: true
I've tried some other options, too:
model.save object,
patch: true
emulateJSON: true
This set contentType to "application/x-www-form-urlencoded", which is good, but the data sent in the ajax request by Backbone.sync was: {model: "{"foo": "bar", "yar": "har"}". The service got that and has no idea what to do with a "model" property.
model.save object,
patch: true
contentType: "application/x-www-form-urlencoded"
This just codes object as a string and stuffs that into options.data. Again, the service doesn't know what to do with it.
Any other ideas on how I can get this to conform to my service's spec? I can make the ajax call myself and update the model (and the collection it belongs to) myself, but I'd really rather not do that. An ajax request that works for me is:
$.ajax
url: "http://server/path/to/service"
type: "PATCH"
data: object
Update: The reason my two earlier options didn't work is clear in Backbone.js itself:
// Ensure that we have the appropriate request data.
if (options.data == null && model && (method === 'create' || method === 'update' || method === 'patch')) {
params.contentType = 'application/json';
params.data = JSON.stringify(options.attrs || model.toJSON(options));
}
// For older servers, emulate JSON by encoding the request into an HTML-form.
if (options.emulateJSON) {
params.contentType = 'application/x-www-form-urlencoded';
params.data = params.data ? {model: params.data} : {};
}
Looking at this, I thought maybe if I stuffed the object into object into options.data and sent in empty attributes, perhaps it'd work:
model.save {},
patch: true
data: object
Apparently this tried to PATCH an option "[object Object]". I guess it did a stringify of the object... somewhere... but this may be close to the right answer?
It looks like what I was looking for is the processData option to jQuery.ajax. Backbone.sync does the following by default:
// Don't process data on a non-GET request.
if (params.type !== 'GET' && !options.emulateJSON) {
params.processData = false;
}
Thus, it wasn't processing the object into URL parameters for me. (jQuery API)
So, a working bit of code would be:
model.save {},
patch: true
data: object
processData: true
In truth, I may not be using Backbone.Model correctly overall... but, at least it's working. :P
I have a single page js app and I'm sending custom headers to my server containing logs, but i need to control the size of those headers because my server won't accept requests larger then 8k.
My solution thus far was to intercept all outgoing ajax request from my application.
I'm using jQuery Global Ajax Events, particularly ajaxSend to intercept all requests. I cannot use beforeSend because that is a local event.
I can't seem to access the request headers in the callback. I need to read all request's header and cut down the logs header if it's too large.
You want to use beforeSend to modify the request before it is being sent. This is all covered in the documentation you've linked to.
The global ajaxSend event will not help you tamper with the request. The closest thing to global you can get would be to is call ajaxSetup, passing a beforeSend option to be default for all subsequent ajax calls.
There appears to be no simple way of getting request headers from an XMLHttpRequest object. Since I assume you're setting your logging headers yourself, however, you might be able to hook into the setting of these headers, and store an accessible reference to them:
XMLHttpRequest.prototype.readableHeaders = {};
XMLHttpRequest.prototype.proxiedSetRequestHeader = XMLHttpRequest.prototype.setRequestHeader;
XMLHttpRequest.prototype.setRequestHeader = function(header, value) {
this.proxiedSetRequestHeader(header, value);
this.readableHeaders[header] = value;
};
In this manner, you should be able to directly inspect the jqXHR.readableHeaders object for your specific logging header, in beforeSend, and call setRequestHeader once more, to truncate the string, if needed.
To retrieve the headers you need access to the underlying instance of XMLHttpRequest from jqXHR object. Use xhr() function to retrieve the instance.
$.ajaxSetup({
beforeSend: function (jqXHR, settings) {
console.log( settings.xhr().readableHeaders );
}
});