I've got a pretty simple request set up to test CORS is working, as follows:
$.get( "http://www.otherdomain.com/thecontroller/test", function( data ) {
console.log(data);
});
I've even tried a vanilla javascript CORS request via the following..
var xhr = new XMLHttpRequest();
xhr.open('GET', 'http://www.otherdomain.com/thecontroller/test');
xhr.onreadystatechange = function () {
if (this.status == 200 && this.readyState == 4) {
console.log('response: ' + this.responseText);
}
};
xhr.send();
However these keeps getting the CORS error (No 'Access-Control-Allow-Origin' header is present on the requested resource) in both FF and Chrome.
On the server side I've tried quite a few things but for now just to test that I can get it working I'm allowing all CORS requests to the action, ie..
[EnableCors("*", "*", "*")]
public ActionResult Test()
{
//stuff
}
Am I missing something? Does Chrome / Firefox cache OPTIONS request? When I examine the network traffic in developer tools it only seems to be performing the GET method (and cancelling due to CORS) but it doesn't list any OPTIONS method.
I've even put a breakpoint in the Global.asax to try and catch the request, but it isn't even hitting it ie..
protected void Application_BeginRequest()
{
if (Request.HttpMethod == "OPTIONS")
{
//stuff
}
}
I'm at a bit of a loss now, any ideas?
A couple of thing you may try (caveat: I'm kinda green on this stuff, too)...
Did you enable CORS in your application Config class?
...
// Enable Cors
config.EnableCors();
...
Also, I think you're supposed to add the EnableCors[...] decorator on the controller, not the Action. At least that's how I've always done it.
namespace Your_API.Controllers
{
[EnableCors(origins: "*", headers: "*", methods: "*")]
public class YourController : ApiController
{
For reference: This is a pretty solid walk-through I used in the past to get my own project going
As mentioned in a comment under my question, it turns out that I had to add a custom header to the jquery axax request. This forced the browser to send the OPTIONS request. This seems a bit hacky to me but it works, here is the solution:
$.ajax({
type: 'GET',
url: 'http://www.otherdomain.com/thecontroller/test',
headers: {
'MyCustomHeader': 'important information'
},
success: function() {
console.log('success');
},
error: function() {
console.log('failure');
}
});
Related
I'm trying to do an ajax post request from my web app to my ASP.NET endpoint (everything locally) to insert data in my database but I'm always getting POST http://localhost:44326/radar/insertConfig net::ERR_CONNECTION_RESET.
What I've gattered so far: The endpoint works perfectly when called via Insomnia;
I have other GET requests working perfectly in the same conditions (same page, same domains, etc);
I have tested my POST request to a public endpoint and everything worked fine; The request never leaves the browser (or never reaches the destination). The issue only happens when sending a POST request to my API from my web application.
What I've tried: using another browser (Chrome, Edge, Firefox), deleting cookies, clearing cache, reseting my connections, reseting my computer, trying from a different connection.
Note1: I'm using a VPN and an antivirus that I cannot turn off (neither).
Note2: When done via firefox the error becomes: Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at http://localhost:44326/radar/insertConfig. (Reason: CORS request did not succeed).
My request:
var json = JSON.stringify(obj);
var url = "http://localhost:44326/radar/insertConfig";
$.post(url, json, function (status) {
if (status == "success")
alert("yay");
else
alert("nay");
}, "json");
The line at which the error happens: plugins.bundle.js:9838
xhr.send( options.hasContent && options.data || null );
What could be the possible problem?
Update: I added a middleware in my server that looks like this:
app.Use(async (context, next) =>
{
context.Response.Headers.Add("Access-Control-Allow-Origin", "*");
await next.Invoke();
});
But if I do that, then the return becomes 415 (unsupported media type). I tried to fix that by changing my request:
$.ajax({
url: url,
type: 'post',
data: json,
contentType: 'application/json',
dataType: 'json',
success: function (status) {
if (status == "success")
alert("yay");
else
alert("nay");
}
});
But then again, the CORS error returns...
from the error message it looks like your client and your server are from different domains, in order to be able to call your server from a different domain you need to enable CORS on your server.
for a asp.net server you can use Microsoft.AspNet.WebApi.Cors nuget package and add EnableCors attribute on your controller as follow
[EnableCors(origins: "http://{your-client-domain}", headers: "*", methods: "*")]
I had to change some stuff for this to work, here was my solution:
step 1: add this to the ConfigureServices and Configure (by default they're in the Startup.cs)
public void ConfigureServices(IServiceCollection services)
{
services.AddCors(options =>
{
options.AddPolicy(name: "MyPolicy",
builder =>
{
//This is how you tell your app to allow cors
builder.WithOrigins("*")
.WithMethods("POST", "DELETE", "GET")
.AllowAnyHeader();
});
});
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseCors("MyPolicy");
}
step 2: Change the request to $.ajax instead of $.post
$.ajax({
url: url,
type: "post",
crossDomain: true,
data: json,
contentType: "application/json",
dataType: "json"
});
I'm working on a tracker that should collect some data on the websites of our clients and send it to our api using fetch request when site users leave the page.
The idea was to use beforeunload event handler to send the request, but I've read here that In order to cover most browsers I also need to use unload event handler.
This is the relevant part of tracker code that our clients will put on their websites:
var requestSent = false;
function submitData(element_id, url) {
if (!requestSent) {
var data = JSON.stringify({ourobject});
fetch(url, {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type':'application/x-www-form-urlencoded',
},
body: data,})
.then(response => response.json())
.then((data) => {
console.log('Hello?');
requestSent = true;
});
}
}
window.addEventListener('beforeunload', function (e) { submitData(1, "https://oursiteurl/metrics");});
window.addEventListener('unload', function(event) {submitData(1, "https://oursiteurl/metrics"); });
I've tested this on chrome and both requests pass, instead of just the first one that is successful, this leads to duplicate data in our database.
After putting console log in next to the part where requestSent flag is set to true, I realized that part of the code never gets executed.
If I preserve logs in network tab, it says that both requests are canceled, even though the data gets to our endpoint
Our api is created in Codeigniter, here is the /metrics endpoint
public function submit () {
$this->cors();
$response = [
'status' => 'error',
'message' => 'No data',
];
$data = json_decode(file_get_contents('php://input'), true);
if (empty($data)) {
echo json_encode($response);exit();
}
// process data and do other stuff ...
Cors function:
private function cors() {
// Allow from any origin
if (isset($_SERVER['HTTP_ORIGIN'])) {
// Decide if the origin in $_SERVER['HTTP_ORIGIN'] is one
// you want to allow, and if so:
header("Access-Control-Allow-Origin: {$_SERVER['HTTP_ORIGIN']}");
header('Access-Control-Allow-Credentials: true');
header('Access-Control-Max-Age: 86400'); // cache for 1 day
}
// Access-Control headers are received during OPTIONS requests
if ($_SERVER['REQUEST_METHOD'] == 'OPTIONS') {
if (isset($_SERVER['HTTP_ACCESS_CONTROL_REQUEST_METHOD']))
// may also be using PUT, PATCH, HEAD etc
header("Access-Control-Allow-Methods: GET, POST, OPTIONS");
if (isset($_SERVER['HTTP_ACCESS_CONTROL_REQUEST_HEADERS']))
header("Access-Control-Allow-Headers: {$_SERVER['HTTP_ACCESS_CONTROL_REQUEST_HEADERS']}");
}
}
EDIT:
Thanks to #CBroe for suggesting to use the Beacon API, using it removed the need for both unload and beforeunload event handlers:
submitData now looks like this:
...
if (navigator.sendBeacon) {
let beacon = navigator.sendBeacon(url, data);
console.log( 'Beacon', beacon );
} else { // fallback for older browsers
if (!requestSent) {
console.log( 'Data object from fallback', data );
var xhr = new XMLHttpRequest();
xhr.open("POST", url, false); // third parameter of `false` means synchronous
xhr.send(data);
}
...
Doing it this way allowed me to only keep beforeunload event handler because it works both on ie and chrome:
window.addEventListener('beforeunload', function (e) { submitData(1, "https://oursiteurl/metrics");});
The idea was to use beforeunload event handler to send the request, but I've read here that In order to cover most browsers I also need to use unload event handler.
Both are not terribly suited to make AJAX/fetch requests, they are likely to get cancelled when the page actually unloads.
You should rather use the Beacon API, that was specifically made for this kind of tracking / keep-alive requests.
According to the browser compability list there on MDN, it is not supported by Internet Explorer yet though. If you need tracking for that as well, maybe go with a two-pronged approach - Beacon for the browsers that support it, an AJAX/fetch fallback for IE.
I am trying to access a public web service provided by USGS. According to the web page, they support CORS, and even provided a JQuery example (one thing worth to mention is that the example sets no header), but I tried everything and so far have no luck. There are lots of posts about cross-domain ajax and CORS on stackoverflow, but none has helped so far.
I tried both plain XMLHttpRequest and JQuery, with and without various headers, nothing worked. The plain one give back status code 0, which I believe it is an indicator that the request was blocked somewhere.
Anybody had successful experience with javascript CORS, either plain or with jquery?
<html>
<head>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.11.2/jquery.min.js"></script>
<script type="text/javascript">
function callWebService() {
var xmlhttp = new XMLHttpRequest();
xmlhttp.onreadystatechange = function() {
if (xmlhttp.readyState == 4) {
if (xmlhttp.status == 200) {
alert(xmlhttp.responseText);
} else {
alert(xmlhttp.status);
}
}
};
xmlhttp.open("GET", "http://waterservices.usgs.gov/nwis/iv/?format=json&sites=01646500¶meterCd=00060", true);
//xmlhttp.setRequestHeader("Accept","text/plain");
xmlhttp.setRequestHeader("Access-Control-Allow-Headers", "x-requested-with, x-requested-by, Content-Type");
xmlhttp.setRequestHeader("Access-Control-Allow-Origin", "*");
xmlhttp.setRequestHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
xmlhttp.setRequestHeader("Access-Control-Max-Age", "604800");
xmlhttp.setRequestHeader("X-Requested-With", "XMLHttpRequest");
xmlhttp.send();
}
function callWebServiceWithJQuery() {
$(document).ajaxError(
function (event, jqXHR, ajaxSettings, thrownError) {
alert('[event:' + objToString(event) + '], [jqXHR:' + objToString(jqXHR) + '], [ajaxSettings:' + objToString(ajaxSettings) + '], [thrownError:' + objToString(thrownError) + '])');
});
$.ajax({
/*beforeSend: function (request) {
request.setRequestHeader("Access-Control-Allow-Headers", "x-requested-with, x-requested-by, Content-Type");
request.setRequestHeader("Access-Control-Allow-Origin", "*");
request.setRequestHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
request.setRequestHeader("Access-Control-Max-Age", "604800");
},*/
url: "http://waterservices.usgs.gov/nwis/iv/?format=json&sites=01646500¶meterCd=00060",
dataType:'json',
data:'',
success: function(response) {
alert("succ");
alert(response);
},
error: function(a,b,c) {
alert("err");
alert(a);
alert(b);
alert(c);
}
});
}
</script>
</head>
<body>
<form>
<button onclick="callWebService();">Click me</button>
</form>
</body>
</html>
You have two problems.
Leaving the page
You are triggering the Ajax request in response to a submit button being clicked.
Immediately after sending the request, the form submits and you leave the page, which causes the browser to abort the Ajax request.
The usual way to prevent the form from submitting when you are using an onclick attribute is to return false; from it. Now we are in the 21st century, however, I urge you to learn about addEventListener and move on from onclick.
Making a non-simple request
You are setting a bunch of custom request headers. These all require that the browser makes a pre-flight OPTIONS request to ask permission to make a cross-domain Ajax request with custom headers. The server doesn't grant permission for that. Don't set the custom request headers.
X-Requested-With is a non-standard (albeit common) hack to let a server send different content based on if the request is from Ajax or not (typically switching between JSON and an HTML document, something better suited to the Accept header). It isn't needed here. Don't set it.
Access-Control-Allow-etc are response headers. The server you are making the request to must respond with them to tell the browser that your site is allowed to use Ajax to access it. You can't set them on the client, it would be ridiculous for a site to grant itself permission to access a different site. Don't try to set these.
I develop a project using backbone, underscore, require .js and REST API.
I've got error Cross-Origin Request Blocked The Same Origin Policy disallows reading the remote resource at...
NOTE : I've configured Access-Control-Allow in server side already. Still, the error still appear.
I found a solution, using ajaxPrefilter, then here I've tried in my project :
router.js :
start: function() {
Backbone.history.start({pushState: true});
},
initialize: function() {
$.ajaxPrefilter( function( options, originalOptions, jqXHR ) {
options.url = options.url;
});
}
Here is my backbone view :
var _wl = new MyModel();
_wl.save(_item,{
success: function(res) {
console.log(res);
}
});
I still got `Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at http://example.com/api/MyWebs. This can be fixed by moving the resource to the same domain or enabling CORS.
Could any one tell me what did I wrong here?
Any helps and ideas would be appreciated.
Can you check if the reponse from http://example.com/api/MyWebs carries back correctly configured CORS headers like "Access-Control-Allow-Origin" ,""Access-Control-Allow-Headers",""Access-Control-Allow-Methods" e.t.c. This can be done by having the dev tools open in chrome/firefox and inspecting the response headers.
Here are a couple of things to ensure if CORS has been configured properly:
Response Headers:
Check if you have configured the Access-Control-Allow-Origin header properly. Initially you can set the value of it to "*" for testing purpose and then later on specify the specific host.
If you are using custom headers make sure that you have added them to the allowed list using "Access-Control-Allow-Headers". The custom headers names are specifed as a comma separated list.
If you want to support PUT,DELETE and POST requests with certain media types, then make sure that you add "Access-Control-Allow-Methods".
Server Side:
Check if you have added code to handle the pre-flight request that comes with OPTIONS method.
EG:
if(containerRequestContext.getRequest().getMethod().equalsIgnoreCase("OPTIONS")) {
// code to check if the request is made from a allowed origin and if everything is fine abort with success / forbidden.
}
Check if each response sent from the server is made with the above response headers correctly set.
EG:
Language/Framework: Jersey Framework,Java
Each response will pass thru the below Filter and the following response headers will be added to them.
#PreMatching
#Provider
public class SecurityResponseFilter implements ContainerResponseFilter {
private static final Logger LOGGER = Logger.getLogger(SecurityResponseFilter.class);
#Override
public void filter(ContainerRequestContext containerRequestContext,ContainerResponseContext containerResponseContext) throws IOException {
try {
containerResponseContext.getHeaders().add("Access-Control-Allow-Origin", "*");
containerResponseContext.getHeaders().add("Access-Control-Allow-Methods", "GET,POST,PUT,DELETE");
containerResponseContext.getHeaders().add( "Access-Control-Allow-Headers", "tk_a,tk_r" );
} catch (Exception e) {
LOGGER.error("Error occured while processing request.",e);
throw e;
}
}
}
In the client side if you need to send custom headers, you could do it as follows :
$.ajaxPrefilter(function(options,originalOptions,jqXHR) {
jqXHR.setRequestHeader("tk_a",$.cookie("tk_a"));
jqXHR.setRequestHeader("tk_r",$.cookie("tk_r"));
});
I have an API hosted on one domain that has CORS enabled with the following headers:
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: POST, GET, OPTIONS
Access-Control-Max-Age: 1728000
I am able to make a GET or POST request from hackst.com and it works fine. Link: http://hackst.com/#w3SbV
From my backbone app hosted on another domain, GET requests work fine. But when I try to create and save a new model (i.e. make a POST request), it fails with the following error:
Failed to load resource: the server responded with a status of 501 (Not Implemented) http://projectwhatup.us:5000/api/posts
XMLHttpRequest cannot load http://projectwhatup.us:5000/api/posts. Origin http://ayush.projectwhatup.us is not allowed by Access-Control-Allow-Origin.
My relevant backbone code:
var newPostData = {
topic : "New Post",
body : "new body",
user_id : 1,
};
var newPostModel = new Post(newPostData);
this.model.create(newPostModel);
I even tried over-riding the create method and making a POST request manually like this:
create : function(data) {
console.log('overriden create');
$.ajax({
"url" : this.url,
"async" : true,
"beforeSend" : function(obj){
console.log(obj);
},
"contentType" : 'application/json',
//"crossDomain" : true, // uncommenting this doesnt help either
"headers" : {
},
"dataType" : 'json',
"type" : 'POST',
"data" : JSON.stringify(data),
"error" : function(err){
console.log('new post creation failed');
console.log(err);
},
"success" : function(resp){
console.log('new post created');
console.log(resp);
}
});
}
Same error.
I tried a stand-alone GET request on JSFiddle as well (http://jsfiddle.net/X9cqh/5/), but that fails even though my backbone app can make the GET request fine.
I'm completely clueless at this point. Any hints, pointers, solutions?
The server should also reply to the preflight with the following header:
Access-Control-Allow-Headers: Content-Type
This is necessary because the content type is application/json, which is outside the acceptable values defined in the CORS spec (http://www.w3.org/TR/cors/).
Your sever setup works. JSFiddle apparently does not make the ajax requests, but you can quickly test that it works by entering these four lines into Chrome console or Safari developer console:
var xhr = new XMLHttpRequest();
xhr.open('GET', 'http://projectwhatup.us:5000/api/posts', false);
xhr.send();
xhr.responseText;
If you try this with a domain that does not allow CORS, it will error out.
The reason that adding a 'Content-Type' header makes your CORS request fail is because your server is set up wrongly.
If the client wishes to specify particular headers or use an unusual http method verb (e.g. PUT), then the browser will first do a 'preflight' OPTIONS call to ensure that it is allowed to set those headers. Your server needs to respond to this OPTIONS call with the appropriate headers. You'll see that options call in the network tab of the Chrome developer tools or firebug if you want to confirm that this is what the problem is.
You may be interested in my more detailed answer here.