CORS / xhr.getRequestHeaders - javascript

Greetings,
I am trying to use CORS (http://www.w3.org/TR/2009/WD-cors-20090317/#access-control-allow-methods-header) for an application on Safari, and when I try to read the response headers from the XMLHTTPRequest, I only receive the Content-Type. None of the other quite standard headers gets through, and I cannot figure out how to get this to work.
Anyone would happen to know how to fix this issue? Could this be a WebKit bug?
Edit
here is the config i use with nGinx:
add_header Access-Control-Allow-Origin *;
add_header Access-Control-Allow-Headers Cache-Control,Pragma,Date;
add_header Access-Control-Allow-Methods GET,POST;

In order for headers to be exposes to JS, you need to set the Access-Control-Expose-Headers header to a comma-separated list of headers you want to expose.
Unfortunately, this header is poorly supported. Mozilla only implemented it in Firefox 4, Webkit as of this moment still does not implement it. I am not sure about IE8 and above (google didn't turn up anything useful, and I don't have them around to test with myself).
(see also eg. Restrictions of XMLHttpRequest's getResponseHeader()? )

Have you verified that your server is actually emitting the Cache-Control, Pragma and Date headers? Perhaps set up a Wireshark trace on the client to see the actual HTTP headers that are being exchanged?

I've been in same situation yesterday. https://stackoverflow.com/users/713326/gijs gave you the right answer but there is another part that is specific to nginx that you have to take care.
"add header" is working only in the case where the response from a service is successful (200, 204, 301, 302 or 304). You have to do a custom build of nginx to include HttpHeadersMoreModule
(http://wiki.nginx.org/HttpHeadersMoreModule). After you have to replace add_header with more_set_headers.
Example:
more_set_headers 'Access-Control-Allow-Origin: $http_origin';
more_set_headers 'Access-Control-Allow-Credentials: false';
more_set_headers 'Access-Control-Allow-Methods: GET, POST, OPTIONS, HEAD, PUT, PATCH, DELETE';
more_set_headers 'Access-Control-Allow-Headers:Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Accept,Authorization;
more_set_headers 'Access-Control-Expose-Headers: Location';

REQUEST:
$.ajax({
url: "http://localhost:8079/students/add/",
type: "POST",
crossDomain: true,
data: JSON.stringify(somejson),
dataType: "json",
success: function (response) {
var resp = JSON.parse(response)
alert(resp.status);
},
error: function (xhr, status) {
alert("error");
}
});
RESPONSE:
response = HttpResponse(json.dumps('{"status" : "success"}'))
response.__setitem__("Content-type", "application/json")
response.__setitem__("Access-Control-Allow-Origin", "*")
return response

Related

Api request blocked because of CORS header Access-Control-Allow-Origin missing [duplicate]

I'm calling this function from my asp.net form and getting following error on firebug console while calling ajax.
Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at http://anotherdomain/test.json. (Reason: CORS header 'Access-Control-Allow-Origin' missing).
var url= 'http://anotherdomain/test.json';
$.ajax({
url: url,
crossOrigin: true,
type: 'GET',
xhrFields: { withCredentials: true },
accept: 'application/json'
}).done(function (data) {
alert(data);
}).fail(function (xhr, textStatus, error) {
var title, message;
switch (xhr.status) {
case 403:
title = xhr.responseJSON.errorSummary;
message = 'Please login to your server before running the test.';
break;
default:
title = 'Invalid URL or Cross-Origin Request Blocked';
message = 'You must explictly add this site (' + window.location.origin + ') to the list of allowed websites in your server.';
break;
}
});
I've done alternate way but still unable to find the solution.
Note: I've no server rights to make server side(API/URL) changes.
This happens generally when you try access another domain's resources.
This is a security feature for avoiding everyone freely accessing any resources of that domain (which can be accessed for example to have an exact same copy of your website on a pirate domain).
The header of the response, even if it's 200OK do not allow other origins (domains, port) to access the resources.
You can fix this problem if you are the owner of both domains:
Solution 1: via .htaccess
To change that, you can write this in the .htaccess of the requested domain file:
<IfModule mod_headers.c>
Header set Access-Control-Allow-Origin "*"
</IfModule>
If you only want to give access to one domain, the .htaccess should look like this:
<IfModule mod_headers.c>
Header set Access-Control-Allow-Origin 'https://my-domain.example'
</IfModule>
Solution 2: set headers the correct way
If you set this into the response header of the requested file, you will allow everyone to access the resources:
Access-Control-Allow-Origin : *
OR
Access-Control-Allow-Origin : http://www.my-domain.example
Server side put this on top of .php:
header('Access-Control-Allow-Origin: *');
You can set specific domain restriction access:
header('Access-Control-Allow-Origin: https://www.example.com')
in your ajax request, adding:
dataType: "jsonp",
after line :
type: 'GET',
should solve this problem ..
hope this help you
If you are using Express js in backend you can install the package cors, and then use it in your server like this :
const cors = require("cors");
app.use(cors());
This fixed my issue
This worked for me:
Create php file that will download content of another domain page without using js:
<?
//file name: your_php_page.php
echo file_get_contents('http://anotherdomain/test.json');
?>
Then run it in ajax (jquery). Example:
$.ajax({
url: your_php_page.php,
//optional data might be usefull
//type: 'GET',
//dataType: "jsonp",
//dataType: 'xml',
context: document.body
}).done(function(data) {
alert("data");
});
You have to modify your server side code, as given below
public class CorsResponseFilter implements ContainerResponseFilter {
#Override
public void filter(ContainerRequestContext requestContext, ContainerResponseContext responseContext)
throws IOException {
responseContext.getHeaders().add("Access-Control-Allow-Origin","*");
responseContext.getHeaders().add("Access-Control-Allow-Methods", "GET, POST, DELETE, PUT");
}
}
You must have got the idea why you are getting this problem after going through above answers.
self.send_header('Access-Control-Allow-Origin', '*')
You just have to add the above line in your server side.
In a pinch, you can use this Chrome Extension to disable CORS on your local browser.
Allow CORS: Access-Control-Allow-Origin Chrome Extension

Cross Origin Header Missing with jQuery Get Request but not with Python or in Browser [duplicate]

I'm calling this function from my asp.net form and getting following error on firebug console while calling ajax.
Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at http://anotherdomain/test.json. (Reason: CORS header 'Access-Control-Allow-Origin' missing).
var url= 'http://anotherdomain/test.json';
$.ajax({
url: url,
crossOrigin: true,
type: 'GET',
xhrFields: { withCredentials: true },
accept: 'application/json'
}).done(function (data) {
alert(data);
}).fail(function (xhr, textStatus, error) {
var title, message;
switch (xhr.status) {
case 403:
title = xhr.responseJSON.errorSummary;
message = 'Please login to your server before running the test.';
break;
default:
title = 'Invalid URL or Cross-Origin Request Blocked';
message = 'You must explictly add this site (' + window.location.origin + ') to the list of allowed websites in your server.';
break;
}
});
I've done alternate way but still unable to find the solution.
Note: I've no server rights to make server side(API/URL) changes.
This happens generally when you try access another domain's resources.
This is a security feature for avoiding everyone freely accessing any resources of that domain (which can be accessed for example to have an exact same copy of your website on a pirate domain).
The header of the response, even if it's 200OK do not allow other origins (domains, port) to access the resources.
You can fix this problem if you are the owner of both domains:
Solution 1: via .htaccess
To change that, you can write this in the .htaccess of the requested domain file:
<IfModule mod_headers.c>
Header set Access-Control-Allow-Origin "*"
</IfModule>
If you only want to give access to one domain, the .htaccess should look like this:
<IfModule mod_headers.c>
Header set Access-Control-Allow-Origin 'https://my-domain.example'
</IfModule>
Solution 2: set headers the correct way
If you set this into the response header of the requested file, you will allow everyone to access the resources:
Access-Control-Allow-Origin : *
OR
Access-Control-Allow-Origin : http://www.my-domain.example
Server side put this on top of .php:
header('Access-Control-Allow-Origin: *');
You can set specific domain restriction access:
header('Access-Control-Allow-Origin: https://www.example.com')
in your ajax request, adding:
dataType: "jsonp",
after line :
type: 'GET',
should solve this problem ..
hope this help you
If you are using Express js in backend you can install the package cors, and then use it in your server like this :
const cors = require("cors");
app.use(cors());
This fixed my issue
This worked for me:
Create php file that will download content of another domain page without using js:
<?
//file name: your_php_page.php
echo file_get_contents('http://anotherdomain/test.json');
?>
Then run it in ajax (jquery). Example:
$.ajax({
url: your_php_page.php,
//optional data might be usefull
//type: 'GET',
//dataType: "jsonp",
//dataType: 'xml',
context: document.body
}).done(function(data) {
alert("data");
});
You have to modify your server side code, as given below
public class CorsResponseFilter implements ContainerResponseFilter {
#Override
public void filter(ContainerRequestContext requestContext, ContainerResponseContext responseContext)
throws IOException {
responseContext.getHeaders().add("Access-Control-Allow-Origin","*");
responseContext.getHeaders().add("Access-Control-Allow-Methods", "GET, POST, DELETE, PUT");
}
}
You must have got the idea why you are getting this problem after going through above answers.
self.send_header('Access-Control-Allow-Origin', '*')
You just have to add the above line in your server side.
In a pinch, you can use this Chrome Extension to disable CORS on your local browser.
Allow CORS: Access-Control-Allow-Origin Chrome Extension

AJAX request with headers failing

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
}
});

