I am creating a web app with Node.js and Express. When I try to upload my project to Heroku, all of my content loads successfully on the page, but I get an error when I try to perform any action that requires an AJAX call to the API I'm working with:
Mixed Content: The page at
'https://farmers-market-finder.herokuapp.com/' was loaded over HTTPS,
but requested an insecure XMLHttpRequest endpoint
http://search.ams.usda.gov/farmersmarkets/v1/data.svc/zipSearch?zip=94118.
This request has been blocked; the content must be served over HTTPS.
I'm working with an API of farmer's market data, made by the USDA, and have followed their suggested format for RESTful AJAX requests:
$.ajax({
type: 'GET',
url: 'http://search.ams.usda.gov/farmersmarkets/v1/data.svc/mktDetail?id=' + id,
async: false,
success: function (data) { ... }
})
I have done a lot of research on Stack Overflow and other sites about this Mixed Content error. Most answers suggest that the 'GET' request route needs to be changed to "https://..." in order to work on Heroku's https server. I tried switching this, but the route to the API no longer works; I get a 404 Not Found error. I also tried switching my route to a relative link (just writing url: '//search.ams...') but that got the same 404 error. Seems like I really do need an http:// link to reach the API I'm trying to access.
Does anyone know of a way to get Heroku to allow me to use an "http" link, instead of https, or some other way to successfully make the request?
In your server.js file, add this code above your other app.use statements. (App = expr
app.use(function (req, res, next){
if (req.headers['x-forwarded-proto'] === 'https') {
res.redirect('http://' + req.hostname + req.url);
} else {
next();
}
});
Related
I am having some trouble with implementing authorization cookies.
When I make a GET request to my api (which is running on port 8080) from my react frontend (which is running on port 3000), the cookies which I set with express are not received in the frontend.
I'm implementing a subdomain per client type of application, and the cookies are rightfully received on the normal "localhost". However, when I try to use like example.localhost, the cookies are not received at all.
I tried to do it both with axios and normal fetches, and I just can't seem to get it to work.
I used Same-Site, Secure etc and set credentials to true both in CORS and Axios.
Here are a couple snippets:
app.get('/api/dashboard/overview', (req, res) => {
res.cookie("test", "test", {httpOnly: false}); //Already tried Secure etc
res.send('Cookie was set!');
});
getRequest('/dashboard/overview').then(response => {
console.log(response.data);
return response;
});
export function getRequest(url) {
return axios.get(apiUrl + url, {withCredentials: true, headers: { crossDomain: true, 'Content-Type': 'application/json' }});
}
I think the problem lies in the subdomain.localhost stuff, but sometimes the cookies do not load on normal localhost either.
I already tried to add a domain to vhosts, but it did not work either.
Hope one of you guys could help!
If your web server is running on localhost:8080, try to add this property to cors object. Might can do the trick.
origin: 'http://localhost:8080'
I'm finished a new REST API, I want to test it and send a GET request to ‘My XAMPP server url="http://127.0.0.1:8080/index.php?a=ad"’ in my chrome extension using AJAX.
I tried many times, But failed... So how i can do it?
Errors in the console:
Mixed Content: The page at 'https://www.google.com/' was loaded over HTTPS, but requested an insecure XMLHttpRequest endpoint 'http://127.0.0.1:8080/index.php?a=ad'. This request has been blocked; the content must be served over HTTPS.
CODE
$.ajax({
url: 'http://127.0.0.1:8080/index.php?a=ad',
success:function(data){
},
error: function() {
$('#notification-bar').text('An error occurred');
}
})
Changing the API URL to have HTTPS instead of HTTP should resolve this problem.
or
adding following code to the HTML page:
<meta http-equiv="Content-Security-Policy" content="upgrade-insecure-requests">
I have a website served up using Nginx. I've create a very simple web-page with a p tag to display the contents of a file, test.html. I have two buttons, one that does a GET request using $.ajax, and one that does a POST request using $.post.
The GET request works fine, and the contents of the file test.html display in my p tag. When I try to POST to that same file, however, I get an error in the console: "Failed to load resource: the server responded with a status of 405 (Not Allowed)". The POST request is pretty simple, taken right from the example on W3Schools.com - https://www.w3schools.com/JQuery/jquery_ajax_get_post.asp. So I am baffled.
I tried to read and understand what a 405 error could mean. Presumably it means that the POST request is not supported by this URL. But how would I enable it to be supported?
<p id="content-from-ajax"></p>
<button id="get-content-btn">Get Content</button>
<button id="post-something-btn">Post something</button>
<script type="text/javascript">
$("#get-content-btn").click(function() {
$.ajax({type: "GET",
url: "test.html",
success: function(result) {
$("#content-from-ajax").html(result);
alert("GET successful");
}
});
});
$("#post-something-btn").click(function(){
alert("GRRRR");
$.post("test.html",
{
name: "Donald Duck",
city: "Duckburg"
},
function(data, status){
alert("something worked");
});
});
</script>
For a POST request to access resources hosted on your web server you will need an application server. Examples include Laravel for PHP, Spring for Java, and Node for JavaScript.
Many application server require you to explicitly specify what type of request a particular endpoint can receive, this can be confusing when learning a new web application framework because a GET request is often the default.
Though a POST request must be handled by an application server it doesn't need to be one your hosting. So you can access public APIs with a POST request (depending on the API and the endpoint your using) without hosting your site on an application server. So if this project is purely educational, this is the best way to test using a POST request without going through the trouble of configuring one yourself.
I have a simple Node.js server up and running. This is the code:
var http = require('http');
var server = http.createServer();
server.on('request', function(req, res) {
res.writeHead(200, {
'content-type': 'text/plain'
});
res.write('Hello World!');
res.end();
})
server.listen(8090);
server.once('listening', function() {
console.log('Hello World server listening on port %d', 8090);
});
I can call this server using curl from the command line:
$curl localhost:8090
However, when I try to call it from a Vue application, I get an error. I have a Vue application running on localhost:8080, and I want to call my localhost:8090 server. My main.js Vue file is this:
import Vue from 'vue'
import resources from 'vue-resource'
Vue.use(resources)
import App from './components/App.vue'
import style from './styles/main.scss'
/**
* Root Vue instance
* #param {[element]} el: 'body' [Adds to the html body]
* #param {[component]} components: {app: App} [Renders ./component/App]
*/
new Vue({
el: 'body',
components: {
app: App
}
})
And this is the App component:
<template>
<h1>{{ msg }}</h1>
<input v-model="msg">
<button v-on:click="get">Call Server</button>
</template>
<script>
export default {
data: function() {
return {
msg: 'Hello World!'
}
},
methods: {
get: function() {
// GET request
this.$http({
url: 'localhost:8090',
method: 'GET'
}).then(function(response) {
console.log('ok');
}, function(response) {
console.log('failed');
});
}
}
}
</script>
When I click the button I get this error:
XMLHttpRequest cannot load localhost:8090. Cross origin requests are
only supported for protocol schemes: http, data, chrome,
chrome-extension, https, chrome-extension-resource.
When I try to call another server, like google.com, I get this error:
build.js:19188 GET http://localhost:8080/google.com 404 (Not Found)
So it seems like Vue is putting the localhost:8080 in front of the call, and maybe this is where my problem lies? Making server calls is completely new to me, I'm just playing around with Vue and want to learn Node.js while I do so.
This basically has nothing to do with Node or Vue and everything to do with how security in the browser is implemented. CORS is not a workaround. Read up on CORS to see why it is needed. This question of mine, which is quite similar to yours, has some good info in the answers sections as well. To be able to call an API without using CORS it needs to run on the same host, port and protocol, otherwise it will be blocked by your browser.
Years ago, before the advent of CORS, you needed to use JSONP to achieve the same. You can of course have a look at it to see how this works, but nowadays there is very little need for that technique as we have proper cross-domain support in the form of CORS.
Regarding your question in one of the comment sections on "How do people call API's when working with Vue.js?", they do one of the following:
Run the API on another server (such as api.mydomain.com), but set the CORS headers on the response.
As above, but the client and server wraps responses using the JSONP method mentioned above.
Run the API on the same server as the one serving pages. This means api calls will be done against an endpoint such as localhost:8080/api
Twist on #3: just proxy calls coming in on the server to another server. Meaning you can have your api server running elsewhere, but your main server that is accepting calls on /api will just send these requests on the the next server after stripping off the /api prefix. Usually, people either setup an Apache or Nginx instance in front of your app server and do the actual proxying on that, but you can also do it in your app server, using something like node-proxy.
You can probably read this through the lines already, but save yourself some trouble (and time) and just use CORS :) #trquoccuong has the details on doing this in his answer.
CORS problems, you can use cors node module or add request header
if use Express
res.header('Access-Control-Allow-Origin', 'http://localhost:8080');
res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE,OPTIONS');
res.header('Access-Control-Allow-Headers', 'Content-Type, Authorization, Content-Length, X-Requested-With');
if use http module
res.setHeader
I tried the following code in Postman and it was working. Is there something wrong with the code?
$.ajax({
url: 'http://api.example.com/users/get',
type: 'POST',
headers: {
'name-api-key':'ewf45r4435trge',
},
data: {
'uid':36,
},
success: function(data) {
console.log(data);
}
});
I got this error in my console as below, please advise.
XMLHttpRequest cannot load http://api.example.com/users/get Response
for preflight is invalid (redirect)
I received the same error when I tried to call https web service as http webservice.
e.g when I call url 'http://api.example.com/users/get'
which should be 'https://api.example.com/users/get'
This error is produced because of redirection status 302 when you try to call http instead of https.
This answer goes over the exact same thing (although for angular) -- it is a CORS issue.
One quick fix is to modify each POST request by specifying one of the 'Content-Type' header values which will not trigger a "preflight". These types are:
application/x-www-form-urlencoded
multipart/form-data
text/plain
ANYTHING ELSE triggers a preflight.
For example:
$.ajax({
url: 'http://api.example.com/users/get',
type: 'POST',
headers: {
'name-api-key':'ewf45r4435trge',
'Content-Type':'application/x-www-form-urlencoded'
},
data: {
'uid':36,
},
success: function(data) {
console.log(data);
}
});
The error indicates that the preflight is getting a redirect response. This can happen for a number of reasons. Find out where you are getting redirected to for clues to why it is happening. Check the network tab in Developer Tools.
One reason, as #Peter T mentioned, is that the API likely requires HTTPS connections rather than HTTP and all requests over HTTP get redirected. The Location header returned by the 302 response would say the same url with http changed to https in this case.
Another reason might be that your authentication token is not getting sent, or is not correct. Most servers are set up to redirect all requests that don't include an authentication token to the login page. Again, check your Location header to see if this is where you're getting sent and also take a look to make sure the browser sent your auth token with the request.
Oftentimes, a server will be configured to always redirect requests that don't have auth tokens to the login page - including your preflight/OPTIONS requests. This is a problem. Change the server configuration to permit OPTIONS requests from non-authenticated users.
Please set http content type in header and also make sure the server is authenticating CORS. This is how to do it in PHP:
//NOT A TESTED CODE
header('Content-Type: application/json;charset=UTF-8');
header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Methods: DELETE, HEAD, GET, OPTIONS, POST, PUT');
header('Access-Control-Allow-Headers: Content-Type, Content-Range, Content-Disposition, Content-Description');
header('Access-Control-Max-Age: 1728000');
Please refer to:
http://www.w3.org/TR/cors/#cross-origin-request-with-preflight-0
How does Access-Control-Allow-Origin header work?
My problem was that POST requests need trailing slashes '/'.
I had the same problem and it kept me up for days. At the end, I realised that my URL pointing to the app was wrong altogether.
example:
URL: 'http://api.example.com/'
URL: 'https://api.example.com/'.
If it's http or https verify.
Check the redirecting URL and make sure it's the same thing you're passing along.
I had the same error, though the problem was that I had a typo in the url
url: 'http://api.example.com/TYPO'
The API had a redirect to another domain for all URL's that is wrong (404 errors).
So fixing the typo to the correct URL fixed it for me.
My problem was caused by the exact opposite of #ehacinom. My Laravel generated API didn't like the trailing '/' on POST requests. Worked fine on localhost but didn't work when uploaded to server.