How to get req.params from GET route in POST route - javascript

I am struggling with one issue for really long time. I can't 'transfer' req.params.token and req.params.id to the POST route. What I mean:
router.get('/passwordReset/:token/:id', isNotLogin, (req, res) => {
console.log('token: ' + req.params.token)
console.log('id: ' + req.params.id)
Token.findOne({userId: req.params.id}, function (err, id) {
if (!id) {
res.redirect('/confirmError');
} else {
res.render('view/password/passwordReset');
};
});
});
Results from console.log are (e.g.):
token: 934f569631026e396da5b9a952bfsnx72ba1d2187ecd734b47b3aca89640faf3
id: 60e5560119fcb9627sgar810
Example link:
https://localhost:3000/passwordReset/934f569631026e396da5b9a952bfsnx72ba1d2187ecd734b47b3aca89640faf3/60e5560119fcb9627sgar810
But when it comes to the POST route:
router.post('/passwordReset/:token/:id', isNotLogin, async (req, res) => {
console.log('token: ' + req.params.token)
console.log('id: ' + req.params.id)
await resetPassword(req.params.id, req.params.token, req.body.password);
});
The results of console.log are:
token: :token
id: :id
And that's what I can't solve. I've tried many things, but I just can't come to the right solution.
I've already tried to replace router.post('/passwordReset/:token/:id' to router.post('/passwordReset' but it didn't help.
Form for the password reset:
<form action="/passwordReset/:token/:id" method="POST">
<label for="password">Password</label>
<input type="password" name="password" id="password">
<button type="submit">Zmień hasło</button>
</form>
And the resetPassword function:
async function resetPassword(userId, token, password) {
const passwordResetToken = await Token.findOne({userId: userId});
if (!passwordResetToken) {throw new Error("Invalid password reset token");}
const isValid = await bcrypt.compare(token, passwordResetToken.token);
if (!isValid) throw new Error("Invalid password reset token");
const salt = crypto.randomBytes(32).toString('hex');
const hash = crypto.pbkdf2Sync(password, salt, 100000, 64, 'sha512').toString('hex');
await User.updateOne(
{_id: userId},
{$set: {hash: hash}},
{$set: {salt: salt}},
{new: true}
);
const user = await User.findById({_id: userId});
sendEmail(user.username, "Password Reset Successfully", {email}, "./email/template/resetPassword.handlebars");
await passwordResetToken.deleteOne();
return true;
};
The error is:
(node:1811) UnhandledPromiseRejectionWarning: CastError: Cast to ObjectId failed for value ":id" (type string) at path "userId" for model "Token"
Whole code: https://github.com/kbilak/ToDoApp

Send the actual value for token and id to the html page. So your form action should be something like this when you inspect the code on your browser
/passwordReset/934f569631026e396da5b9a952bfsnx72ba1d2187ecd734b47b3aca89640faf3/60e5560119fcb9627sgar810
Using the ejs template format
<form action="/passwordReset/<%= token %>/<%= id %>" method="POST">
<label for="password">Password</label>
<input type="password" name="password" id="password">
<button type="submit">Zmień hasło</button>
</form>
In order to send the value to your html page, you need to add those value when you are rendering the page. Just like you have it here
router.get('/passwordReset/:token/:id', isNotLogin, (req, res) => {
token = req.params.token
id =req.params.id
Token.findOne({userId: req.params.id}, function (err, id) {
if (!id) {
res.redirect('/confirmError');
} else {
res.render('view/password/passwordReset', {
token: token
id: id
} );
};
});
});

Related

Axios request works only on second request