ExtJS 4.2.1: Cannot retrieve HTTP Response headers upon Ext.ajax.request callback

I'm trying to read the headers of the coming response upon Ext.ajax.request.
Here it is the code:
Ext.Ajax.request({ url: 'http://localhost:3000/v0.1/login' ,
method: 'POST',
scope:this,
jsonData: {"_username":username,"_userpwd":password},
success: function(responseObject){
var headers = responseObject.getAllResponseHeaders();
console.info(headers );
Ext.destroy(Ext.ComponentQuery.query('#loginWindow'));
this.application.getController('SiteViewController').showView();
},
failure: function(responseObject){
alert(responseObject.status);
}
});
But the only header that it is printed out in console is:
Object {content-type: "application/json; charset=utf-8"}
All the other headers are missing, but they are present in the chrome inspector!!!
What am I missing? Thanks
Because you're probably doing a cross-domain request, you will only have headers explicitly exposed by the server. Same domain requests expose all the headers.
On the server side you have to add the header "Access-Control-Expose-Headers" with the exhaustive list of headers you want to expose, separated by a coma. In php it would look like this:
header("Access-Control-Expose-Headers: Content-length, X-My-Own-Header");
The headers will indeed be available through responseObject.getAllResponseHeaders() or something like responseObject.getResponseHeader('content-type').
More information about cross-domain requests and headers: http://www.html5rocks.com/en/tutorials/cors/
PS: Ace.Yin had the right answer, but I don't have enough reputation to simply comment.
i ran into the same issue and finally i found the solution here: http://www.html5rocks.com/en/tutorials/cors/
here is the part about the headers:
Access-Control-Expose-Headers (optional) -
The XMLHttpRequest 2 object has a getResponseHeader() method that returns the value of
a particular response header. During a CORS request, the getResponseHeader() method
can only access simple response headers.
Simple response headers are defined as follows:
Cache-Control
Content-Language
Content-Type
Expires
Last-Modified
Pragma
If you want clients to be able to access other headers, you have to use the
Access-Control-Expose-Headers header. The value of this header is a comma-delimited
list of response headers you want to expose to the client.
i have not verify it yet, but it seems on the right track :)

