I have a form on client-side which is sending data to server through AJAX in my Express app. I want to show some responses to the user when there are errors or the message sent successfully.
This code represents an error message when the message cannot be sent. The specific div in my handlebars template looks like this:
<div class="form-validation-error" style="{{formValidationError}}">ERROR: Message cannot be sent!</div>
Which is turned off by CSS default:
.form-validation-error {
display: none;
}
In my routes/contact.js I have a router.post block which is handling the message sending:
router.post('/', (req, res, next) => {
if(req.body.firstname.length === 0 || !req.body.firstname.match(/\D+/igm)) {
var validateFirstname = false;
} else {
var validateFirstname = true;
};
if(req.body.captcha.length === 0 || !req.body.captcha.match(/^kettő|ketto|two$/igm)) {
var validateCaptcha = false;
} else {
var validateCaptcha = true;
};
if(validateFirstname === true && validateCaptcha === true) {
console.log('SUCCESS: Form validated! The Nodemailer script will be here!');
} else {
console.log('ERROR: Form not validated!');
const formValidationErrorTrue = 'display: block;'; // -> How can I achieve this!??
res.send({formValidationError: 'display: block;'}); // -> Or this!??
};
});
After this block, I have a router.get template rendering part:
router.get('/', (req, res, next) => {
fsAsync((err, data) => {
if(err) {
res.render('404', {
title: 'Error 404'
});
}
const contact = data[2].contact;
res.render('contact', {
title: 'Contact',
data: contact,
formValidationError: formValidationErrorTrue // -> How can I achieve this!??
});
});
});
My question is, how can I share variables between router.post and router.get?
You could create a method and call that one in both get and post routes. I would encapsulate all logic in a controller instead of directly in your route. Perhaps you could also solve this using middleware (google express middleware) but I usually see that being used for authentication or error logging.
(Sorry for the short answer. I am typing on my phone)
Related
I am trying to get a simple server-side cache up-and-running within node/express with a GET request. Managed to get this working when dealing with simple URL parameters, but not sure how to approach it with a JSON body.
This is what I have thus far for the URL parameter version:
const mcache = require('memory-cache');
app.set('view engine', 'jade');
const cache = (duration) => {
return (req, res, next) => {
let key = '__express__' + req.originalUrl || req.url;
let cachedBody = mcache.get(key);
if (cachedBody) {
res.send(cachedBody);
return;
} else {
res.sendResponse = res.send;
res.send = (body) => {
mcache.put(key, body, duration * 1000);
res.sendResponse(body);
};
next();
}
};
};
app.get('/user/:id', cache(10), (req, res) => {
setTimeout(() => {
if (req.params.id == 1) {
res.json({ id: 1, name: 'John' });
} else if (req.params.id == 2) {
res.json({ id: 2, name: 'Bob' });
} else if (req.params.id == 3) {
res.json({ id: 3, name: 'Stuart' });
}
}, 3000); //setTimeout was used to simulate a slow processing request
});
Any pointers?
For sure in your cache middleware you shouldn't be using res.send because you can only perform one response per request. So before you get to res.json in your app.get, res is already send.
I assume that you want to pass the data with that res.send to the app.get? If so you can either pass it inside next() or use res.locals like res.locals.varName = catchedBody and then you can use that inside your app.get function because it will be available during entire http request
I'm new in node.js...
I would create a chat app for school project but i have some problems with node module named "express"..
Error is in res.redirect('/');
var app = express();
var sess;
app.use(session({
secret: 'secret',
resave: true,
saveUninitialized: true
}));
app.get('/auth', function (req, res) {
var type = req.query.type;
sess = req.session;
if (type === "login") {
if (sess.loggedin) {
return res.redirect('/');
} else {
res.sendFile(__dirname + "/views/login.html");
}
}
});
app.post('/auth', function (req, res) {
var type = req.query.type;
sess = req.session;
if (type === "login") {
var login = req.body.login;
var password = req.body.password;
if (login === "" || password === "") {
res.send('You need to fill all inputs!');
res.end();
} else {
mysql query {
if (results.length > 0) {
res.send('Logged In');
sess.loggedin = true;
sess.username = results[0].username;
/* ERROR! --> return res.redirect('/'); <-- ERROR! */
} else {
res.send('Incorrect Username and/or Password!');
res.end();
}
res.end();
});
}
}
});
I watched youtube videos and readed forums but i didn't find the solution...
It will be nice if you can help me :)
It is because you have already sent a response with res.send('Logged In'). An HTTP Request is always a single request and a single response. The snippet below is taken from your code to show what happens.
res.send('Logged In'); // <-- Sends a response
res.redirect('/'); // <-- Sends another response in form of redirect.
The send methods sends a string as a response, which the client will receive. So now the headers for the response are already set and cannot send another response.
And sending a message and then redirecting doesn't make a lot of sense. You wouldn't have time to read the message because you are navigating to a different page. Lose the res.send and see what happens.
I am learning how to set up a Express server and have run into an odd issue when creating new users. The POST request goes through and gives no error and takes me to my dashboard but when I look in the sqlite DB it shows all values from the registration form saved as undefined. I have installed body-parser and called it properly into the server, I believe.
I have tried changing extended: true instead of false and added adding res.setHeader('Content-Type', 'text/plain') into the app.use as stated by body-parser guide but it still saves as undefined every time.
here is my server.js code
const app = express();
app.use(bodyParser.urlencoded({extended: false}));
app.use(bodyParser.json());
app.use(function(req, res, next) {
res.header("Access-Control-Allow-Origin", "*");
res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
res.setHeader('Content-Type', 'text/plain');
next();
});
here is the POST code in the server.js
app.post('/register', function(req, res) {
bcrypt.hash(req.body.password, saltRounds, function(err, hash) {
let db = new sqlite3.Database("./database/InvoiceApp.db");
let sql = `INSERT INTO users(name, email, company_name, password) VALUES ('${req.body.name}','${req.body.email}','${req.body.company_name}','${hash}')`;
db.run(sql, function(err) {
if (err) {
throw err;
} else {
return res.json({
status: true,
message: "User Created"
});
}
});
db.close()
});
});
and this is my form code, i am using vue.js
register () {
const formData = new FormData()
let valid = this.validate()
if (valid) {
formData.append('name', this.model.name)
formData.append('email', this.model.email)
formData.append('company_name', this.model.company_name)
formData.append('password', this.model.password)
this.loading = "Registering you, please wait"
axios.post('http://localhost:3128/register', formData).then(res => {
this.loading = ''
if (res.data.status === true) {
this.$router.push ({
name: 'dashboard',
params: { user: res.data.user }
})
} else {
this.status = res.data.message
}
})
} else {
alert('Passwords do not match')
}
}
I get no error messages when creating a new user, only if I try and login as a created user or do other actions linked to the created user. Why is my form saving as undefined in the sqlite DB?
You should use JSON format from your Vue app instead of FormData.
FormData is an object that passes with Content-Type application/x-www-form-urlencoded.
And you body parser is configured for the Json.
Please try to replace
formData.append('name', this.model.name)
formData.append('email', this.model.email)
formData.append('company_name', this.model.company_name)
formData.append('password', this.model.password)
with an actual JS object
{
"name": this.model.name
.....other fields.....
}
and pass this object or this object stringified with JSON.strinfigy(obj);
After spending a day researching FormData and JSON I figured it out thanks to this post and the answer by M U. Because I was using Vue.js and had everything from the form stored using v-model all I had to do was update the post method to:
register () {
let valid = this.validate()
if (valid) {
this.loading = "Registering you, please wait"
axios.post('http://localhost:3128/register', this.model).then(res => {
this.loading = ''
if (res.data.status === true) {
this.$router.push ({
name: 'dashboard',
params: { user: res.data.user }
})
} else {
this.status = res.data.message
}
})
} else {
alert('Passwords do not match')
}
}
and the whole form uploaded to the database with the password hashed
I am trying to have all my error messages in one file, each error is denoted by an error code, then in my functions/services, when there is an error, I call a function that takes the error code as an argument, then returns an object to the client with the error code and the respective error message from the errors.js file.
as an example, a user trying to register with an email that already exists in the database, here is how I try to do it:
// userService.js -- where my register function is
const { errorThrower } = require('../../utils/errorHandlers');
...
static async registerNewUser(body) {
const exists = await User.where({ email: body.email }).fetch();
if(exists) {
errorThrower('400_2');
}
...
}
errorHandlers.js file:
exports.errorThrower = (errCode) => {
throw Object.assign(new Error(errors[errorCode]), { errorCode })
}
exports.errorHandler = (err, req, res, next) => {
if(!err.status && err.errorCode) {
err.status = parseInt(err.errorCode.toString().substring(0, 3), 10);
}
let status, message
if (err.status) {
status = err.status
message = err.message
} else {
status = 500;
message = 'unexpected behavior, Kindly contact our support team!'
}
res.status(status).json({
errorCode: err.errorCode,
message
})
}
errors.js
module.exports = {
'400_1': 'JSON payload is not valid',
'400_2': 'user already registered',
...
}
...
const user = require('./routes/user');
const { errorHandler } = require('../utils/errors');
...
app.use('/user' , user);
app.use(errorHandler);
...
now with this setup, when hitting the register endpoint by postman, I only get the following in the console
UnhandledPromiseRejectionWarning: Error: user already registered
could someone please tell me what am I missing here?
thanks in advance!
You're not catching the error which you throw inside your errorThrower, thus getting the error UnhandledPromiseRejectionWarning. What you need to do is catch the error and pass it on the the next middleware, in order for the errorHandler-middleware to be able to actually handle the error. Something like this:
exports.register = async(req, res) => {
try {
await registerNewUser(req.body);
} catch(err) {
next(err);
}
};
If you don't want to do this for every middleware, you could create a "base"-middleware which handles this:
const middlewareExecutor = async (req, res, next, fn) => {
try {
return await fn.call(fn, req, res, next);
} catch (err) {
next(err);
}
};
Now you can pass your middlewares as an argument and delegate handling the error to the executor:
app.use('/user' , async (req, res, next) => middlewareExecutor(req, res, next, user));
I am currently trying to create a client-side reroute for users that are invalid. The server validates if the user has access to the current page and if not it returns {data: 'invalid'} in the success callback of the ajax call I check this value with the following:
if (data.status === 'invalid') {
window.location.href = domain + '/login';
return false;
}
This works sometimes but other times I receive the following browser alert message:
RequestAbortedError: Request aborted
I have attempted to swap out window.location.href with window.location.replace() and top.location.href but neither resolved the issue. I can see that the server is processing the information correctly and returning {data: 'invalid'} but as soon as it tries to run the line window.location.href I receive this error. I have an image below if it helps.
When "OK" is clicked the page does redirect to the appropriate page. The end result is happening as expected but I cannot resolve the error.
UPDATE INCLUDING SERVER SIDE CODE
function authentication (req, res, next) {
console.log('entered');
if (typeof req.rsaConPortal.email !== 'undefined') { // Check if session exists
console.log('passed 1');
User.findOne({ "user.email": req.rsaConPortal.email, "user.status”: req.resConPortal.status}, function (err, user) {
if (!user) {
console.log('failed 2');
req.rsaConPortal.reset();
res.send({status: 'invalid'});
} else {
console.log('passed 2');
req.rsaConPortal.email = user.user.email;
req.rsaConPortal.id = user._id;
req.rsaConPortal.status = user.user.status;
next();
}
});
} else {
console.log('failed 1');
res.send({status: 'invalid'});
}
}
app.get('/api/savedApp/', authentication, function(req, res) {
if (req.rsaConPortal.status !== 'registered') {
res.send({status: 'invalid'});
} else {
User.find({ "_id": req.rsaConPortal.id }, {
profile: 1, documents: 1 }, function(err, user) {
if (err) throw err;
res.send(user);
});
}
});
Is there a better way to authenticate my users? I am using Mozilla's Client-Sessions npm package
The logs on the server are logging "Passed1" and "Passed2". It is sending the client "Invalid" based off the status inside the get call.
Based on reading further about express and a few comments I have received on this question I have decided to re-think my approach and look for a better alternative which I am happy to say I have found in express.Router. I was able to create an authentication function to determine if the user is authorized and handle the business logic of whether to let the user pass or send them back to the login. Then I created a route for each page that I have that takes the business logic a step further based on the users status and either lets them pass or sends them back to login.
Thanks to everyone who looked into this and provided comments.
var router = express.Router();
app.use(router);
var authenticate = function(req, res, next) {
if (req.rsaConPortal !== undefined) {
if (req.rsaConPortal.email !== undefined) { // Check if session exists
// lookup the user in the DB by pulling their email from the session
User.findOne({ "user.email": req.rsaConPortal.email, "user.password": req.rsaConPortal.passport }, function (err, user) {
if (!user) {
// if the user isn't found in the DB, reset the session info and
// redirect the user to the login page
req.rsaConPortal.reset();
req.rsaConPortal.email = '';
req.rsaConPortal.passport = '';
req.rsaConPortal.id = '';
req.rsaConPortal.status = '';
res.redirect('../login');
} else {
req.rsaConPortal.email = user.user.email;
req.rsaConPortal.passport = user.user.password;
req.rsaConPortal.id = user._id + '';
req.rsaConPortal.status = user.user.status;
next();
}
});
} else {
res.redirect('../login');
}
} else {
res.redirect('../login');
}
};
router.get(['/','/create_warranty','/help','/marketing_assets','my_documents','profile'], authenticate, function(req, res, next) {
if (req.rsaConPortal.status !== 'approved') {
res.redirect('../login');
} else {
next();
}
});