I don't want to know how to make cross XHR request, i want to know how to make cross xhr request with XHR header (X-Requested-With: XMLHttpRequest ) <-- I want this header to get sent with request to server, when i try with traditional way of http.setRequestHeader("","")
where var http = new XMLHttpRequest();
it doesn't work. Wondering, what is the way to do that?
p.s: ALL request will be POST based
Adding additional headers triggers a preflight OPTIONS request which must get a response with suitable CORS headers before the browser will make the POST request.
You need to configure the server to respond to the OPTIONS request and say that the non-standard header is allowed.
Related
From: https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#preflighted_requests
The following is an example of a request that will be preflighted:
const xhr = new XMLHttpRequest();
xhr.open('POST', 'https://bar.other/resources/post-here/');
xhr.setRequestHeader('X-PINGOTHER', 'pingpong');
xhr.setRequestHeader('Content-Type', 'application/xml');
xhr.onreadystatechange = handler;
xhr.send('<person><name>Arun</name></person>');
The example above creates an XML body to send with the POST request.
Also, a non-standard HTTP X-PINGOTHER request header is set. Such
headers are not part of HTTP/1.1, but are generally useful to web
applications. Since the request uses a Content-Type of
application/xml, and since a custom header is set, this request is
preflighted.
Will a preflight request be triggered if the request is same-origin but does not follow the header guidelines?
No, preflight requests are only done for cross-site requests. If you look at the beginning of the MDN article there is an image that explicitly says "Same origin-requets (always allowed)" as an example.
The below image isn't very visible if running SO in dark mode, if so, check the image in the article on the url below.
https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS
I have a simple cross domain service designed to handle the Simple CORS request. I am able to call it through plain xmlHTTP call or jQuery($.ajax) but its throwing Access-Control-Allow-Origin error with AngularJS $http
var url = 'http://some-cross-domain-url/some-path';
$http.get(url); //preflight OPTION verb issued by browser and
//since server is not expecting it, it failed
$.ajax(url, {type: 'GET'}); //working fine as no preflight request sent
CORS request called via Angular $http was triggering preflight (OPTIONS verb) but with plain Ajax call or jQuery Ajax its sent as non-preflighted CORS request as confirmed by debugger network tab in chrome.
As the service designed to handle the Simple CORS request call we need to ensure that Angular also prepare request in a way so that browser issue simple CORS request (See Simple vs Not so simple CORS request at MDN).
Solution: Remove the headers added by Angular by referring Access-Control-Request-Headers
GET request without any headers is treated as simple request
If you have configured Angular $http defaults, it will add these headers into request which makes it not so simple CORS as shown in below image.
All custom HTTP headers sent as Access-Control-Request-Headers when preflighted. Once server allows the communication as per CORS rule, browser sends the actual request(with original Method and Headers etc)
//remove custom headers by looking at Access-Control-Request-Headers
var headers = {
'Authorization': undefined,//undefined tells angular to not to add this header
'pragma': undefined,
'cache-control': undefined,
'if-modified-since': undefined
};
$http.get(url, {
headers: headers
});
I have a node web server (express) running on port 6000.
When http://localhost:6000/ is invoked, index.html is served to the client which has a script, logic.js that runs:
var xhr = new XMLHttpRequest();
var url = 'http://localhost:4000/endpoint';
xhr.open('POST', url, true);
xhr.setRequestHeader('Content-type', 'application/json');
xhr.onreadystatechange = function() {
if (xhr.readyState === 4 && xhr.status === 200) {
console.log('endpoint');
callback(xhr.responseText);
}
};
xhr.send(JSON.stringify({'key': 'value'}));
There is another express server running on port 4000.
The server sets up an endpoint which simply returns what the request body submitted:
app.route('/segment')
.post(bodyParser.json(), function(req, res) {
res.set('Access-Control-Allow-Origin', '*');
res.send(req.body);
});
When I access http://localhost:6000/, I'm seeing this in the browser console:
Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at http://localhost:4100/segment. (Reason: CORS header ‘Access-Control-Allow-Origin’ missing)
Is there a way to bypass this during prototyping?
Your endpoint probably doesn't serve the OPTIONS HTTP method which is used by browsers to check CORS headers (before they make the real request).
If you need different CORS headers in development and in production, I think the best way is to add a new backend config option with the value for allowed origins and serve them from some global response filter.
You have set xhr.setRequestHeader('Content-type', 'application/json'); so this is a preflighted request (rule of thumb: A Content-Type that is not a valid value for the enctype attribute of an HTML form will trigger a preflighted request).
Your server side code only sets the Access-Control-Allow-Origin header in response to POST requests. You need to write server side code to respond to the preflight OPTIONS request too.
Here are a few ways to solve this problem:
Best: CORS header (requires server changes)
CORS (Cross-Origin Resource Sharing) is a way for the server to say “I will accept your request, even though you came from a different origin.” This requires cooperation from the server – so if you can’t modify the server (e.g. if you’re using an external API), this approach won’t work.
Modify the server to add the header Access-Control-Allow-Origin: * to enable cross-origin requests from anywhere (or specify a domain instead of *). This should solve your problem.
2nd choice: Proxy Server
If you can’t modify the server, you can run your own proxy. And this proxy can return the Access-Control-Allow-Origin header if it’s not at the Same Origin as your page.
Instead of sending API requests to some remote server, you’ll make requests to your proxy, which will forward them to the remote server. Here are a few proxy options.
3rd choice: JSONP (requires server support)
If CORS and the proxy server don’t work for you, JSONP may help. You essentially make a GET request with a callback parameter:
(get) http://api.example.com/endpoint?callback=foo
The server will wrap the JSON reply in a function call to your callback, where you can handle it:
foo({"your": "json", here: true})
There are some downsides, notably that JSONP only supports GET requests and that you still need a cooperative server.
I am trying to get all the request headers that I have set after an ajax call is made. My authorization and a few other headers vary for each call and it is a pain to keep track of all of them. In the $( document ).ajaxComplete(), the xhr and the settings do not seem to have the request headers. There are the whole response headers though. Is there a way wherein I can get all the request headers after I pass the request (in the ajaxcomplete)?
I'm testing a HTTP Server that I have developed myself with C++ and Boost libraries. More specifically, I'm testing an endpoint where a JSON is received by PUT.
To test the RESTFul webservice I use Curl with the following command:
curl -H "Content-Type: application/json" -H "Content-Length: 34" -H "Connection: close" -X PUT --data "#response_json" http://localhost:8080/answer
where response_json is a file with the json to be sent. This works fine, the server receives the request as a PUT and do what is supposed to do.
However, when I test the webservice from AJAX with this:
function sendPut2() {
var http = new XMLHttpRequest();
var url = 'http://localhost:8080/answer';
var data = JSON.stringify({"question": "a", "answer": "b"});
http.open("PUT", url, true);
http.setRequestHeader("Content-type", "application/json");
http.setRequestHeader("Content-Length", data.length);
http.setRequestHeader("Connection", "close");
http.onreadystatechange = function() {
if(http.readyState == 4 && http.status == 200) {
alert(http.responseText);
}
}
http.send(data);
}
the server receives it as OPTIONS and does not work. Moreover, in Firebug console I can see: "NetworkError: 404 Not Found - http://localhost:8080/answer".
I have tried with Firefox and Chrome. What is wrong in my javascript code?
This is the Firebug with the request from Javascript:
The browser has a same origin policy for security reasons. When you request a Ajax PUT in the browser from a different origin than the current web page was loaded from, then the request is subject to that same origin policy. The destination site can choose to support CORS (cross origin resource sharing) which is a specific scheme that the browser implements that lets it ask the target site if a specific cross origin request is OK or not.
Using the OPTIONS request before the PUT request is one such part of the CORS scheme. If the browser detects certain conditions on the original cross origin request, then it will first issue an OPTIONS request and, if it gets the right response from that, then it will issue the target request (a PUT in your case). Things that can trigger the browser to use the OPTIONS request are things like custom headers, certain types of authorization required, certain content types, certain types of requests, etc...
CURL, on the other hand, enforces no such same origin security (that is someting a browser invented for its own web page security model) so it just sends the PUT request right through without requiring the correct answer from the OPTIONS request first.
FYI, if the Javascript in the browser that is making the Ajax request is requesting from the same origin as the loaded web page that contains the Javascript, then it should not trigger the OPTIONS request because it would be a same origin request rather than a cross origin request. If you have a local server, make sure that the web page is being loaded from the local server (same hostname and port number) too, not from the file system and not one using an IP address and the other using localhost or something like that. As far as the browser is concerned, the hostname has to physically be the same, not just the same IP address.
Here's info from MDN on what requests are "preflighted" with the OPTIONS request:
Preflighted requests
Unlike simple requests (discussed above), "preflighted" requests first
send an HTTP request by the OPTIONS method to the resource on the
other domain, in order to determine whether the actual request is safe
to send. Cross-site requests are preflighted like this since they may
have implications to user data. In particular, a request is
preflighted if:
It uses methods other than GET, HEAD or POST. Also, if POST is used
to send request data with a Content-Type other than
application/x-www-form-urlencoded, multipart/form-data, or text/plain,
e.g. if the POST request sends an XML payload to the server using
application/xml or text/xml, then the request is preflighted. It sets
custom headers in the request (e.g. the request uses a header such as
X-PINGOTHER)
FYI, here's a pretty good explanation of the various aspects of CORS. Because your request is a PUT, it will be in the "not-so-simple request" part of that article.