I am building a user login system and currently the way I have it set up, the user types in their email and password and then that information is sent to my server to be checked if accurate. If it is, I create a JWT token and send it back along with the mongodb _id of the user, I then in another request use the mongodb _id of the user from the first request to retrieve all the information (blogs in this case) associated with their mognodb _id. All of this works except for the issue I am running into is the axios request does not work the first time. So I have to hit submit to log in for an error to occur TypeError: Cannot read properties of undefined (reading 'data') and then when I hit submit again it works. I believe the problem is getBasic not getting set before the rest of the code runs on the first attempt, and on the second attempt it is already defined. I am not sure how I can fix this without maybe a manual timer before I do the getBasic.data.map, but I know there must be a better way to fix this.
And just to add this is just me trying to test this concept so I understand later things like bcrypt will need to be added to the password instead of plaintext in the database, etc.
Frontend:
function Login() {
const [user, setUser] = useState()
const [getBasic, setGetBasic] = useState()
const [Item, setItem] = useState({
email: '',
password: ''
});
async function fetchData() {
const sentItem = {
user: user.data.user._id,
}
await axios.post('http://localhost:3001/api', sentItem)
.then(result => setGetBasic(result))
}
const loginFunc = async () => {
const checkUser = {
email: Item.email,
password: Item.password
}
await axios.post('http://localhost:3001/api/login', checkUser)
.then(result => setUser(result))
.then(fetchData())
}
if (user && getBasic) {
localStorage.setItem('key', JSON.stringify(user.data.token))
console.log(localStorage.getItem('key'))
return (
<div>
<div>
<input onChange={handleChange} name="email" value={Item.email} placeholder="email" />
<input onChange={handleChange} name="password" value={Item.password} placeholder="password" />
<button onClick={loginFunc}>Submit</button>
</div>
{
getBasic.data.map(({ text, _id, }, i) => {
return (
<div>
<p>{_id}</p>
<p>{text}</p>
</div>
);
})
}
)
}
return (
<div>
<input onChange={handleChange} name="email" value={Item.email} placeholder="email" />
<input onChange={handleChange} name="password" value={Item.password} placeholder="password" />
<button onClick={loginFunc}>Submit</button>
</div>
)
}
export default Login
Server:
app.post('/api/login', async (req, res) => {
const user1 = await User.findOne({ email: req.body.email, password: req.body.password })
if (user1) {
const payload = { name: req.body.name, email: req.body.email };
const token = jwt.sign(payload, private_key, { expiresIn: '200s' });
console.log('correct login')
res.status(200).send({ auth: true, token: true, user: user1 })
} else {
console.log('incorrect login')
}
})
app.post('/api', async (req, res) => {
Blog.find({ user: req.body.user }, function (err, result) {
if (err) {
console.log(err)
} else {
res.send(result)
}
})
})
While nature of React.useState is asynchronous, and changes will not be reflected immediately, closures take their role in situations like this, as pointed out in link in below.
More details provided here - The useState set method is not reflecting a change immediately
As stated in error from your example, cannot read data of undefined - meaning user is undefined, and you try to access to user.data which leads to error.
const sentItem = {
user: user.data.user._id,
}
Instead of
await axios.post('http://localhost:3001/api/login', checkUser)
.then(result => setUser(result))
.then(fetchData())
why don't you try this (remove fetchData function if not reused on other places...):
const result = await axios.post('http://localhost:3001/api/login', checkUser);
const sentItem = {
user: result.data.user._id,
}
axios.post('http://localhost:3001/api', sentItem)
.then(result => setGetBasic(result));
setUser(result);
I think you should create user state like this
const [user, setUser] = useState({})
.then(fetchData())
The then method accepts a function which runs when the other stuff is done. However, you are not passing in a function here, but the return result from calling fetchData.
Try
.then(fetchData)
or
.then(() => fetchData())

Express HTML patch form req.body is undefined

