Backbone Fetch Request is OPTIONS method - javascript

I have a Backbone Collection object with the URL http://localhost:8080/api/menu/1/featured.
I am trying to perform a fetch operation to retrieve the collection from the URL and parse it. However, on the server side, the method type that I see for this request is OPTIONS. The server is only suppose to support GET method. I am not sure how Backbone is figuring out what method type to use, and why it changes to OPTIONS method type randomly sometimes. I am using a Node.js server to process the request. This code below is pretty much what I did.
var FeaturedCollection = Backbone.Collection.extend({
model:FeaturedContent,
url:function () { return url_featured; },
parse:function (response) {
console.log(response);
return response;
}
});
var featuredCollection = new FeaturedCollection();
featuredCollection.fetch();

It's been awhile, but I remember coming across this before. There's two things this could be: Backbone by default tried to do RESTful API calls to your backend, this means GET, POST, PUT, and DELETE.
Many backends weren't built with real REST support and only support GET and POST. When Backbone sends a PUT or DELETE command your browser (not Backbone) automatically sends an OPTIONS request first to see if it's allowed to make these kinds of requests. If your server answers improperly this call will fail and probably Backbone won't do anything.
To get around this set Backbone.emulateHTTP = true; Or have your server properly answer OPTIONS calls. See the documentation for more info: http://backbonejs.org/#Sync-emulateHTTP
The other issue is that you're making ajax requests cross-domain / sub-domain and you need to properly enable CORS. This also includes properly answering OPTIONS requests.

I had the exact same problem as OP - using Backbone and NodeJS to save data via a CORS POST request would constantly send an OPTIONS http request header, and not trigger the POST request at all.
Apparently CORS with requests that will "cause side-effects on user data" will make your browser "preflight" the request with the OPTIONS request header to check for approval, before actually sending your intended HTTP request method.
https://developer.mozilla.org/en-US/docs/HTTP_access_control#Overview
This thread was what solved my problem - How to allow CORS?
The poster used some middleware to approve PUT/GET/POST/DELETE requests like so -
res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE');
...
next();
and the next(); would allow the OPTIONS check to continue on to the POST request.
Worked like a dream for me, hope it helps someone else too.