AngularJS: POST Data to External REST API

I have a basic AngularJS service setup like so:
app.factory('User', function($resource) {
return $resource('http://api.mysite.com/user/:action:id/:attr', {}, {
history: {
method: 'GET',
params: {
attr: 'history'
}
},
update: {
method: 'POST',
params: {
name: 'test'
}
}
});
});
and I use it like this:
User.history({id: 'testID'}, function(data) {
console.log('got history');
console.log(data);
});
User.update({id: 'me'}, function(data) {
console.log('updated');
console.log(data);
});
Problem one: User.update(), despite having the method set to POST, keeps sending OPTIONS as the request method.
Though Chrome Dev tools reports the request header Access-Control-Request-Method:POST is sent as well (Not sure if that means anything).
Problem two: I keep getting an error with CORS, despite having these headers set in the API code:
header('Access-Control-Allow-Origin: *');
header("Access-Control-Allow-Methods: PUT, GET, POST, DELETE, OPTIONS");
This problem only shows up though if making a non-GET request.
What's the proper way to be handling this? I've also looked into JSONP, but with this being a RESTful api, I'm not sure how to get around the problems with only GET support.
Your two problems are actually one problem. The OPTIONS request is part of the CORS process. For POST requests, the browser first sends an OPTIONS call, and the server responds if it is okay to execute it.
If the OPTIONS request fails, Angular / Chrome shows you the reason in the console. For example:
OPTIONS https://*** Request header field Content-Type is not allowed by Access-Control-Allow-Headers. angular.min.js:106
XMLHttpRequest cannot load https://***. Request header field Content-Type is not allowed by Access-Control-Allow-Headers.
You probably have to set Access-Control-Allow Headers on the server, too:
header('Access-Control-Allow-Headers: Content-Type, x-xsrf-token')
x-xrsf-token is for angular' to prevent CSRF. You may have to add more headers, depending on what you send from the client.
Here is a very good guide on CORS: https://developer.mozilla.org/en-US/docs/HTTP/Access_control_CORS
In AngularJS to make CORS working you also have to overwrite default settings of the angular httpProvider:
var myApp = angular.module('myApp', [
'myAppApiService']);
myApp.config(['$httpProvider', function($httpProvider) {
$httpProvider.defaults.useXDomain = true;
delete $httpProvider.defaults.headers.common['X-Requested-With'];
}
]);
Just setting useXDomain to true is not enough. AJAX request are also
send with the X-Requested-With header, which indicate them as being
AJAX. Removing the header is necessary, so the server is not rejecting
the incoming request.
Note: Answer only works for older AngularJS version previous to 1.2. With 1.2 and above you don't have to do do anything to enable CORS.
Better to solve this problem at the server. On apache you can solve it like this in a .htaccess file. This is a source of pain for angular development and can be solved in angular as well but its probably not the best way to do it.
Header set Access-Control-Allow-Origin "*"
Header add Access-Control-Allow-Headers "origin, x-requested-with, content-type"
Header add Access-Control-Allow-Methods "PUT, GET, POST, DELETE, OPTIONS"

Categories