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
Related
This question already has answers here:
No 'Access-Control-Allow-Origin' header is present on the requested resource—when trying to get data from a REST API
(26 answers)
Closed 3 years ago.
I'm trying to fetch data from a different origin to another server using Fetch API and I precise is from http to https
I can read the data from my browser but I don't know how to fetch them.
I already tried to set Access-Control-Allow-Origin to * but I still get this message :
I'm a little bit lost right know, Thank you for your support. 😁
const myHeaders = new Headers({
"Access-Control-Allow-Origin": "*",
"Content-Type": "application/json"
});
const fetchConfig = {
method: "GET",
headers: myHeaders,
mode: "cors",
cache: "no-cache"
};
function fetchData(url) {
fetch(url, fetchConfig)
.then(response => {
return response.json();
})
.then(data => {
console.log(data);
})
.catch(error => console.error(error));
}
fetchData("https://api.example.com/");
The Access-Control-Allow-Origin header needs to be set by the server you are retrieving the data from, in response to your request.
CORS Anywhere is a NodeJS proxy which adds CORS headers to the proxied request.
The URL to the proxy is literally taken from the path, validated and proxied. The protocol part of the proxied URI is optional, and defaults to "http". If port 443 is specified, the protocol defaults to "https".
This package does not put any restrictions on the http methods or headers, except for cookies. Requesting user credentials is disallowed. The app can be configured to require a header for proxying a request, for example, to avoid a direct visit from the browser.
You can simply add https://cors-anywhere.herokuapp.com/ at the beginning of your url.
Like this https://cors-anywhere.herokuapp.com/http://example.com/api/....
Check this link for more details: https://www.npmjs.com/package/cors-anywhere
I'm working on a project where I need first to get a authentication token from a server.
For this I need to send a GET request to the authorisation server with two key-value pairs included in the header: 1/ the client id (const) and 2/ a HMAC SHA1 calculated value based on client ID timestamp and so on.
This is working fine with Postman. (I calculate the sha1 on an online calculator)
Problem 1: (cryptojs client side)
As a node app I included the cryptojs library and the calculation works. But even with RequireJS I can not get cryptojs to run in the browser.
Error: Module name "crypto-js" has not been loaded yet for context: _. Use require([])
Problem 2: (cors)
Apparently chrome refuses the connection as the server does not accept all incoming connections.
Adding mode: 'no-cors' to the fetch request does not solve the problem.
Problem 3: (headers)
I need to add two key - value pairs to the get request headers. In postman this is no problem but I'm not sure this works with append or just adding them to my headers: { }
I constantly get a server error as if no headers where added.
I have already tried REquireJS for the cryptojs problem.
I have added the headers to a myHeaders object
const myHeaders = new Headers();
myHeaders.append('ClientID', CLIENTID);
myHeaders.append('Clientsecret', hashedToken);
and also just added the values to:
headers: {
...
'ClientID': CLIENTID,
'Clientsecret': hashedToken,
}
Both don't seem to help.
My code:
function getToken(){
hashedToken = getHashedSecret(); //this won't work client side as cryptojs can not be loaded
const CLIENTID = "CLIENTID";
const AUTHURL = "https://authorization.server.com/api/CLIENTID/authorization/";
var TIMESTAMP = getTimeStamp();
const myHeaders = new Headers();
// myHeaders.append('Content-Type', 'application/json');
myHeaders.append('ClientID', CLIENTID);
myHeaders.append('Clientsecret', hashedToken);
console.log(myHeaders);
let response = fetch(AUTHURL+TIMESTAMP, {
method: 'GET',
headers: {
myHeaders,
'Accept': 'application/json',
'Content-Type': 'application/json',
'Origin': '',
'Host': 'authorization.server.com',
include: 'ClientID', CLIENTID
},
mode: 'no-cors',
})
.then(response => response.json())
.then(data => {
console.log(data);
document.getElementById('output').innerHTML = data;
})
.catch(error => console.error(error))
console.log('data');
return data;
}
I should get a token from the server
It sounds like https://authorization.server.com doesn't allow access from your page's origin. Remember that in browsers, the Same Origin Policy prevents scripts from one origin from requesting information from other origins by default. (postman, not being a browser, is not subject to this restriction). This is so that scripts on Site A (a malicious actor) can't steal your personal information from Site B (perhaps your online banking) by making requests (from your browser, thus with your authentication information) to Site B.
For this to work, server code at https://authorization.server.com will need to respond to requests from the browser using the Cross-Origin Resource Sharing to allow access from your origin. You cannot do it from your client-side code (for obvious reasons).
Alternately, you can run a server on your origin, make the requests to that server, and it can make the requests to https://authorization.server.com and pass back the responses to you. Your server, not being a browser, is not subject to the SOP.
I am trying to access linkedin profile using axios get request, which doesn't work on localhost and I get the following error
XMLHttpRequest cannot load
https://api.linkedin.com/v1/people/~:(id,email-address)?format=json.
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:8030' is therefore not allowed
access. The response had HTTP status code 401.
I am able to get access-token using react-linkedin-login package, after getting the access token I am trying the following code
var linkedInUrl = `https://api.linkedin.com/v1/people/~:(id,email-address)?format=json`;
var headers = {
'Authorization': `Bearer ${accessToken}`,
'Access-Control-Allow-Methods':'GET,PUT,PATCH,POST,DELETE',
'Access-Control-Allow-Origin':'*',
'Access-Control-Request-Headers':'Origin, X-Requested-With, Content-Type, Accept',
'Content-Type':'application/x-www-form-urlencoded'
};
return (dispatch) => {
axios.get(linkedInUrl, {headers}).then(({data}) => {
console.log(data);
}, (error) => {
console.log(error);
});
}
The problems lies in linkedin server how it takes request I guess, it doesn't allow localhost to make call I think. How to overcome this to actually develop the service before I deploy and run on server.
Thanks for helping..
This is because of a browser restriction called the "Same-origin Policy", which prevents fetching data from, or posting data to, URLs that are part of other domains. You can get around it if the other domain supports Cross-origin Resource Sharing (CORS), but it looks like LinkedIn doesn't, so you may have trouble.
One way around this is to have a web service which can proxy your request to LinkedIn - there's no domain restrictions there.
https://en.wikipedia.org/wiki/Same-origin_policy
https://en.wikipedia.org/wiki/Cross-origin_resource_sharing
try jsonp for CORS request - reference - axios cookbook
var jsonp = require('jsonp');
jsonp(linkedInUrl, null, function (err, data) {
if (err) {
console.error(err.message);
} else {
console.log(data);
}
});
EDIT
Use jQuery to perform JSONP request and to set headers
$.ajax({url: linkedInUrl,
type: 'GET',
contentType: "application/json",
headers: header, /* pass your header object */
dataType: 'jsonp',
success: function(data) {
console.log(data);
},
error: function(err) {
console.log('Error', err);
},
});
https://cors-anywhere.herokuapp.com/ - Add this before the url and it will work
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!!
I try to make a GET request from an Angular2 app to a web service hosted on an IIS box on the corporate network. I spent a big part of the day reading up on CORS and preflight request. I got it to the point where the server sends back a 200 response to the preflight request but when the client does the second call to actually request the resource I get 401 Unauthorized.
I make the request like so:
let headers = new Headers({ 'withCredentials': true });
let options = new RequestOptions({ headers: headers });
return this.http.get(url, options)
.map(this.extractData)
.catch(this.handleError);
The preflight request/response
The following request/response
I wasn't sure if I had configure the security properly on the IIS box but when I tried with a simple call using jQuery/ajax I had no issues and got the data back using (withCredentials and crossDomain being key):
$.ajax({
type: "GET",
url: "http://xxx:8087/odata/Users",
xhrFields: {
withCredentials: true
},
crossDomain: true,
success: function(msg) {
...
},
error: function(e){
...
}
});
Looking at the linked image with request/response made with TypeScript is it something obvious I'm missing to make this work?