I'm new to Express and trying to create a login authentication system. But I'm having issues getting XHR to show a ready state 4. It was working fine until I added express.Router which I saw in a GitHub project.
These are my relevant bits of code:
// server.js
const userAuth = require("./auth");
app.use(cors());
app.use(bodyParser.json());
app.use("/auth", userAuth);
// ./auth/index.js
const router = require("express").Router();
const userLogin = require("./userLogin");
const userRegister = require("./userRegister");
router.post("/login", userLogin);
router.post("/register", userRegister);
module.exports = router;
// ./auth/userLogin.js
module.exports = (req, res) => {
const { username, password } = req.body;
console.log(username, password);
res.status(200).send("hello");
};
// client side
const http = new XMLHttpRequest();
http.open("post", "https://example.com/auth/login");
http.setRequestHeader("Content-type", "application/json");
http.onreadystatechange = () => {
if (!(http.readyState === 4 && http.status === 200)) {
console.error("Could not submit form.");
console.log(http.responseText);
}
};
http.send(JSON.stringify({ username, password }));
The chrome console shows "State 2", then "State 3" plus the "hello" response. But there is no "State 4"?
The code is pretty much identical to the linked Github project (theirs have async in userLogin.js but I've tried that too).
Any help/tips much appreciated!
change your onreadystatechange method to this and it will work:
http.onreadystatechange = function () {
// rest of your code
}
This is a template you can refer back to if needed:
const http = new XMLHttpRequest();
http.open("post", "https://example.com/auth/login");
http.setRequestHeader("Content-type", "application/json");
http.onreadystatechange = function () { // change this line
if (http.readyState === 4 && http.status === 200) {
console.log(http.responseText);
}
else {
console.error("Error in submitting or receiving response.");
}
};
http.send(JSON.stringify({ username, password }));
if this answer helps solving the issue, consider accepting the answer or upvoting it. Thank you.
Related
This question already has answers here:
Pass Data to Service in Axios
(1 answer)
axios post request from frontend not recieving in node js backend
(3 answers)
Closed 18 days ago.
I'm using classic Javascript ajax to send a request with a payload to my backend (node.js). On the backend, I get the request but I'm not finding the payload in the request. I need help with this (please).
Here's the ajax call:
function signIn() {
const username = document.getElementById('username');
const password = document.getElementById('password');
ajax('auth/signin', 'POST', {username: username.value, password: password.value});
}
function ajax(endpoint, method, payload) {
const xhttp = new XMLHttpRequest();
const url = 'http://localhost:3000/' + endpoint;
xhttp.onreadystatechange = function() {
console.log('readyState: ', this.readyState);
if (this.readyState === 4) {
if (this.status === 200) {
console.log('success!');
} else {
console.log('error: ', JSON.stringify(this));
}
}
};
xhttp.open(method, url, true);
xhttp.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
if (payload) {
xhttp.send(payload);
} else {
xhttp.send();
}
}
On the backend, I print out the request like this:
router.post('/signin', (req, res, next) => {
console.log('signing in');
fs.appendFileSync('log.txt', new Date().toString() + ': in auth.js : router.post/signin\n' + util.inspect(req, {depth: 7}));
});
This writes the request 7 layers deep to log.txt. I open log.txt, I see the request in all its gory detail, but when I do a search for the username or password I entered, I find nothing.
So either the payload (username/password) is not getting to the backend with the request, or it's more than 7 layers deep. I'm assuming it's the former, not the latter.
Is there anything wrong with how I'm making my ajax call? Is this not how you send a payload along with a request? Is it something else I'm doing wrong? Thanks!
Here is my frontend JS. As a side note, FormData.append() does not work, so I have to set its properties explicitly. #data is an object with input name: value properties.
function http(data) {
const httpData = new FormData();
for (let prop in data) {
httpData[prop] = data[prop];
}
const xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function () {
if (this.readyState === 4) {
const status = this.status;
const data = this.responseText;
}
};
xhttp.open('POST', '/api/login');
xhttp.setRequestHeader('Content-Type', 'x-www-form-urlencoded');
xhttp.send(httpData);
}
And on the server side
app.use(express.urlencoded({
extended: true
}));
express ready to parse form data, and in the /routes/api.js ( I am using express router )
router.post('/login', (req, res, next) => {
console.log(req.body);
res.end();
});
I got req.body as an {}
Where did I fuck up ?
Here is the working code, courtesy of #Quentin, frontend
function http(data) {
const httpData = new FormData();
for (let prop in data) {
httpData.append(prop, data[prop]);
}
const xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function () {
if (this.readyState === 4) {
const status = this.status;
const data = this.responseText;
}
};
xhttp.open('POST', '/api/login');
xhttp.send(httpData);
}
and backend in /routes/api.js
const formidable = require('formidable');
router.post('/login', (req, res, next) => {
const form = formidable({ multiples: true });
form.parse(req, (err, fields, files) => {
console.log(fields);
res.end();
});
});
You have three problems.
append() does work. It just doesn't store the data in a way that shows up with console.log. Assigning arbitrary properties doesn't work.
You need to send the correct Content-Type header. This is multipart/form-data with a mandatory boundary parameter that you have no way of knowing. Don't try to set the Content-Type header manually. XMLHttpRequest will generate it automatically from the FormData object.
You need a body parser for Express that supports multipart/form-data. The built-in one does not. The documentation for it on npm suggests 4 alternatives.
I have this problem because I am sending data via JavaScript using XMLHttpRequest but not receiving it in node.js express post method. I don't know what I'm doing wrong.
My script - funcion form execute on window load
function form() {
const btn = document.getElementById("save-data");
let currentPage = window.location.href.split("/").at(-1);
function sendData(data) {
const XHR = new XMLHttpRequest();
XHR.open("POST", window.location.href);
XHR.setRequestHeader("Content-Type", "application/json");
console.log(data); // I get right data
XHR.send(data);
}
btn.addEventListener("click", () => {
switch (currentPage) {
case "settings":
const prefix = document.getElementById("prefix").value;
let buttonState = document.getElementById("module-state");
if (buttonState.hasAttribute("enable")) {
buttonState = "enable";
} else {
buttonState = "disable";
}
sendData({
prefix: prefix,
buttonState: buttonState,
announcements: {
giveawayChannelID: 123,
updateChannelID: 123,
},
});
}
});
}
My Node.js code
router.post('/:id/settings', (req, res) => {
console.log(req.body) // {}
})
Pleas help.
You need to install body-parser package by NPM, and require it in your code
Try
npm i body-parser
Then in your main node script, require it
const bodyParser = require('body-parser');
after that, you should set it as a middleware for express
app.use(bodyParser.json()); //Handles JSON requests
app.use(bodyParser.urlencoded({ extended: false })); //Handles normal post requests
after these steps, you can access values like req.body.value
Im having trouble sending form data to my local NodeJS express server. I believe it has something to do with how I'm formatting my request. I am trying to register a new user account to a Postgresql database, using Passport as middleware, although I dont believe the code ever makes it that far.
Chrome DevTools Network Tab gives me additional info about the bad request
{"status":"Cannot read property 'length' of undefined"}
When a user hits 'Create Account', this code is fired off:
processForm(event) {
// prevent default action. in this case, action is the form submission event
event.preventDefault();
// create a string for an HTTP body message
const name = encodeURIComponent(this.state.user.name);
const email = encodeURIComponent(this.state.user.email);
const password = encodeURIComponent(this.state.user.password);
const formData = `name=${name}&email=${email}&password=${password}`;
// create an AJAX request
const xhr = new XMLHttpRequest();
xhr.open('post', '/auth/register');
xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
xhr.responseType = 'json';
xhr.addEventListener('load', () => {
if (xhr.status === 200) {
// success
// change the component-container state
this.setState({
errors: {}
});
// set a message
localStorage.setItem('successMessage', xhr.response.message);
this.setState({redirect: true});
} else {
// failure
const errors = xhr.response.errors ? xhr.response.errors : {};
errors.summary = xhr.response.message;
this.setState({
errors
});
}
});
xhr.send(formData);
}
Chrome DevTools flashes the 'xhr.send(formData)' as the first error in the stack.
This is the express handler code, but I don't think it ever makes it that far:
router.post('/register', authHelpers.loginRedirect, (req, res, next)
=> {
return authHelpers.createUser(req, res)
.then((response) => {
passport.authenticate('local', (err, user, info) => {
if (user) { handleResponse(res, 200, 'success'); }
})(req, res, next);
})
.catch((err) => { handleResponse(res, 500, 'error'); });
});
Any help would be greatly appreciated. I've spent hours trying to troubleshoot it. That's like every stackoverflow post ever.
I'm attempting to post a JSON object to my server, but when I console log req.body it shows an empty object. Here's my code:
var submitRecipe = () => {
let recipe = {alias: null, description: null, instructions: null};
recipe.alias = document.getElementById('alias').value;
recipe.description = document.getElementById('description').value;
recipe.instruction = document.getElementById('instructions').value;
postRequest("/createrecipe", recipe, (xhr) => {
console.log("Hello!" + JSON.parse(xhr.responseText));
})
};
var postRequest = (url, data, callback = undefined) => {
xhr.onreadystatechange = () => {
//Call a function when the state changes.
if(xhr.readyState == 4 && xhr.status == 200) {
console.log("testing");
return callback(200 , xhr.responseText);
}else{
return callback(400, xhr.responseText);
}
}
xhr.open('POST', url)
xhr.send(data);
}
Node
createRecipe = function(req, res){
console.log(req.body);
}
I'm using express to transfer the server information, and I am using bodyParser.json(). From there, I just call the controller with the following:
express
var express = require("express");
var bodyParser = require("body-parser");
var server = express();
var recipeController = require("./controllers/recipeController");
server.use(bodyParser.json());
server.use(bodyParser.urlencoded({ extended: true}));
server.post("/createrecipe", recipeController.createRecipe);
The createRecipe function just console logs the information, but as stated before, req.body is an empty object. All tips are appreciated.
XHR expects your data to be encoded or packed in whatever way you expect it to be send unlike other library wrappers like jQuery or Angular Ajax wrapper functions.
Alsobody-parsermiddleware was not identifying the Content-type and was not activating for the required request.
Simply JSON.stringify your json data
data = JSON.stringify(data);
and add the application/json MIME type as xhr's content-type header.
xhr.setRequestHeader("content-type", "application/json");
Also, If you want to use url-encoded do the encoding of your data before attaching and add the corresponding header content type.
My full test code (for reference purposes):
Server (testServer.js):
var express = require("express");
var bodyParser = require("body-parser");
var server = express();
server.use(bodyParser.json());
server.use(bodyParser.urlencoded({ extended: true}));
server.post("/createrecipe", function(req, res){
console.log(req.body);
var resp = {server: "hello world", dataReceived: req.body};
res.json(resp);
});
server.get("/", function(req, res){
res.sendFile(__dirname + "/testClient.html");
})
server.listen(3000, function(){
console.log("Server running");
})
Client (testClient.html):
<input type="text" id="alias" value="a">
<input type="text" id="description" value="b">
<input type="text" id="instructions" value="c">
<button onclick="submitRecipe()"> TEST</button>
<script>
var submitRecipe = () => {
let recipe = {alias: null, description: null, instructions: null};
recipe.alias = document.getElementById('alias').value;
recipe.description = document.getElementById('description').value;
recipe.instructions = document.getElementById('instructions').value;
postRequest("/createrecipe", recipe, (status, xhr) => {
var data = (JSON.parse(xhr.responseText));
console.log(data.dataReceived);
})
};
var postRequest = (url, dataObj, callback = undefined) => {
//--------------Added line--------------------
var data = JSON.stringify(dataObj);
//--------------Added line--------------------
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = () => {
//Call a function when the state changes.
if(xhr.readyState == 4 && xhr.status == 200) {
return callback(200 , xhr);
}else if(xhr.status == 400){
return callback(400, xhr);
}
}
xhr.open('POST', url)
//--------------Added line--------------------
xhr.setRequestHeader("content-type", "application/json");
//--------------Added line--------------------
xhr.send(data);
}
</script>