I am working on an internal web application at work. In IE10 the requests work fine, but in Chrome all the AJAX requests (which there are many) are sent using OPTIONS instead of whatever defined method I give it. Technically my requests are "cross domain." The site is served on localhost:6120 and the service I'm making AJAX requests to is on 57124. This closed jquery bug defines the issue, but not a real fix.
What can I do to use the proper http method in ajax requests?
Edit:
This is in the document load of every page:
jQuery.support.cors = true;
And every AJAX is built similarly:
var url = 'http://localhost:57124/My/Rest/Call';
$.ajax({
url: url,
dataType: "json",
data: json,
async: true,
cache: false,
timeout: 30000,
headers: { "x-li-format": "json", "X-UserName": userName },
success: function (data) {
// my success stuff
},
error: function (request, status, error) {
// my error stuff
},
type: "POST"
});
Chrome is preflighting the request to look for CORS headers. If the request is acceptable, it will then send the real request. If you're doing this cross-domain, you will simply have to deal with it or else find a way to make the request non-cross-domain. This is why the jQuery bug was closed as won't-fix. This is by design.
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)
Based on the fact that the request isn't sent on the default port 80/443 this Ajax call is automatically considered a cross-origin resource (CORS) request, which in other words means that the request automatically issues an OPTIONS request which checks for CORS headers on the server's/servlet's side.
This happens even if you set
crossOrigin: false;
or even if you ommit it.
The reason is simply that localhost != localhost:57124. Try sending it only to localhost without the port - it will fail, because the requested target won't be reachable, however notice that if the domain names are equal the request is sent without the OPTIONS request before POST.
I agree with Kevin B, the bug report says it all. It sounds like you are trying to make cross-domain ajax calls. If you're not familiar with the same origin policy you can start here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Same_origin_policy_for_JavaScript.
If this is not intended to be a cross-domain ajax call, try making your target url relative and see if the problem goes away. If you're really desperate look into the JSONP, but beware, mayhem lurks. There really isn't much more we can do to help you.
If it is possible pass the params through regular GET/POST with a different name and let your server side code handles it.
I had a similar issue with my own proxy to bypass CORS and I got the same error of POST->OPTION in Chrome. It was the Authorization header in my case ("x-li-format" and "X-UserName" here in your case.) I ended up passing it in a dummy format (e.g. AuthorizatinJack in GET) and I changed the code for my proxy to turn that into a header when making the call to the destination. Here it is in PHP:
if (isset($_GET['AuthorizationJack'])) {
$request_headers[] = "Authorization: Basic ".$_GET['AuthorizationJack'];
}
In my case I'm calling an API hosted by AWS (API Gateway). The error happened when I tried to call the API from a domain other than the API own domain. Since I'm the API owner I enabled CORS for the test environment, as described in the Amazon Documentation.
In production this error will not happen, since the request and the api will be in the same domain.
I hope it helps!
As answered by #Dark Falcon, I simply dealt with it.
In my case, I am using node.js server, and creating a session if it does not exist. Since the OPTIONS method does not have the session details in it, it ended up creating a new session for every POST method request.
So in my app routine to create-session-if-not-exist, I just added a check to see if method is OPTIONS, and if so, just skip session creating part:
app.use(function(req, res, next) {
if (req.method !== "OPTIONS") {
if (req.session && req.session.id) {
// Session exists
next();
}else{
// Create session
next();
}
} else {
// If request method is OPTIONS, just skip this part and move to the next method.
next();
}
}
"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
https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS
Consider using axios
axios.get( url,
{ headers: {"Content-Type": "application/json"} } ).then( res => {
if(res.data.error) {
} else {
doAnything( res.data )
}
}).catch(function (error) {
doAnythingError(error)
});
I had this issue using fetch and axios worked perfectly.
I've encountered a very similar issue. I spent almost half a day to understand why everything works correctly in Firefox and fails in Chrome. In my case it was because of duplicated (or maybe mistyped) fields in my request header.
Use fetch instead of XHR,then the request will not be prelighted even it's cross-domained.
$.ajax({
url: '###',
contentType: 'text/plain; charset=utf-8',
async: false,
xhrFields: {
withCredentials: true,
crossDomain: true,
Authorization: "Bearer ...."
},
method: 'POST',
data: JSON.stringify( request ),
success: function (data) {
console.log(data);
}
});
the contentType: 'text/plain; charset=utf-8', or just contentType: 'text/plain', works for me!
regards!!
Related
What is the reason the server is returning object as 'undefined' and 'XMLHttpRequest cannot load the "URL" Response for preflight is invalid (redirect).
Flow of app - its just a normal post service sending document details to the server in return should return an object holding various parameters, but its returning 'undefined'
The service for posting the document
fileUpload: {
method: 'POST',
url: config.apiPath + 'employee/service/pushRecords', //this is the URL that should return an object with different set of parameters (currently its returning Error error [undefined])
isArray: false,
params: {},
headers: {
'content-type': undefined
}
},
above service i have used after creating formdata w.r.t document
function registerFormdata(files, fieldName) {
files = files || [];
fieldName = fieldName || 'FileSent';
var returnData = new FormData();
_.each(files, function (file, ind) {
returnData.append(fieldName,file);
});
return returnData;
}
now this is the controller where these services are used
function sendFilesToServer() {
var formData = employeePushService.registerFormdata(directive.dropZoneFile.fileToUpload);
return docUploadService.fileUpload(formData)
.then(function(document) {
// Extra actions but here the server should be returning an object with set of parameters but in browser console its Error [undefined]
}).catch(logger.error);
}
Assuming that the URL target in yout post is correct, it seems that you have a CORS problem, let me explain some things.
I don't know if the server side API it's developed by yourself, if it is, you need to add the CORS access, your server must return this header:
Access-Control-Allow-Origin: http://foo.example
You can replace http://foo.example by *, it means that all request origin will have access.
First, you need to know that when in the client you make an AJAX CORS request, your browser first do a request to the server to check if the server allow the request, this request is a OPTION method, you can see this if, for example in chrome, you enable the dev tools, there, in the network tab you can see that request.
So, in that OPTIONS request, the server must set in the response headers, the Access-Control-Allow-Origin header.
So, you must check this steps, your problem is that the server side is not allowing your request.
By the way, not all the content-type are supported in CORS request, here you have more information that sure will be helpfull.
Another link to be helpfull for the problem when a 302 happens due to a redirect. In that case, the POST response must also include the Access-Control-Allow-Origin header.
I'm getting the following error using AJAX to call an API on UPS
XMLHttpRequest cannot load https://wwwcie.ups.com/rest/Ship. Response to
preflight request doesn't pass access control check: No 'Access-Control-Allow-
Origin' header is present on the requested resource. Origin
'http://localhost:63786' is therefore not allowed access.
AJAX Call:
$.ajax({
url: "https://wwwcie.ups.com/rest/Ship",
type: "POST",
dataType: 'json',
crossDomain: true,
contentType: 'application/json',
data: JSON.stringify(message),
success: function (result) {
//code to execute on success
}
error: function (result) {
//code to execute on error
}
})
I have no control over the API server so I cannot modify the headers being sent back. I've tried JSONP, changing the headers I send, and a number of other solutions to no avail. I've read that making a server-side proxy could be a possible fit but I'm not sure how I would go about this. Any advice/code samples on possible solutions would be greatly appreciated. Thanks in advance.
What is to stop a malicious website from sending requests to your bank's web app and transferring all of your money? To prevent these types of shenanigans, if you're using a web browser, the server must explicitly state which remote origins are allowed to access a certain resource, if any.
If you need a key to access it, then CORS probably isn't enabled. You can always double check by looking at the response headers. See this:
https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Origin
So as others have already mentioned, you can get around this by making the request from your own server (where the headers don't identify it as a browser and subject to CORS limitations), and proxy it to your client side app.
Assuming you're using Node/Express, something like this should work:
const express = require('express');
const app = express();
const myHeaders = new Headers();
const myInit = { method: 'GET',
headers: myHeaders,
mode: 'cors',
cache: 'default' };
app.get('/ups/stuff/stuff', (req, res) => {
fetch('/ups/api/stuff', myInit)
.then(response => response.json())
.then(json => res.json(json);
});
app.listen(3000);
The native fetch API is neat because you can use it on both client and server, and it supports promises like jQuery.
https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch
having a problem with getting data back from database. I am trying my best to explain the problem.
1.If I leave "mode":"no-cors" inside the code below, then I can get data back from server with Postman, but not with from my own server. Thinking it has to be my client side error
When I remove "mode":"no-cors" then I am getting 2 errors:
-Fetch API cannot load http://localhost:3000/. Request header field access-control-allow-origin is not allowed by Access-Control-Allow-Headers in preflight response.
-Uncaught (in promise) TypeError: Failed to fetch
Quick Browsing suggested to put in the "mode":"no-cors" which fixed this error, but it does not feel right thing to do.
So I thought maybe somebody has a suggestion how to approach this problem.
Really hope I was clear enough, but pretty sure I am not giving clear explanation here :S
function send(){
var myVar = {"id" : 1};
console.log("tuleb siia", document.getElementById('saada').value);
fetch("http://localhost:3000", {
method: "POST",
headers: {
"Access-Control-Allow-Origin": "*",
"Content-Type": "text/plain"
},//"mode" : "no-cors",
body: JSON.stringify(myVar)
//body: {"id" : document.getElementById('saada').value}
}).then(function(muutuja){
document.getElementById('väljund').innerHTML = JSON.stringify(muutuja);
});
}
Adding mode:'no-cors' to the request header guarantees that no response will be available in the response
Adding a "non standard" header, line 'access-control-allow-origin' will trigger a OPTIONS preflight request, which your server must handle correctly in order for the POST request to even be sent
You're also doing fetch wrong ... fetch returns a "promise" for a Response object which has promise creators for json, text, etc. depending on the content type...
In short, if your server side handles CORS correctly (which from your comment suggests it does) the following should work
function send(){
var myVar = {"id" : 1};
console.log("tuleb siia", document.getElementById('saada').value);
fetch("http://localhost:3000", {
method: "POST",
headers: {
"Content-Type": "text/plain"
},
body: JSON.stringify(myVar)
}).then(function(response) {
return response.json();
}).then(function(muutuja){
document.getElementById('väljund').innerHTML = JSON.stringify(muutuja);
});
}
however, since your code isn't really interested in JSON (it stringifies the object after all) - it's simpler to do
function send(){
var myVar = {"id" : 1};
console.log("tuleb siia", document.getElementById('saada').value);
fetch("http://localhost:3000", {
method: "POST",
headers: {
"Content-Type": "text/plain"
},
body: JSON.stringify(myVar)
}).then(function(response) {
return response.text();
}).then(function(muutuja){
document.getElementById('väljund').innerHTML = muutuja;
});
}
In my case, the problem was the protocol. I was trying to call a script url with http instead of https.
try this
await fetch(url, {
mode: 'no-cors'
})
See mozilla.org's write-up on how CORS works.
You'll need your server to send back the proper response headers, something like:
Access-Control-Allow-Origin: http://foo.example
Access-Control-Allow-Methods: POST, PUT, GET, OPTIONS
Access-Control-Allow-Headers: Origin, X-Requested-With, Content-Type, Accept, Authorization
Bear in mind you can use "*" for Access-Control-Allow-Origin that will only work if you're trying to pass Authentication data. In that case, you need to explicitly list the origin domains you want to allow. To allow multiple domains, see this post
you can use solutions without adding "Access-Control-Allow-Origin": "*", if your server is already using Proxy gateway this issue will not happen because the front and backend will be route in the same IP and port in client side but for development, you need one of this three solution if you don't need extra code
1- simulate the real environment by using a proxy server and configure the front and backend in the same port
2- if you using Chrome you can use the extension called Allow-Control-Allow-Origin: * it will help you to avoid this problem
3- you can use the code but some browsers versions may not support that so try to use one of the previous solutions
the best solution is using a proxy like ngnix its easy to configure and it will simulate the real situation of the production deployment
Sometimes, please check your port number. If localhost port number is mismatch, you will get the same error as well.
I was getting this error and realized my server.js wasn't running.
I am sending data in json format from javascript using XMLHttpRequest and receiving data at node.js file as following
client.js
$.ajax({
type: "POST",
dataType: 'json',
data: JSON.stringify(Idata),
url: AjaxURL,
success: function (result) {
return true;
}
});
server.js
var http = require('http');
console.log("server initialized");
var server = http.createServer(function (req, response) {
req.on('data', function (data) {
var d = JSON.parse(data);
console.log("data : " + d.OperationType);
});
req.on('end', function () {
response.end();
});
}).listen(3000);
Now i successfully got the value of operationtype but the at client browser this error is showing:
XMLHttpRequest cannot load http://localhost:3000/. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost' is therefore not allowed access.
The primary problem is that you are making a "GET" request. A data payload/HTTP request body cannot be uploaded with an XMLHttpRequest "GET" request. See the specification of XMLHttpRequest: "If stored method is GET act as if the data argument is null."
In order to send data, you should make a "POST" request, in this case:
xmlhttp.open("POST", AjaxURL, true);
In response to updated question:
For security reasons, your AJAX request will not be allowed to access your server hosted at localhost:3000 unless the AJAX request is initiated from a page served by localhost:3000 OR you tell your server to allow "cross-origin" requests. The latter can be accomplished by adding a line like this to your server:
response.setHeader("Access-Control-Allow-Origin", "*");
This sets a header that tells the browser to allow AJAX requests to localhost:3000 even if the request is initiated from a page that was not served by localhost:3000.
Please do the following:
In client.js, output the value Idata(using console.log) right before sending it.
Use the 'Network' tab in Chrome(or equivalent in your favorite browser) to check what is actually being sent.
In server.js, output the value data(using console.log) right after recieving it.
That will probably give you a clue as to where it went wrong, which will help you determine your next course of action. If you still don't know how to proceed, update the question and I will update my answer.
What I can say right now based on my (very lacking) knowledge of node.js, is that if the variable data is being treated as an object, store += data; is not a good idea, as that would mean you are trying to add an object to an (empty) string.
(Probably resulting in the object returning "[object Object]", or some such)
I'm trying to do an AJAX request to https://developers.zomato.com/api/v2.1/search referring to Zomato API
The server has headers:
"access-control-allow-methods": "GET, POST, DELETE, PUT, PATCH, OPTIONS",
"access-control-allow-origin": "*"
The problem is that the API requires additional headers set for user-key. But whenever I set custom headers then chrome would do a pre-flight request by sending an OPTIONS request to the above URL which is failing, and thus the AJAX request is failing as well.
If I don't set the headers, then I don't get a CORS error, but rather a forbidden error from server since I'm not setting user-key header.
Any way to go about this catch-22 situation?
Both Jquery and JavaScript way are failing:
$(document).ready(function () {
$.ajax({
url: 'https://developers.zomato.com/api/v2.1/search',
headers: {
'Accept': 'application/json',
'user_key': 'XXXXX'
},
success: function (data) {
console.log(data);
}
});
});
var xhr = new XMLHttpRequest();
var url = 'https://developers.zomato.com/api/v2.1/search';
xhr.open('GET', url, false);
xhr.setRequestHeader('Accept', 'application/json');
xhr.setRequestHeader('user_key', 'XXXXXX');
xhr.send(null);
if (xhr.status == 200) {
console.log(xhr.responseText);
}
Error I'm getting:
OPTIONS https://developers.zomato.com/api/v2.1/search
XMLHttpRequest cannot load https://developers.zomato.com/api/v2.1/search. Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:8000' is therefore not allowed access. The response had HTTP status code 501.
If somebody wants to reproduce you can get a free user-key here:
https://developers.zomato.com/api
There does not appear to be a work-around for this issue from a browser. The CORS specification requires a browser to preflight the request with the OPTIONS request if any custom headers are required. And, when it does the OPTIONS preflight, it does not include your custom headers because part of what the OPTIONS request is for is to find out what custom headers are allowed to be sent on the request. So, the server is not supposed to require custom headers on the OPTIONS request if it wants this to work from a browser.
So, if the server is requiring the custom headers to be on the OPTIONS request, then the server is just expecting something that will not happen from a browser.
See related answers that describe more about this here:
jQuery CORS Content-type OPTIONS
Cross Domain AJAX preflighting failing Origin check
How do you send a custom header in a cross-domain (CORS) XMLHttpRequest?
Using CORS for Cross-Domain Ajax Requests
And, another user with the same issue here:
Zomato api with angular
It appears the Zomato is not browser friendly, but requires access from a server where you don't have CORS restrictions.
FYI, the error coming back from Zomato is 501 which means NOT IMPLEMENTED for the OPTIONS command. So, it looks like it's not only that the key is not being sent with the OPTIONS command, but that Zomato does not support the OPTIONS command, but that is required for the use of custom headers on a cross-origin request from a browser.
You can't bypass Access-Control-Allow-Headers in preflight response.
However as mentioned by #Jaromanda X in comments, Zomato sends:
Access-Control-Allow-Headers:X-Zomato-API-Key
...meaning you can only send this non-standard header from browser. Also don't go too low-level in request definition when jQuery has pretty and prepared shorthands ...
TL;DR Working example:
$.ajax({
type: "GET", //it's a GET request API
headers: {
'X-Zomato-API-Key': 'YOUR_API_KEY' //only allowed non-standard header
},
url: 'https://developers.zomato.com/api/v2.1/dailymenu', //what do you want
dataType: 'json', //wanted response data type - let jQuery handle the rest...
data: {
//could be directly in URL, but this is more pretty, clear and easier to edit
res_id: 'YOUR_RESTAURANT_OR_PLACE_ID',
},
processData: true, //data is an object => tells jQuery to construct URL params from it
success: function(data) {
console.log(data); //what to do with response data on success
}
});