I'm having some major problems understanding why the Fetch API won't let me send a POST request to my restify server.
I have a basic restify server with a route that receives POST requests on /login.
This route works perfectly as intended if I test with Postman or HTTPRequester, but when I then test it on a browser application with the fetch API, I get the following error (in Chrome):
OPTIONS http://localhost:1337/login 405 (Method Not Allowed)
Fetch API cannot load http://localhost:1337/login. 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:3000' is therefore not allowed access. The response had HTTP status code 405. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.
Two issues
I specifically use the POSt method in my request, so why suddenly OPTIONS?
I've already set Access-Control-Allow-Origin: * on my server.
EDIT: I use restify v5.2.0
My server app:
const restify = require('restify');
const app = restify.createServer({
'name': 'API Token Test',
'version': '1.0.0'
});
app.use(restify.plugins.acceptParser(app.acceptable));
app.use(restify.plugins.bodyParser());
app.use(restify.plugins.jsonp());
app.use((req, res, next) => {
res.header('Access-Control-Allow-Origin', '*');
res.header('Access-Control-Allow-Headers', 'X-Requested-With');
return next();
});
app.post('/login', (req, res) => {
db.execute('SELECT idusers, password FROM users WHERE username = ?', [req.body.username], (selError, rows) => {
if (passwordHash.verify(req.body.password, rows[0].password)) {
crypto.randomBytes(256, (err, buf) => {
if (err) return res.status(500).end();
else {
const token = buf.toString('hex');
db.execute('INSERT INTO accesstokens SET userid = ?, token = ?', [rows[0].idusers, token], (insError) => {
if (insError) return res.status(500).end();
else return res.send({ "AccessToken": token });
});
}
});
} else {
res.status(401).end();
}
});
});
app.listen(1337);
(I've left out mysql stuffs and crypto/password-hash -requires, which are irrelevant to the problem)
And my clientside script:
(() => {
document.addEventListener('DOMContentLoaded', () => {
const form = document.querySelector('.loginForm');
form.onsubmit = () => {
const data = JSON.stringify({
'username': form.username.value,
'password': form.password.value
});
let headers = new Headers();
headers.set('Accept', 'application/json');
headers.set('Content-Type', 'application/json');
headers.set('Content-Length', data.length);
fetch('http://localhost:1337/login', {
'method': 'POST',
'headers': headers,
'mode': 'cors',
'cache': 'default',
'body': data
})
.then((result) => result.json())
.then((data) => {
console.log(data);
localStorage.setItem('token', data);
})
.catch((err) => {
console.log(err);
});
return false;
};
});
})();
Appears all CORS support has been moved to this module since restify v5.x.
Installing restify-cors-middleware and adding the following to my app worked:
const corsMiddleware = require('restify-cors-middleware');
const cors = corsMiddleware({
'origins': ['*']
});
app.pre(cors.preflight);
app.use(cors.actual);
Related
I can't enable CORS in GCF, allUsers is enabled. Here's my code following this post recommandations
I make a POST call with fetch and JSON as body.
My server supposed to handle the request by performing a reCaptcha verification.
Then respond based on the reCaptcha score.
Thing is I can't even make the request, my server returns status 500.
Emails are sent when sent with 'mode : no-cors'.
exports.contactSendmail = (req, res) => {
res.set('Access-Control-Allow-Origin', '*');
if (req.method === 'OPTIONS') {
/* handle preflight OPTIONS request */
res.set('Access-Control-Allow-Methods', 'GET, POST');
res.set('Access-Control-Allow-Headers', 'Content-Type, Accept');
// cache preflight response for 3600 sec
res.set('Access-Control-Max-Age', '3600');
return res.status(204);
}
const { message, token, email } = JSON.parse(req.body);
console.log(message, token, email);
// Load Node native HTTPS package
const https = require('https');
const sgMail = require('#sendgrid/mail');
sgMail.setApiKey(process.env.SENDGRID_API_KEY);
const recapatchaKeys = {
secret: `myhiddensecretkey`,
response: token,
};
const urlPath = `/recaptcha/api/siteverify?secret=${recapatchaKeys.secret}&response=${recapatchaKeys.response}`;
const recaptchaOptions = {
hostname: 'google.com',
// port: 443,
path: urlPath,
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Content-Length': 0,
},
};
const reqRecaptcha = https.request(recaptchaOptions, (recaptchaResponse) => {
console.log(`reCaptcha statusCode: ${recaptchaResponse.statusCode}`);
recaptchaResponse.on('data', (d) => {
process.stdout.write(d);
const recapatchaRes = JSON.parse(d);
if (recapatchaRes.score > 0.7) {
const msg = {
to: process.env.CONTACT_EMAIL_RECIPIENT,
from: email,
subject: 'Nouveau contact',
text: message,
// html: "<strong>Its too simple to send mail</strong>"
};
//ES8
(async () => {
try {
await sgMail.send(msg);
res.status(200).send('Email sent');
console.log('Email sent !');
} catch (err) {
console.error('Error with Sendgrid' + err.toString());
}
})();
} else {
res.status(403).send('Forbidden to send Email');
console.log('Forbidden to send Email');
}
});
});
reqRecaptcha.write('');
reqRecaptcha.end();
};
Here's my front call
const response = await fetch(process.env.CONTACT_SENDMAIL_URL, {
method: 'POST',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify(emailBody),
});
Any help would be appreciated
As mentioned in the comments, you are not handling any authentication in there. The Documentation states that:
you can either deploy it with the --allow-unauthenticated flag, or use the Console to grant the Cloud Functions Invoker role to allUsers. Then handle CORS and authentication in the function code.
And in order to handle authentication for end users you can in your code you can follow the instruction in this other piece of documentation, which is quite detailed.
I CAN'T SEND POST REQUEST FROM CLIENT SIDE !
I need to send HTTP Post request in Node js Rest API to the payment gateway.
Post request needs to have headers and body payload. I have frontend and backend separated, I'm using Rest API with express.js and payment gateway needs server to server communication so I can't to do that from client side. Basically when user clicks on payment I need to send call to my backend server then my backend server needs to send request to the payment gateway.
Payment gateway has documentation with only MVC ( Model View Controller ) and they really can't help.
So logic inside controller should be something like this
exports.payment = (req, res, next) => {
const { amount, id, currency } = req.body;
//add headers
//create body paloyad
//send request to https://payment....com/api-key/transaction
//receive response
res.status(200).json({ status: 'success' });
}
There are several ways to do it, like Fetch, however, I prefer using Axios:
const axios = require('axios');
exports.payment = (req, res, next) => {
const { amount, id, currency } = req.body;
//add headers
const options = {
headers: {'X-Custom-Header': 'value'}
};
//create body payload
const body = {
amount: amount
id: id
currency: currency
};
//send request to https://payment....com/api-key/transaction
axios.post('https://payment....com/api-key/transaction', body, options)
.then((response) => {
//receive response
console.log(response);
res.status(200).json({ status: 'success' });
})
.catch((error) => {
console.log(error)
});
Use Axios to send POST requests. Its much easier.
const axios = require('axios')
let config = {
headers: {
header1: value,
}
}
let data = {
'amount': amount,
'id':id,
'currency':currency,
}
axios.post('https://payment....com/api-key/transaction', data,config)
.then(function (response) {
console.log(response);
})
You can use https://github.com/node-fetch/node-fetch
const fetch = require('node-fetch');
const body = {a: 1};
const response = await fetch('https://httpbin.org/post', {
method: 'post',
body: JSON.stringify(body),
headers: {'Content-Type': 'application/json'}
});
const data = await response.json();
console.log(data);
I trying to send a request to an api and return the response with express when someone curl my site.
app.post('/', async (req, res) => {
request.get('the_api')
.on('response', function(resp){
return res.status(200).send({
text: resp.body
})
})
})
For that requirement, in my projects I use axios, from the documentation:
Axio is a promise based HTTP client for the browser and node.js.
Here is a complete example:
const axios = require('axios');
// api openexchange
const oexchange = axios.create({
baseURL: 'https://openexchangerates.org/api/',
timeout: 60000
})
oexchange.interceptors.request.use((config) => {
config.params = config.params || {};
config.params.app_id = 'myapitoken'
return config;
});
app.get('/', async (req, res) => {
try {
//here it will request:
//https://openexchangerates.org/api/latest.json?app_id=myapitoken
req.oexchange = await oexchange.get('latest.json')
return res.status(200).json(req.oexchange.data)
} catch (e) {
res.status(500).json({ errors: [{ location: 'cotacao', msg: 'Houve um erro ao acessar a api do open exchange.', param: 'openexchangerates' }] })
}
})
In my example I am requesting an external exchange api.
Here is the documentation from axios.
Hope it helps.
I'm using axios.post() to edit a mysql database on the back end of my Reactjs app. The data gets through the post request to the back end fine. But I need to know when the post request finished and return some data from it to know if what the back end code ran worked correctly. I've tried the following where newEdit is an object with the information that's need in the back end
axios
.post('http://ip:3001/edit_table', newEdit)
.then((response) => {
console.log("response: ",response);
}, (error) =>{
console.log("error: ",error)
});
Neither of the console log statements get ran. Once again, the object does get to the routed nodejs file fine, I am just unable to get any kind of response. Anyone know what's happening? thanks.
if your backend code is OK and return response then you can following below example that works perfectly.
const updateData = async () => {
try {
const response = await axios.put(`https://jsonplaceholder.typicode.com/posts/${id}`, {
method: 'PUT',
body: JSON.stringify({
id: id,
title: post.title,
body: post.body,
userId: 1
}),
headers: {
"Content-type": "application/json; charset=UTF-8"
}
})
.then(response => response.json())
.then(json => console.log(json));
console.warn(response.data);
} catch (error) {
console.warn(error);
}
};
Make sure that your backend is returning a response to the client.
You can either use res.send or res.json. res.send([body]) is used to send HTTP response to the client while res.json(body) is used to send JSON response.
res.send([body])
res.send(new Buffer('whoop'));
res.send({ some: 'json' });
res.send('<p>some html</p>');
Example:
var express = require('express')
var app = express()
app.get('/', function (req, res) {
res.send('hello world')
})
app.listen(3000)
res.json([body])
res.json(null)
res.json({ user: 'tobi' })
res.status(500).json({ error: 'message' })
Example:
var express = require('express')
var app = express()
app.get('/', function (req, res) {
res.json({ success: true })
})
app.listen(3000)
References:
Express API reference
Node.js response object methods res.send and res.json
I have the following route in my express app:
app.post("/users/me/trackers/court_cases", caseValidator, DriversController.court_cases);
I would like to be able to pass information from my second middleware, caseValidator, to the third set of middleware. The second middleware currently fetches JSON data from a RESTful API, that I would like to pass along to the final route before sending it to the user.
Here's my current case validator function:
caseValidator = function(req, res, next){
var case_id = req.body.case_id;
var authOptions = {
method: 'GET',
url: `https://www.courtlistener.com/api/rest/v3/dockets/${case_id}/`,
headers: {
'Authorization' : "myauth"
},
json: true
};
var url = `https://www.courtlistener.com/api/rest/v3/dockets/${case_id}/`
axios(authOptions)
.then((response) => {
console.log("success!")
next();
//// Pass in the JSON data to the next middleware?
})
.catch((error) => {
res.status(400)
.send(error)
});
};
you can use req.someVar.
axios(authOptions)
.then(response => {
console.log("success!");
req.someVar = response.data;
next();
})
then in next middleware you have access to that data.