I'm currently making a to-do list using Express and Node.js with MongoDB and Mongoose. I'm trying to update the to-do list so I've tried to use patch however this just changes the data to null. (No error messages shown but the HTTP status code is 400)
router.patch('/:id', ensureAuth, async(req, res) => {
try {
const id = req.params.id;
const updatedTask = await Task.findByIdAndUpdate(id, {
task: req.body.taskbox
})
res.send(result);
} catch (err) {
res.status(400).json({
message: err.message
})
}
})
I'm thinking the error might be to do with the "req.body.taskbox" since console.log(req.body.taskbox) shows "undefined" and console.log(req.body) prints {}. Furthermore, when I hardcode something there, the patch works. (e.g. task: "Clean window" - this would update the task successfully to say "Clean window").
The code below shows where the HTML form is generated:
static addTaskToTable(currentTask) {
const list = document.querySelector('#task-list');
const row = document.createElement('div');
row.innerHTML = `
<div class="row">
<div class="col-md-8">
<form id="task-form-for-${currentTask.taskId}">
<input type="text" name="taskbox" value="${currentTask.task}" />
</form>
</div>
<div class="col">
<form id="edit-for-${currentTask.taskId}">
<p><input type="submit" class="btn btn-sm btn-primary" value="edit" onclick="Store.editTask('${currentTask.taskId}')" /></p>
</form>
</div>
<div class="col">
<form id="delete-form-for-${currentTask.taskId}">
<p><input type="submit" class="btn btn-danger btn-sm delete" onclick="Store.deleteTask('${currentTask.taskId}')" value=" x " /></p>
</form>
</div>
</div>
`;
list.appendChild(row);
}
I loop through the tasks in the database to display them using this function: (This could be another place that could've caused this error maybe?)
static displayTasks(){
Store.getTasksArr().then(taskData => {
let tasks = [];
let i;
taskData.map((currentTasks) => {
tasks.push(currentTasks)
})
tasks.forEach((task) => UI.addTaskToTable(task));
})
}
The fetch request:
static editTask = async(id) => {
const res = await fetch('http://localhost:5500/tasks/' + id, {
method: "PATCH",
});
const json = await res.json();
console.log(json);
}
My get, post, and delete all work fine and for the post, I wrote something very similar so I'm also confused as to why the patch doesn't retrieve the data from the textbox but in the post request it does:
router.post('/', ensureAuth, async(req, res) => {
try {
const task = new Task({
task: req.body.newtask,
user: req.user.id
})
const newTask = await task.save()
res.status(201)
res.redirect('/home')
} catch (err) {
res.status(400).json({
message: err.message
})
}
})
I also have a body-parser middleware already defined:
app.use(express.urlencoded({extended: false}));
app.use(express.json());

How to show username using HTML and nodejs?

So, I am building a simple login page and I wanna show the username after you succesfully login to the page. But I am kind of confused how to do that in HTML. I saw lots of materials on ejs and angular and react and passport. But I am not using that. I am just using simple HTML,CSS and NodeJS to build this.
So, if someone can help me regarding this, I will be much obliged.
So my HTML page after I login:
<!DOCTYPE html>
<head>
</head>
<body>
Hi user. //want to show the username here
<form name="logout" method="post" action='/logout'>
<label class="logoutLblPos">
<input name="submit2" type="submit" id="submit2" value="log out">
</label>
</form>
</body>
and my code for successful login in server.js file:
app.post("/login", urlencodedParser, async (req, res) => {
const { email, password } = req.body;
db.query(
"SELECT * FROM users WHERE email = ?",
[email],
async (error, result) => {
if (error) {
console.log(error);
} else {
try {
if (result.length === 0 || !result) {
res.json({ message: "Email has not been registered" });
} else {
bcrypt.compare(password, result[0].PASSWORD, (err, results) => {
if (err) {
console.log(err);
return res.json({
message: "there has been some problem matching the password",
});
} else {
if (results) {
//token creation
const id = result[0].ID;
console.log(id)
const token = jwt.sign({ id }, process.env.JWT_SECRET, {
expiresIn: process.env.JWT_EXPIRES_IN,
});
console.log("the token is " + token);
const cookieOptions = {
expires: new Date(
Date.now() +
process.env.JWT_COOKIE_EXPIRES * 24 * 60 * 60 * 1000
),
httpOnly: true,
};
res.cookie("myCookieJwt", token, cookieOptions)
res.status(200).redirect("/user");
} else {
res.json({ message: "Please enter correct password" });
}
}
});
}
} catch (error) {
console.log(error);
return;
}
}
}
);
});
You need to use some kind of library that make POST request to the endpoint. JQuery Ajax to rescue, you can use other libraries like Axios as well
$.ajax({
type: 'POST',
url: "/login",
data: "FORM DATA",//<---- Form Data (username,password)
dataType: "text or JSON", //<-- Whichever you are using
success: function(resultData) { $('#username').text(resultData.username); } //<-- I'm assuming username is coming in
});
you need to return username from node.js endpoint then You can set the text of div or any other HTML tag dynamically.
This example might need a tweaking as it might not work out of the box, but it will set you on correct path.

ReferenceError: email is not defined

Hi Please help a struggling dev.
I have been trying all day to get this fixed to no avail. Essentially all I want to to do is post from my AddUsers class and for this to be stored through to my sql database. It is a very simple query but has gotten the better off me!The state is updated on change but seems to be an issue with the server.js (error included at bottom of post)
Server.js
app.post("/admin-Add-Users", function(req, res) {
var request = new sql.Request();
// query to the database and get the records
request.query(
"insert into Login (email, password) values ('" +
req.body.email +
"','" +
req.body.password +
"')",
function(err, recordset) {
if (err) console.log(err);
}
);
res.send({ message: "Success" });
});
AddUsers class
class AddUsers extends React.Component {
constructor() {
super();
this.state = { users: [], email: "", password: "" };
this.onSubmit = this.handleSubmit.bind(this);
}
handleSubmit(e) {
e.preventDefault();
const data = { email: this.state.email, password: this.state.password };
fetch("/admin-Add-Users", {
method: "POST", // or 'PUT'
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify(data)
})
.then(response => response.json())
.then(data => {
console.log("Success:", data);
})
.catch(error => {
console.error("Error:", error);
});
}
render() {
console.log(this.state.users);
return (
<div>
<LoginForm></LoginForm>
<form>
<input
type="text"
placeholder="email"
value={this.state.email}
onChange={e => this.setState({ email: e.target.value })}
/>
<input
type="text"
placeholder="password"
value={this.state.password}
onChange={e => this.setState({ password: e.target.value })}
/>
<input type="submit" onClick={this.onSubmit} />
</form>
</div>
);
}
}
ReferenceError: email is not defined
UPDATE: After trying recommendations I have been given I now revive a new error.
Error: SyntaxError: Unexpected token < in JSON at position 0
It seems like there is nothing wrong in your React app.
The problem is at your API end where you're formulating an insert query without actually reading the request json content (email & password) fields.
You could add following lines before the query is being generated.
// create sql obj
...
var email = req.body.email;
var password = req.body.password;
...
// your query
You need to add middleware to your express app to be able to parse request body's.
Try adding this to the file where you configure express:
app.use(express.json());
email and password fields must be retrieved from req
This is not a complete answer but it turns out the issue is related to CORS. I am not sure of the solution at this point ,but I am fairly sure this is the cause.
Thanks for all your help :)
Hi Everyone thanks for all your help. I fixed this issue by using the following code within my server.js
app.post("/admin-Add-Users", async (req, response) => {
sql.connect(config, function(err) {
if (err) {
console.log(err);
response.status(400);
response.send(err);
} else {
try {
// create Request object
var request = new sql.Request();
var body = req.body;
console.log(body);
if (body) {
var email = body.email;
var password = body.password;
var queryString = `insert into Login (email,password) values ('${email}', '${password}')`;
console.log(queryString);
request.query(queryString, function(err, recordset) {
console.log(err);
response.status(400);
// response.send(err);
});
response.status(201);
response.send("User added ");
} else {
response.status(400);
response.send("no content was provided");
}
} catch (e) {
console.log(e);
response.status(400);
response.send(e);
}
}
});
});

