Evening All,
I'm trying to build a simple register page with mongodb, node, express, mongoose and angularJS.
I'm struggling to understand the relationship between all of the components and how to wire them all together.
This is my form...
<div class="main">
<p class="sign" align="center">Register</p>
<form class="form1">
<input class="un " type="email" align="center" placeholder="Email" name="username" required>
<input class="pass" type="password" align="center" placeholder="Password" name="password" required>
<button type="submit" class="btn btn-danger" align="center" ng-click="submit()">Register</button>
</div>
My controller for that view...
angular.module('registerCtrl', [])
.controller('registerController', function ($scope, $http, dataService) {
$scope.submit= function(){
console.log('clicked submit');
$http.post('/RegisterUser').then(function(response){
console.log(response.data);
console.log(response.status)
})
}
});
server.js for server and db logic...
const express = require('express');
const bodyParser = require('body-parser');
const mongoose = require('mongoose');
const app = express();
const router = express.Router();
mongoose.connect("mongodb://localhost:27017/userDB", { useNewUrlParser: true });
const userSchema = new mongoose.Schema({
email: String,
password: String
});
const User = new mongoose.model("User", userSchema);
app.use(bodyParser.urlencoded({
extended: true
}))
app.use(express.static('node_modules'));
app.use(express.static('public'));
const port = 3000;
app.listen(port);
console.log('Server is running on port 3000');
//POSTS
app.post("/RegisterUser", function (req, res) {
const newUser = new User({
email: req.body.username,
password: req.body.password
});
newUser.save();
})
view routes...
angular.module('app.routes', [])
.config(function ($routeProvider) {
$routeProvider
.when("/", {
templateUrl: "views/Login.html"
})
.when("/Register", {
templateUrl: "views/Register.html",
controller: "registerController"
})
.when("/Home", {
templateUrl: "views/Home.html",
controller: "homeController"
})
.when("/CocktailDetails", {
templateUrl: "views/CocktailDetails.html",
controller: "cocktailDetailsController"
})
.when("/Favourites", {
templateUrl: "views/Favourites.html",
controller: "favouritesController"
})
.otherwise({
redirectTo: "/"
})
})
Essentially what I want to achieve is simply posting the inputted email and password to the database and then, if the POST was successful divert the view route to my home view.
Should I be passing in the data from the form into the post request in my controller as opposed to the server side? As currently the post data is being passed as null.
Is someone able to explain the best way to achieve this and describe the relationship between the database, server and client side framework in this scenario.
Thanks
Client
It looks like your form is missing ng-models and you're also not posting their data. Add ng-model to the inputs:
<input type="email" ng-model="form.username">
<input type="password" ng-model="form.password">
Also create their bindings in the controller. Then pass the form data along to the server when you post:
.controller('registerController', function ($scope, $http, $location, dataService) {
// injecting $location now ^
$scope.form = { // <-- An object whose properties are bound to the form inputs
username: '',
password: ''
}
$scope.submit = function(){
console.log('clicked submit');
$http.post('/RegisterUser', $scope.form) // <-- posting `$scope.form` data now
.then(function(response){
console.log(response.data);
console.log(response.status);
$location.path('/Home'); // <-- redirecting to Home route
})
}
});
Server
Since there was no data being posted before, req.body was empty on the server. The server is also not sending back a response. If you wanted to send back the user object, you could add this to the server route after newUser.save():
return res.status(200).send(newUser);
send is what sends a response, return just exits the function.
Related
I have a simple html form:
<div class="signup-form">
<h2>Signup for an account</h2>
<form method="POST" action="/submit-signup">
<input type="text" title="username" placeholder="username" />
<input type="password" title="username" placeholder="password" />
<button type="submit" class="btn">Submit</button>
</form>
</div>
and an index.js with routes:
var bodyParser = require('body-parser')
const express = require('express')
const app = express()
const port = 3000
app.use(bodyParser.urlencoded({ extended: false }))
app.use(bodyParser.json())
const Pool = require('pg').Pool
const pool = new Pool({
user: 'user',
host: 'localhost',
database: 'app',
password: 'password',
port: 5432,
})
app.get('/', function(req, res) {
res.sendFile(__dirname +'/index.html');
});
app.get('/login', function(req, res) {
res.sendFile(__dirname +'/login.html');
});
app.get('/signup', function(req, res) {
res.sendFile(__dirname +'/signup.html');
});
app.post('/submit-signup',(req, res) => {
console.log(req.body)
const username = req.body.username
const password = req.body.password
pool.query('INSERT INTO users (username, password) VALUES ($1, $2)', [username, password], (error, results) => {
if (error) {
throw error
}
response.status(201).send(`User added with ID: ${result.insertId}`)
})
})
app.listen(port, () => {
console.log(`App running on port ${port}.`)
})
Every time the form is submitted with answers the request body logs as {} and the error is thrown from the query because of the null value (the column in my database is set to not-null). I've tried most fixes from old stack overflow posts, so any help is appreciated
You are using the body-parser middleware. This middleware by default will try to parse every http body into a js-object based on a given json value.
As you are sending your data over an HTML form this data is not in JSON representation, but in the data generated by the HTML form.Mozilla Specs
To make this work you either need to iplement a way to read the body sent by the HTML form or use a more modern approach by sending a (REST) JSON request to your express server.
To make your code work use body-parser.urlencoded() as HTML form data is an url encoded text Ref to existing Stackoverflow answer
To make your form a bit more modern, use a JavaScript framework and follow a client-server pattern. Have a look at this tutorial REST tutorial with express
Your problem cuz you don't use a name tag in HTML form.
<div class="signup-form">
<h2>Signup for an account</h2>
<form method="POST" action="/submit-signup">
<input type="text" name="username" title="username" placeholder="username" />
<input type="password" name="password" title="password" placeholder="password" />
<button type="submit" class="btn">Submit</button>
</form>
</div>
Add name attribute in your html input field
I put a contact form on my personal portfolio website, and every couple of weeks, the email address associated with the contact form (which is used nowhere else) gets compromised. Through some means that I'm not aware of, someone is able to gain access to the password (which is used nowhere else) and attempts to log in to my account with it.
When the account is compromised, I find I have three emails from seemingly random addresses with seemingly random, single-word subjects and content.
All passwords are over 10 characters long, using capitals, lowercase, numbers, and symbols.
The website is static, hosted on Netlify. The email is handled by a Node.js microservice hosted on Heroku. Here's my contact form.
<form id="contact-form" autocomplete="off">
<label for="name">Name</label>
<input type="text" id="name" name="name" value="" required />
<label for="email">Email</label>
<input type="email" id="email" name="email" value="" required />
<label for="subject">Subject</label>
<input type="text" id="subject" name="subject" value="" required />
<label for="text">Message</label>
<textarea id="text" name="text" value="" required></textarea>
<button type="submit">Submit</button>
</form>
Here's the code that handles the contact form:
$("#contact-form").submit((e) => {
e.preventDefault();
$.ajax({
url: "https://my-microservice.herokuapp.com/email",
type: "POST",
dataType: "json",
data: $("#contact-form").serialize(),
success: function (response) {
$("#contact-form").html(
"<div class='form-success'><h2>Your message has been sent! Thanks for getting in touch.</h2></div>"
);
},
error: function (xhr, status, error) {
$("#contact-form").html(
"<div class='form-failure'><h2>Uh-oh! Something went horribly wrong. You can email me directly at <a href='mailto: anotheremailaddress#gmail.com'>anotheremailaddress#gmail.com</a></h2></div>"
);
},
});
});
Here's the microservice that handles the email:
require("dotenv").config();
const express = require("express");
const nodemailer = require("nodemailer");
const app = express();
const bodyParser = require("body-parser");
const cors = require("cors");
var corsOptions = {
origin: "https://www.my-portfolio-site.com",
optionsSuccessStatus: 200
};
app.use(
bodyParser.urlencoded({
extended: true
})
);
var transporter = nodemailer.createTransport({
service: "gmail",
auth: {
user: process.env.EMAIL,
pass: process.env.PASSWORD
}
});
app.post("/email", cors(corsOptions), (req, res) => {
var mailOptions = {
from: "my-portfolio-site.com",
to: process.env.EMAIL,
subject: "PORTFOLIO MESSAGE",
text: `from: ${req.body.email}\nsubject: ${req.body.subject}\nmessage:\n\n${req.body.text}`
};
transporter.sendMail(mailOptions, function(error, info) {
if (error) {
console.log(error);
res.send(error);
} else {
console.log("Email sent: " + info.response);
res.send(info);
}
});
});
let port = process.env.PORT;
if (port == null || port == "") {
port = 8000;
}
const listener = app.listen(port, function() {
console.log("Your app is listening on port " + listener.address().port);
});
Any insight into how my password might be being compromised and how I might stop it would be very much appreciated. Thanks for your time.
on the microservice there is a piece of code that allows CSRF attack thi is the part:
app.use(
bodyParser.urlencoded({
extended: true
})
);
for more details you can take a look here or search on google what that extended : true means.
I'm making a very simple authenticated website for learning giving users the ability to create an account log in and view content.
The issue is not seeing the registered users on the home page (as a test) after a successful post registration. After the post, I am redirecting the user back to the home page and the get route runs a postgres function using node-postgres module to retrieve all of the users in the database and returns them. That doesn't seem to be firing. Yet, when I run the same function in my database, I do see the new user.
What's weird is that when I hit enter on the home route in the browser afterwards, the new user does pop up. So I'm really not sure if this is a caching thing or not understanding promises correctly (I've just started getting into this) or something else.
I have tried using callbacks instead of promises as shown on here: https://node-postgres.com/features/queries to see if it makes a difference. Other than that, I've added log statements to see if the promise actually resolves or rejects. But it seems to always resolve so I'm really unsure what's going on here.
<!-- register.ejs -->
<%- include('partials/header') %>
<div class="container mt-5">
<h1>Register</h1>
<div class="row">
<div class="col-sm-8">
<div class="card">
<div class="card-body">
<!-- Makes POST request to /register route -->
<form action="/register" method="POST">
<div class="form-group">
<label for="email">Email</label>
<input type="email" class="form-control" name="username">
</div>
<div class="form-group">
<label for="password">Password</label>
<input type="password" class="form-control" name="password">
</div>
<button type="submit" class="btn btn-dark">Register</button>
</form>
</div>
</div>
</div>
</div>
</div>
<%- include('partials/header') %>
\\index.js (in ./db)
const {
Pool
} = require('pg');
const pool = new Pool({
database: 'secrets'
});
module.exports = {
query: (text, params, callback) => {
return pool.query(text, params, callback)
}
};
//jshint esversion:6
/* Imports
* ================================================================================ */
const express = require('express');
const bodyParser = require('body-parser');
const db = require('./db');
const util = require('util');
/* App setup
* ================================================================================ */
const app = express();
app.disable('etag');
const port = 3000;
app.use(express.static(util.format("%s/%s", __dirname, 'public')));
app.set('view engine', 'ejs');
app.use(bodyParser.urlencoded({
extended: true
}));
/* Routes
* ================================================================================ */
// Get request
app.get("/", function (req, res, next) {
db.query('SELECT * FROM dbo.usp_SelectUsers()')
.then(function(dbResult) {
console.log(dbResult.rows);
res.send(dbResult.rows);
}, (reason) => {
console.log("Not fulfilled :(");
}).catch(_err => next(_err));
});
// Post request for registration
app.post("/register", function (req, res, next) {
const queryText = 'SELECT dbo.usp_CreateUser($1, $2)';
const queryValues = [req.body.username, req.body.password];
db.query(queryText, queryValues)
.then(res.redirect("/"))
.catch(err => next(err))
});
CREATE OR REPLACE FUNCTION dbo.usp_SelectUsers()
RETURNS TABLE (User_ID INTEGER, User_Name VARCHAR(100)) AS
$func$
BEGIN
RETURN QUERY
SELECT u.User_ID
,u.User_Name
FROM dbo.User u
;
END
$func$ LANGUAGE plpgsql;
Expected result is to see new users in database and displayed on home page ("/") after successful post without having to refresh the page again.
I have updated my registration code to use async / await as follows and this fixed everything for me. I realized that the issue I was having previously is that the post did not fully complete before the database call to retrieve the the list of users were made (ie: database did not yet have the new user when calling dbo.usp_SelectUsers).
app.post("/register", async (req, res, next) => {
try {
const queryText = 'SELECT dbo.usp_CreateUser($1, $2)';
const queryValues = [req.body.username, req.body.password];
const results = await db.query(queryText, queryValues);
res.redirect("/login");
} catch (err) {
throw err;
}
});
As a side note, I have read that async / await is also much better to use nowadays and this fix added onto the pluses. Please see the following article for reference:
https://hackernoon.com/6-reasons-why-javascripts-async-await-blows-promises-away-tutorial-c7ec10518dd9
I am trying to create a book app i have react on the front and node js on the backend. When i tried to create in backend its say Cannot POST /create.What do i have to do ,the folder is divided into front end and backend. i am using axios.I am new to react js please help.How can i pass data from a form in react to express to save.
this is the react component create
import React, {Component} from 'react';
class Create extends Component{
render(){
return(
<div>
<br/>
<div class="container">
<form action="http://127.0.0.1:3000/create" method="post">
<div style={{width: '30%'}} class="form-group">
<input type="text" class="form-control" name="BookID" placeholder="Book ID"/>
</div>
<br/>
<div style={{width: '30%'}} class="form-group">
<input type="text" class="form-control" name="Title" placeholder="Book Title"/>
</div>
<br/>
<div style={{width: '30%'}} class="form-group">
<input type="text" class="form-control" name="Author" placeholder="Book Author"/>
</div>
<br/>
<div style={{width: '30%'}}>
<button class="btn btn-success" type="submit">Create</button>
</div>
</form>
</div>
</div>
)
}
}
export default Create;
this index.js in the backend
var express = require('express');
var app = express();
var bodyParser = require('body-parser');
var session = require('express-session');
var cookieParser = require('cookie-parser');
var cors = require('cors');
app.set('view engine', 'ejs');
//use cors to allow cross origin resource sharing
app.use(cors({
origin: 'http://localhost:3000',
credentials: true
}));
var books = [{
"BookID": "1",
"Title": "Book 1",
"Author": "Author 1"
},
{
"BookID": "2",
"Title": "Book 2",
"Author": "Author 2"
},
{
"BookID": "3",
"Title": "Book 3",
"Author": "Author 3"
}
]
app.get('/home', function (req, res) {
console.log("Inside Home Login");
res.writeHead(200, {
'Content-Type': 'application/json'
});
console.log("Books : ", JSON.stringify(books));
res.end(JSON.stringify(books));
})
app.post('/create', function (req, res) {
var newBook = {
"BookID": req.body.BookID,
"Title": req.body.Title,
"Author": req.body.Author
}
books.push(newBook)
console.log(books);
})
//start your server on port 3001
app.listen(3001);
console.log("Server Listening on port 3001");
There were a few errors. Here is some updated code and a description of what was going on:
React App.js:
import React, { Component } from 'react';
import axios from 'axios';
class Create extends Component {
constructor(props) {
super(props);
this.state = {
bookID: '',
bookTitle: '',
bookAuthor: '',
};
}
handleInputChange = e => {
this.setState({
[e.target.name]: e.target.value,
});
};
handleSubmit = e => {
e.preventDefault();
const { bookID, bookTitle, bookAuthor } = this.state;
const book = {
bookID,
bookTitle,
bookAuthor,
};
axios
.post('http://localhost:3001/create', book)
.then(() => console.log('Book Created'))
.catch(err => {
console.error(err);
});
};
render() {
return (
<div>
<br />
<div className="container">
<form onSubmit={this.handleSubmit}>
<div style={{ width: '30%' }} className="form-group">
<input
type="text"
className="form-control"
name="bookID"
placeholder="Book ID"
onChange={this.handleInputChange}
/>
</div>
<br />
<div style={{ width: '30%' }} className="form-group">
<input
type="text"
className="form-control"
name="bookTitle"
placeholder="Book Title"
onChange={this.handleInputChange}
/>
</div>
<br />
<div style={{ width: '30%' }} className="form-group">
<input
type="text"
className="form-control"
name="bookAuthor"
placeholder="Book Author"
onChange={this.handleInputChange}
/>
</div>
<br />
<div style={{ width: '30%' }}>
<button className="btn btn-success" type="submit">
Create
</button>
</div>
</form>
</div>
</div>
);
}
}
export default Create;
You were getting errors for using class and not className. class is a reserved word in react.js and should not be used.
You were using the default post method which I would not recommend. I split the post out into it's own action and used the common library axios to make the CORS post call. I also created a function to handle the input changing on every key press with react.js.
I added state to your component. This is common when there are form inputs to store them in state. I also changed the name of your variables to be title case which is the common way to write code variables.
Node.js index.js:
const express = require('express');
const logger = require('morgan');
const cors = require('cors');
const app = express();
//use cors to allow cross origin resource sharing
app.use(
cors({
origin: 'http://localhost:3000',
credentials: true,
})
);
app.use(logger('dev'));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
let books = [];
app.get('/home', function(req, res) {
console.log('Inside Home Login');
res.writeHead(200, {
'Content-Type': 'application/json',
});
console.log('Books : ', JSON.stringify(books));
res.end(JSON.stringify(books));
});
app.post('/create', function(req, res) {
const newBook = {
BookID: req.body.bookID,
Title: req.body.bookTitle,
Author: req.body.bookAuthor,
};
books.push(newBook);
console.log(books);
});
//start your server on port 3001
app.listen(3001, () => {
console.log('Server Listening on port 3001');
});
You weren't parsing the body of the req, so it was coming back as
undefined. I added app.use(express.json()); and
app.use(express.urlencoded({ extended: false })); which should
take care of most of the issues.
I updated the req.body variables to match those coming over from
React.
I added the module morgen which you see here app.use(logger('dev')); this is helpful by showing all your requests and statuses for dev purposes. In this case, it was showing that you were getting a 500 (internal server error) because express couldn't read bookID of undefined (because the body wasn't being parsed).
This should be working now, let me know if you have any problems.
I don't use express some so the details may not apply.
In essence you will have to send a network request to your server.
How you do this is up to you, The most common ways are with axios(a library) or with vanilla js with the fetch api.
I would just use the fetch api.
it takes two parameters an url and the options.
so it should be called like this fetch(url,options)
so in your case it would be fetch('localhost:3001/create, options)
What should be in the options.
I just suggest you look at the fecth MDN docs here https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API
but your case you will need to pass an object with the method property set to post and the new book you want to data property set to a JSON serialized object of the book you want to create.
for example
let book ={
BookId:1,
Title: "coolBook",
Author: "Me"
}
fetch("localhost:3001/create",
{
method: "post",
data: JSON.stringify(book)
}
When passing the books a string instead of an object you will likely have to take that string and parse it as an object on the server so that you express /create handler looks more like:
app.post('/create', function (req, res) {
var newBook = JSON.parse(req.body.data)
books.push(newBook)
console.log(books);
})
On the react side you need to create an event handler that calls the above fetch function. I recommend you watch a react/express tutorial though as I can;t really cover all the thing required here in a stack overflow question such as: using and validating forms in react, error handling, async/await and so on.
All the Best! hope that was slightly helpful
You should have some kind of response on your /create endpoint
For example:
app.post('/create', function (req, res) {
var newBook = {
"BookID": req.body.BookID,
"Title": req.body.Title,
"Author": req.body.Author
}
books.push(newBook)
console.log(books);
res.status(201).json({"some":"response"})
})
I tried looking for different answers but they didn't work. I don't receive the email in my gmail account when I am trying to send it, I am just getting it in my Ethereal account.
This is my server code:
const express = require('express');
const path = require('path');
const cons = require('consolidate');
const bodyParser = require('body-parser');
const nodemailer = require("nodemailer");
const app = express();
app.engine('html', cons.swig)
app.set('../public', path.join(__dirname, 'public'));
app.use('../src/css', express.static(path.join(__dirname, 'src/css')));
app.use('../src/js', express.static(path.join(__dirname, 'src/js')));
app.use(bodyParser.urlencoded({ extended: false}));
app.use(bodyParser.json());
app.post('/send',(req,res) => {
const output = `
<p>name of client: ${req.body.name}</p>
`;
let transporter = nodemailer.createTransport({
host: "smtp.ethereal.email",
port: 587,
secure: false,
auth: {
user: "*****#ethereal.email",
pass: "********"
},
tls: {
rejectUnauthorized: false
}
});
let mailOptions = {
from: `${req.body.email}`,
to: "******#gmail.com",
subject: "Node contact request",
text: "Hello world?",
html: output
};
transporter.sendMail(mailOptions, (error, info) => {
if(error) {
return console.log(error);
}
console.log("Message sent: %s", info.messageId);
console.log("Preview URL: %s", nodemailer.getTestMessageUrl(info));
res.render('index')
});
});
app.listen(5000);
I am using react app for my front-end(e-commerce), and I want that after sending the email the client will see a message saying "The email has been sent" (the location doesn't really matter for now). How can I do it after submitting the form? Right now the client is directing to localhost:5000/send instead of staying at the same page.
This is my react code:
import React from 'react';
const contact = () => {
return (
<form className="contact" action="http://localhost:5000/send" method="post">
<div className="contact-topFields">
<div className="contact-topFields-email">
<input
className="contact-topFields-email-input"
type="email"
name="email"
required/>
<ion-icon id="email-icon" name="mail"></ion-icon>
<p className="contact-topFields-email-text">Email</p>
</div>
<div className="contact-topFields-name">
<input
className="contact-topFields-name-input"
type="text"
name="name"
required/>
<ion-icon id="name-icon" name="person"></ion-icon>
<p className="contact-topFields-name-text">Name</p>
</div>
</div>
<div className="contact-bottomFields">
<div className="contact-bottomFields-phone">
<input
className="contact-bottomFields-phone-input"
type="text"
name="phonenumber"
required/>
<ion-icon id="phone-icon" name="call"></ion-icon>
<p className="contact-topFields-phone-text">phone</p>
</div>
</div>
<div className="contact-text">
<textarea className="contact-text-textarea" name="message" required></textarea>
</div>
<button className="contact-submitButton" type="submit">send </button>
</form>
)
}
export default contact;
To prevent the page being directed to localhost:5000/send:
In order to trigger sending the email while keep the user's view unchanged, you may want to override the form's submit function.
For example, you may want to do this in your JSX code:
<form className="contact" onSubmit={{this.handleSubmit}}>
<- whatever inside form ->
</form>
Then you may want to define a function handleSubmit:
function handleSubmit (evt) {
evt.preventDefault() // this is used to prevent ui from being directed
// Do http request here, use a http agent such as fetch(), axios() etc etc.
}