Backbone.js maps CRUD methods to HTTP. Taken from Backbone's source code:
var methodMap = {
'create': 'POST',
'update': 'PUT',
'delete': 'DELETE',
'read': 'GET'
};
Backbone.sync = function(method, model, options) {
var type = methodMap[method];
Probably the problem resides on your node.js backend.

What version of backbone are you using? I had exactly the same issue, but then realised I had been using an old version of backbone (0.3.3) in a tutorial. Upgraded the link to the latest backbone.js (0.9.2) and underscore.js(1.3.3) and it sends as a GET.

Related

How can I turn a Node.js http.IncomingMessage into a Fetch Request object?

I'm trying to work with the eBay APIs. It's a small personal project that just needs to run locally, and although I know C#, I'm much more comfortable with Javascript so I'm looking for ways to get this done in JS.
I found this promising looking eBay Node API with browser support. Browser support is something I'm looking for, but it also says that
A Proxy server is required to use the API in the Browser.
They give an example file for a proxy server that is a Cloudflare worker.
I'm trying to translate that into something I can run in Node locally using the basic Node HTTP server. I've been following through it and am doing OK so far, figured out the different ways to access the headers and check them, etc., but now I'm at the point where the proxy server is making the proxy request to the eBay APIs. The way the example file is set up, it seems as though the Cloudflare worker intercepts the HTTP request and by default treats it as a Fetch Request. So then when it goes to pass on the request, it just kind of clones it (but is replacing the headers with "cleaned" headers):
// "recHeaders" is an object that is _most_ of the original
// request headers, with a few cleaned out, and "fetchUrl"
// is the true intended URL to query at eBay
const newReq = new Request(event.request, {
"headers": recHeaders
});
const response = await fetch(encodeURI(fetchUrl), newReq);
The problem is that I don't have a Fetch Request to clone - since I'm running a Node HTTP server, what is event.request in the example code is for me a http.IncomingMessage.
So how can I turn that into a Fetch Request? I'm guessing at the very least there's stuff in the message body that needs to get passed along, if not other properties I'm not even aware of...
I don't mind doing the work, i.e. reading the stream to pull out the body, and then putting that into the Request object somehow (or do I even need to do that? Can I just pipe the stream from the IncomingMessage directly into a Request somehow?), but what else besides the body do I need to make sure I get from the IncomingMessage to put into the Request?
How do I turn a Node http.IncomingMessage into a Fetch Request and be sure to include all relevant parts?
I've made a simple function to convert.
const convertIncomingMessageToRequest = (req: ExpressRequest): Request => {
var headers = new Headers();
for (var key in req.headers) {
if (req.headers[key]) headers.append(key, req.headers[key] as string);
}
let request = new Request(req.url, {
method: req.method,
body: req.method === 'POST' ? req.body : null,
headers,
})
return request
}

How do I set a default Header for all XMLHTTPRequests

Problem description
We are running a Kibana 4.3 service. I do not want to modify the source code.
The objective is add an encrypted token, call it A-Token to every Ajax request that the browser makes to Kibana.
Background
The Kibana service is proxied by nginx.
When a user makes an Ajax request to the Kibana service, the request is intercepted by an nginx http_auth_request proxy and passed to an "auth" service that validates the token. If its missing or invalid, then "auth" returns 201 to http_auth_request and the request to the Kibana service is executed, else it returns a 404 and the request is denied since it was made without a valid token.
(this scheme is based on the encrypted token pattern often used as a countermeasure for cross-site scripting in session-less situations like the one at hand).
I read the W3 XMLHttpRequest documentation and it seems that setRequestHeader needs to run after open and before send - which implies that this scheme is either impossible in a general case or very JS platform dependent.
A test using the Jquery .ajaxSetup like this example, confirms that headers cannot be set independently:
$.ajaxSetup({
beforeSend: function(xhr) {
xhr.setRequestHeader(A-Token", 1314159);
}
});
Looking for possible solutions which will not require forking Kibana.
Danny
I was searching for solution for this problem as well but couldn't find anything and then I came up with next solution:
XMLHttpRequest.prototype.origOpen = XMLHttpRequest.prototype.open;
XMLHttpRequest.prototype.open = function () {
this.origOpen.apply(this, arguments);
this.setRequestHeader('X-TOKEN', 'the token');
};

How to send user token with every JsonP request?

In my sencha touch 2.1, after successful login a token is sent back from service which has to be stored and used with all future service calls. Since I am dealing with remote web service so all the stores use JsonP proxy to fetch data which is why I want to add the token to all such calls. Since JsonP doesn't support headers I am planning to add this token as url param but I am not sure how to do this for all JsonP calls originating from app.
A similar question for AJAX calls was found
Send user details (session token) within every AJAX requests (Sencha Touch 2)
but since JsonP does not support 'beforerequest' event and headers, I am stuck.
Is there any other event I can listen/intercept to add this url param? is there a way to write base proxy class which has this functionality? Please share some examples if you know how to do this.
Ok, I found a way that worked for me.
I extended JsonP proxy and in buildUrl method I appended cached token, and now I an using this proxy in all my stores. Here is the code:
Ext.define('myshop.proxy.CustomJsonpProxy', {
extend: 'Ext.data.proxy.JsonP',
alias: 'proxy.customjsonpproxy',
buildUrl: function(request) {
var me = this,
url = me.callParent(arguments);
if(!Ext.isEmpty(loggedInUserToken)){
url = Ext.urlAppend(url, "token="+loggedInUserToken);
}
return url;
}
});
Please share if you know of other better ways.

Sending POST message with AJAX Problem

I am currently trying to send a POST message which works fine except for the error that there are not correct credentials. However, after I add the credentials header, the message type is changed into OPTIONS and fails. I do not understand how adding a header causes the type to change to OPTIONS. Any help would be appreciated.
ajaxRequest = $j.ajax({
url: url,
type: 'POST',
beforeSend : function(req) {
req.setRequestHeader('Authorization', auth),
}
success: function(data, status) {
console.log("Success!!");
console.log(data);
console.log(status);
},
error: function(xhr, desc, err) {
console.log(xhr);
alert('fail')
console.log("Desc: " + desc + "\nErr:" + err);
}
});
EDIT: just to be more clear, I can literally go in and comment out the setRequestHeader function and it sends the message POST.
The problem you're encountering is because of cross-domain restrictions when using AJAX. When you try to set an authorization header, the browser issues what's known as a pre-flight request to see if the server will accept requests from this domain.
A pre-flight request is typically sent as an OPTIONS request. If the server you're invoking doesn't return an Access-Control-Allow-Origin header that matches your domain, the AJAX request is blocked.
There's more on this here: Cross-Origin Resource Sharing
"User agents can discover via a preflight request whether a cross-origin resource is prepared to accept requests, using a non-simple method, from a given origin."
I've run into the same problem- there are a few possible workarounds depending on your scenario.
If you have any way of setting the above mentioned header on the 3rd party server (some applications/services offer this) then that's probably the easiest way.
There's also a javascript library called EasyXDM that may work for you, but again, it will only be of use if you have access to the 3rd party server to upload a configuration file for this library.
Other options to investigate are PostMessage and Cross Domain Iframe communication. The latter is more of an old-school hack, the former is the recommended approach for newer browsers. It won't work for IE6/7.
The option we will probably end up using is a simple proxy- invoke our own server with the AJAX request, and on the server invoke the 3rd party server. This avoids the cross domain issue entirely, and has other advantages for our scenario.
I guess this is a problem in Internet Explorer. without explicitly telling the request-method (POST|GET) the request header doesn't contain the custom-header in IE, but it works in other browsers.
Yet try to post this in the bugs for jquery. Also try in other browsers.
Edit 1 : I saw this as a bug in jQuery 1.4.x .... I reported a bug report now.
The OPTIONS response happens when the server does not know how to respond to the ajax request.
I've seen it happen often when trying to post to a third-party domain (i.e. cross-site posting)
The OPTIONS method represents a request for information about the communication options available on the request/response chain identified by the Request-URI. This method allows the client to determine the options and/or requirements associated with a resource, or the capabilities of a server, without implying a resource action or initiating a resource retrieval.
Have you tried:
Having some sort of callback on the url that is being posted to?
Explicitly setting the headers (I'm assuming you're using PHP) on the url that is being posted to?

How to POST data to an HTTP page from an HTTPS page

I know this is a long shot, but I figured I'd ask the question anyway.
I have an HTTPS page and am dynamically creating a form. I want to POST the form to an HTTP page. Is this possible without the browser popping up a warning? When I do this on IE8, I get the following message:
Do you want to view only the webpage content that was delivered securely?
Essentially, I'm asking about the inverse of question 1554237.
Sadly, I know of absolutely no way to not get warned when posting from HTTPS to HTTP. If you serve the form securely, the browser expects to submit the data securely as well. It would surprise the user if anything else was possible.
Nope, can't be done. Our good friend IE will always pop up that warning.
There is a way to do this if you write a back-end service of your own. So lets say you want to post an HTTP request to s1 using your front-end service fs1.
If you use Spring, you can use an ajax call from fs1 to a 'uri' that is recognized by your spring back-end, say bs1. Now, the service bs1 can make the call to the s1.
Pictorial representation here: http://i.stack.imgur.com/2lTxL.png
code:
$.ajax
({
type: "POST",
uri:/json/<methodName>
data: $('#Form').serialize(),
success: function(response)
{
//handle success here
},
error: function (errorResponse)
{
//handle failure here
}
})
You can solve this by either acting as a proxy for the form destination yourself (i.e. let the form submit to your server which in turn fires a normal HTTP request and returns the response), or to let access the page with the form by HTTP only.
If you don't need to actually redirect to the insecure page, you can provide a web service (authenticated) that fires off the request for you and returns the data.
For example:
From the authenticated page, you call doInsecure.action which you create as a web service over https. doInsecure.action then makes a manual POST request to the insecure page and outputs the response data.
You should be able to do this with the opensource project Forge, but it sounds like overkill. The Forge project provides a JavaScript interface (and XmlHttpRequest wrapper) that can do cross-domain requests. The underlying implementation uses Flash to enable cross-domain (including http <=> https) communication.
http://github.com/digitalbazaar/forge/blob/master/README
So you would load the Forge JavaScript and swf from your server over https and then do a Forge-based XmlHttpRequest over http to do the POST. This would save you from having to do any proxy work on the server, but again, it may be more work than just supporting the POST over https. Also, the assumption here is that there's nothing confidential in the form that is being posted.

Categories

Resources