ExpressJS variable undefined

I have an ExpressJS app that when a user makes a POST request to a route, it should lookup the ID in the MongoDB using req.params.formId
I have some console.log statements tfor debugging and so I can see what info is being returned.
The route should lookup the ID passed and when it finds it, use the req.body data and also a field from the MongoDB document but this just seems to return as undefined
Here is the code for the route:
app.post("/api/v1/forms/:formId", (req, res) => {
const { name, email, message } = req.body;
console.log(req.body);
Form.findById(req.params.formId, Form.recipient, err => {
if (err) {
res.send(err);
} else {
const formRecipient = Form.recipient;
const newForm = {
name,
email,
message,
recipient: formRecipient
};
console.log(newForm);
const mailer = new Mailer(newForm, contactFormTemplate(newForm));
try {
mailer.send();
res.send(req.body);
} catch (err) {
res.send(err);
}
}
});
});
So an example, if I make a POST request to localhost:5000/api/v1/forms/5ad90544883a6e34ec738c19 the console.log of newForm shows { name: ' Mr Tester',
email: 'person#example.com',
message: 'Hi there',
recipient: undefined }
The forms Mongoose schema has a field named recipient
the correct way is to provide the fields you want to get as the second argument:
Form.findById(req.params.formId, 'recipient', (err, form) => {
if (err) {
// error handling code
} else {
const formRecipient = form.recipient;
}
...
});
here's the Docs

Categories