Reputation: 3782
I created a REST api and want to validate the body and the params before calling the controller logic. For the validation I use Joi (https://www.npmjs.com/package/joi).
Let's say I have a route with one url parameter and some body variables. The params
object contains this url parameter but Joi still returns a 400
. The detail message is
"userId" is required
I tried to create a minimalistic example showing my code. To reproduce the error create the app.js file
const express = require('express');
const bodyParser = require('body-parser');
const morgan = require('morgan');
const cors = require('cors');
const app = express();
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());
app.use(cors());
app.use('/users', require('./routes/users.js'));
app.listen(3000);
Due to the fact each validation fails there is only one route required to test it. Create users.js with the following content
const express = require('express');
const router = express.Router();
const usersController = require('../controllers/users.js');
const usersControllerPolicy = require('../policies/users.js');
router.get('/:userId', usersControllerPolicy.getUserById, usersController.getUserById);
module.exports = router;
And this users.js controller file
exports.getUserById = async (req, res, next) => {
const { userId } = req.params;
return res.status(200).json("everything is fine");
};
When it comes to the policy I created the users.js policy which adds the required schema to the middleware
const joi = require('joi');
const schemaValidation = require('../middleware/schemaValidation.js');
module.exports = {
getUserById: (req, res, next) => {
schemaValidation({
params: {
userId: joi.string().guid().required()
},
body: {}
}, req, res, next);
}
}
and then the schema gets validated by my schemaValidation.js
const joi = require('joi');
module.exports = (schema, req, res, next) => {
const { error } = joi.validate(req, schema);
if (error)
return res.status(400).json("something went wrong");
next(); // execute the controller logic
}
As you can see I pass in the whole req
object. I do this because sometimes I have to validate the body and the params. The url parameter userId
is not found by Joi so I get returned a status code of 400.
How can I fix my middleware validation to validate both objects within the req
object?
Upvotes: 5
Views: 3091
Reputation: 18493
Actually Joi has access to the userId
and can properly validate it or not, here's why:
// replace this
const { error } = joi.validate(req, schema);
// by this
console.log(req.params.userId);
const { error } = joi.validate(req, schema);
console.log(error.toString());
console output while visiting localhost:3000/users/10
:
10
ValidationError: child "params" fails because [child "userId" fails because ["userId" must be a valid GUID]]
and while visiting an URL including a valid GUID in the params, like ``:
ad3756ae-2661-4d8c-aeda-dd51deef5ea9
ValidationError: "_readableState" is not allowed. "readable" is not allowed. "_events" is not allowed. "_eventsCount" is not allowed. "_maxListeners" is not allowed. "socket" is not allowed. "connection" is not allowed. "httpVersionMajor" is not allowed. "httpVersionMinor" is not allowed. "httpVersion" is not allowed. "complete" is not allowed. "headers" is not allowed. "rawHeaders" is not allowed. "trailers" is not allowed. "rawTrailers" is not allowed. "aborted" is not allowed. "upgrade" is not allowed. "url" is not allowed. "method" is not allowed. "statusCode" is not allowed. "statusMessage" is not allowed. "client" is not allowed. "_consuming" is not allowed. "_dumped" is not allowed. "next" is not allowed. "baseUrl" is not allowed. "originalUrl" is not allowed. "_parsedUrl" is not allowed. "query" is not allowed. "res" is not allowed. "route" is not allowed
So Joi has access to everything that it needs and works as expected. 🚀
So why the error 400?
Well, as logged in the console, Joi fails the validation for req
. That is because, by default, Joi doesn't accept unknown params in an object. You can change this with .unknown(true)
which validates an object even if unknown parameters are included.
So, in your case, you should replace the code of policies/users.js
by the following:
const joi = require('joi');
const schemaValidation = require('../middleware/schemaValidation.js');
module.exports = {
getUserById: (req, res, next) => {
// properly defines a schema with optional parameters
const schema = joi.object({
params: joi.object({
userId: joi.string().guid().required()
}).unknown(true),
}).unknown(true);
schemaValidation(schema, req, res, next);
}
}
Now, when you visit again an URL including a valid GUID (like http://localhost:3000/users/ad3756ae-2661-4d8c-aeda-dd51deef5ea9), everything happens as expected and ""everything is fine"
is being sent by the server! 🎉
Upvotes: 5