I'm working on a simple login for a web application, and can't seem handle .getValidationResult() correctly. I've spent quite a bit of time pouring over the npm documentation for express-validator, trying to find an answer in tutorials, and looking on sites like Stack Overflow without managing to find the answer to my question. Perhaps I just don't know the right question to ask.
I want to ensure that
the user submitted something that has the form of an email address,
that the password isn't empty. I then want to
sanitize the email before interacting with the DB later on, then
check to see if any of the first 3 procedures failed. If there were failures, return the user to the login page.
My question is what is the correct way to use express-validator's .getValidationResult()?
Here's the offending piece of code:
export let postLogin = (req: Request, res: Response, next: NextFunction) => {
req.assert("email", "Email is not valid").isEmail();
req.assert("password", "Password cannot be blank").notEmpty();
req.sanitize("email").normalizeEmail({ gmail_remove_dots: false });
req.getValidationResult().then(function(result){
if (result != undefined) {
console.log(result.array();
return res.redirect("/login");
}
});
//do other login related stuff
}
I'm guessing that something simple is causing my error here, but I can't seem to find what it is.
It returns a promise for an object called Validation Object. This object contains information about the errors that your application has had.
The explanation.
Runs all validations and returns a validation result object for the
errors gathered, for both sync and async validators.
All it does is returning errors if there is one. Here is some example code returned by that function.
//The error object
{
"msg": "The error message",
"param": "param.name.with.index[0]",
"value": "param value",
// Location of the param that generated this error.
// It's either body, query, params, cookies or headers.
"location": "body",
// nestedErrors only exist when using the oneOf function
"nestedErrors": [{ ... }]
}
The function returns isEmpty() when there is no errors to display.
The function returns .array([options]) if there are any errors. Errors are located in [options] array.
Check out this link for the example code of what it might return.
UPDATE
You can also just use it like this, which is easier.
Please note that this is new API as of v4.0.0 release of express-validator.
const { check, validationResult } = require('express-validator/check');
//go to a link
app.get('/myURL', (req, res, next) => {
// Get the validation result\
const errors = validationResult(req).throw();
if (!errors.isEmpty()) {
return res.status(422).json({ errors: errors }); //err.mapped()
});
Related
I am quite new to node and am currently encountering an error that seems pretty common: Error: Can't set headers after they are sent to the client.
I've read a few threads but cannot wrap my head around this one, here is my code so far:
router.get('/:id', (req, res) => User.findAll({
where : {
id: req.params.id,
},
attributes : {
exclude : ['updatedAt'],
}
})
.then(user => {
res.send(user);
res.sendStatus(200);
})
.catch((err) => console.log(err)));
What's wrong here? Any help and advices would be greatly appreciated!
The real reason behind this problem is that you are using both
res.send(user) and res.sendStatus(200).
The error says: Error: Can't set headers after they are sent to the client
You already send something to the client (in this case res.send(user)) and you can't send another thing now (in this case res.sendStatus(200)).
Once you do res.send, res.redirect, res.sendStatus, res.render, you should not do add any of these again.
You will get this problem if you are using more than one at the same time. Even if you have something like:
for(condition) {
res.send("1st");
}
res.send("2nd");
You should add return statements in front of them like:
for(condition) {
return res.send("1st");
}
return res.send("2nd");
Also as #Halil Akar said that 200 status is always returned with res.send. You can also use res.status(301).send(user) to send data and a status at the same time.
I hope it helps.
I would like to prevent a registration with an email address which already exists. Is it possible to use express-validator's new syntax for this? For example:
router.post('/register', [
check('email').custom((value, {req}) => {
return new Promise((resolve, reject) => {
Users.findOne({email:req.body.email}, function(err, user){
if(err) {
reject(new Error('Server Error'))
}
if(Boolean(user)) {
reject(new Error('E-mail already in use'))
}
resolve(true)
});
});
})
]
....
How would i pass Users?
express-validator is only aware of the request object itself, what keeps its complexity pretty low for the end-user.
More importantly, it only truly knows about the request's input locations -- body, cookies, headers, query and params.
Your custom validator is completely correct. That being said, it might not be testable, as you seem to be depending on global context.
In order to make it testable, the 2 options that I see are:
1. Inject req.Users:
This one would involve using some middleware that sets your store objects onto req:
// Validator definition
const emailValidator = (value, { req }) => {
return req.Users.findOne({ email: value }).then(...);
}
// In production code
// Sets req.Users, req.ToDo, req.YourOtherBusinessNeed
app.use(myObjectsStore.middleware);
app.post('/users', check('email').custom(emailValidator));
// In tests
req = { Users: MockedUsersObject };
expect(emailValidator('foo#bar.com', { req })).rejects.toThrow('email exists');
2. Write a factory function that returns an instance of your validator:
This is my preferred solution, as it doesn't involve using the request object anymore.
// Validator definition
const createEmailValidator = Users => value => {
return Users.findOne({ email: value }).then(...);
};
// In production code
app.post('/users', [
check('email').custom(createEmailValidator(myObjectsStore.Users)),
]);
// Or in tests
expect(createEmailValidator(MockedUsersObject)('foo#bar.com')).rejects.toThrow('email exists');
Hope this helps!
Converting my comments into a final, conclusive answer here :
A validator is simply supposed to validate the fields of request entities against the given criteria of data type / length / pattern.
You would need to write the method yourself, to determine if the user pre-exists or not. An express-validator ( or rather any validator ) would not do the task of cherry picking if the item exists in your list of items ( or your data-source), neither should it interact with the data-source concerned.
In node 8.11.1, express 4.16.3 I installed
"express-messages" : "*",
"connect-flash" : "*",
"express-validator" : "*",
"express-session" : "*"
I have a page that gets all articles app.get('/', function(req, res){.... and also has a "New" button, for adding a new article. When hitting New, a pop-up Bootstrap form appears.
I want to check for empty form fields. The HTML form field that I check is
<input type="text" name="name" id="name" class="form-control" />
The form's action goes to
const { check, validationResult } = require('express-validator/check');
app.post('/add', [check('name').exists()],
function(req, res) {
const errors = validationResult(req);
console.log(errors.isEmpty());
if (!errors.isEmpty()) {
req.flash('err', 'errors'); //test
res.redirect('/');
}
else {
pool.connect(function(err, client, done) {
if (err) {
return console.log('db connect error '+ err);
}
client.query('insert into event (name) values($1)',
[req.body.name]);
done();
req.flash('success', 'saved');
res.redirect('/');
}); //pool connect
}//else
});
I am based in this. Whatever I do, the console.log(errors.isEmpty()); is always TRUE, so I end up saving to my DB empty fields. So, the express-validator does not catches errors ?
Also, I am not sure how to pass the errors back in the / route, so the errors can be rendered, this is why I just use req.flash('err', 'errors'); for now. Do I use req.flash like for the success part?
Please help me debug this.Thanks
express-validator maintainer here.
Regarding express-validator letting empty strings come through
check() will create a validator chain for something in the request named name.
It's not assertive of the location of the field, it could be req.query.name or req.body.name, for example.
If you want specifically req.body, then use the other chain creator body().
Then, even if you do have name in req.body, please note that check('name').exists() is not validating its content. It's simply checking it exists, as the name says.
If you want to check that it has some content, whatever that is, you can use check('name').not().isEmpty().
Regarding passing the errors to the redirected page
You can call either errors.array() or errors.mapped() to get the actual errors (please check the Validation Chain API for details).
I haven't used req.flash, so you should test if you can pass an JS object in there, or if you can only pass strings.
If you must use a string, then JSON.stringify/JSON.parse should be your friends to transport your errors.
I am busy building functional tests for an existing nodejs api I wrote. I am using mocha and expect.js.
One test that is failing is when I want to do a negative test for the existence of a url parameter. What I want is to send a message back to the the consumer of the service if the param is missing.
Currently the positive test I have works:
var request = require('request');
it('get a specific user from the api', function (done) {
request.get('http://localhost:8080/api/v1/user/123', function (error, response, body) { ... });
});
However, the following does not work and all I get is a timeout:
it('should return a 400 message if UserId is not supplied stating that "UserId expected"', function(done) {
request.get('http://localhost:8080/api/v1/user/', function (error, response, body) {
if (!error && response.statusCode == 400) {
expect(body).to.be.equal('UserId expected');
done();
}
});
});
Both the above tests are testing this endpoint:
app.get('/api/v1/user/:userid', function (req, res) {
if(!req.params.userid) {
return res.status(400).json('UserId expected');
}
... get the user and return it with status 200 ...
});
However, as stated before, the first test is successful while the second test times out.
Update 1:
Additionally, I am getting the following output:
GET /api/v1/user/123 200 2.905 ms - 274
GET /api/v1/user/ - - ms - -
Update 2:
So adding the following code solves the problem, but doesn't really solve the problem:
app.get('/api/v1/user', function (req, res) {
return res.status(400).json('UserId expected');
});
Obviously I don't want a rather redundant piece of code sitting around. I am baffled as to why I cannot enter the 'api/v1/user/:userid' endpoint and just test for the existence of :userid in there? It's as if nodejs is requiring the existence of :userid, otherwise the endpoint does not exist. Is this correct?
What am I missing?
According to mocha documentation the done callback must always be called, also in case of errors. In fact it accepts an error.
So I think that you must rewrite your code with:
it('should return a 400 message if UserId is not supplied stating that "UserId expected"', function(done) {
request.get('http://localhost:8080/api/v1/user/', function (error, response, body) {
if (error) return done(error);
if (response.statusCode != 400) return done(new Error("Not 400"));
expect(body).to.be.equal('UserId expected');
done();
});
});
So I did some more investigation and asked another question regarding this issue here.
In the end the issue was that I didn't fully understand the express framework. Basically what I alluded to in Update 2 of my question is the route I will need to follow and specify a route where no param is expected, but return the error message I am expecting in that case.
The problem I have with this approach is that in some instances one might want to use more than one parameter in the ur. However, this will mean the permutations to cater for each scenario where a parameter is undefined gets a bit daunting. (2 params will mean 4 combinations, 3 will mean 9 etc.) This will be a maintenance nightmare!
My approach from now on will be to rather follow an approach where any info I want to pass to the server will be sent via JSON in the body from of each request. This will allow me to keep to one single route.
I have been dealing with this problem now for quite a while, and I can't seem to figure out why it's happening.
I'm getting the Error: Can't set headers after they are sent.
I was able to track down the offending Function Call via the Stack Trace, which leads me to believe that the error lies within this function:
exports.getCardUser = function(req, res, next) {
if (req.user) {
User.cardUser(req.user.userId, function(responseValue) {
res.json(200, responseValue);
});
} else {
res.send(401);
}
};
However, if I hit the Endpoint via a regular REST Client, iex: Hitting the API Endpoint in an Isolated Environment, the Error is not being thrown.
Any ideas?
Edit: skypjack's brought me on the right track - Callback was called twice. Thanks!