Reputation: 17
I have tried a lot to find the problem, but nothing worked. Here the details, including my errors (at the end). I am just putting the relevant code here. I use express-validator 7.1.0
I am trying to validate a bigger html (ejs) form:
<%- include('partials/header') %>
<h1>Feature-Request hinzufügen</h1>
<body>
<form class="addrequest" enctype="multipart/form-data" id="new_document_attachment" action="/auth/request/create" method="POST">
<div class="form-group mb-2">
<label for="title">Titel</label>
<input type="text" class="form-control" id="title" name="title" maxlength="50" required>
</div>
<div class="form-group mb-2">
<!-- https://jsfiddle.net/djibe89/knv43w6t/118/ -->
<label for="description">Beschreibung</label>
<textarea class="form-control" id="description" name="description" rows="10" maxlength="<%=MAX_DESCRIPTION_CHARACTER_INPUT%>" required></textarea>
</div>
<div class="mb-2">
<label for="formFile" class="form-label">Dateianhang</label>
<div class="file-loading">
<input id="input-folder-3" name="input-folder-3[]" type="file" accept=".jpeg,.jpg,.webp,.gif,.png.pdf" multiple>
</div>
</div>
<div class="row">
<div class="form-group mb-2 col">
<label class="mb-2">Kategorie <span id="howManyCategoriesMustBeProvided" class="text-danger-emphasis">(mindestens eine)</span></label>
<% requestOptions.categoriesFromDB.forEach(category => { %>
<div class="form-check">
<input class="form-check-input" type="checkbox" value="<%= category.category %>" id="category<%= category.id %>" name="category[]">
<label class="form-check-label text-<%= category.cssClass %>" for="category<%= category.id %>">
<%= category.category %>
</label>
</div>
<% }) %>
</div>
<% if (user && user.isAdmin) { %>
<div class="col">
<label class="mb-2">Status</label>
<% requestOptions.statusFromDB.forEach((status) => { %>
<div class="form-group mb-2">
<input class="form-check-input" type="radio" value="<%= status.id %>"
id="status<%= status.id %>" name="statusId" <%= status.id === ADMIN_DEFAULT_STATUS ? 'checked' : '' %>>
<label class="form-check-label text-<%= status.cssClass %>" for="status<%= status.id %>">
<%= status.status %>
</label>
</div>
<% }) %>
</div>
<% } %>
<div class="col">
<label class="mb-2">Priorität</label>
<% requestOptions.prioritiesFromDB.forEach((priority, index) => { %>
<div class="form-group mb-2">
<input class="form-check-input" type="radio" value="<%= index +1 %>"
id="<%= priority.priority %>" name="priority" <%= index === DEFAULT_PRIORITY ? 'checked' : '' %>>
<label class="form-check-label text-<%= priority.cssClass %>" for="<%= priority.priority %>">
<%= priority.priority %>
</label>
</div>
<% }) %>
</div>
</div>
<span class="d-inline-block addrequest_submit_btn"
tabindex="0"
data-bs-toggle="popover"
data-bs-trigger="hover focus"
data-bs-placement="right"
data-bs-content="Du musst mindestens eine Kategorie auswählen.">
<button class="btn btn-primary" type="submit" disabled>Zur Überprüfung einreichen</button>
</span>
<p>Du kannst deine Anfrage nach dem Einreichen nicht mehr Bearbeiten.</p>
</form>
<%- include('partials/footer') %>
I've implemented express-validator in my utils.js like this:
const { body, validationResult } = require("express-validator");
const ensureValid = (req, res, next) => {
const result = validationResult(req);
if (!result.isEmpty()) {
return res.status(400).json({ errors: result.array() });
}
next();
};
module.exports = { body, ensureValid };
I am also using routes. In this case like this in my app.js:
const authRoutes = require('./routes/authRoutes');
app.use('/auth', authRoutes);
in my authRoutes.js i am now trying to use the validation chain like following.
const { body, ensureValid } = require('../utils');
const authController = require('../controllers/authController');
router.post('/request/create',
body('title')
.escape()
.isString().withMessage('Der Titel muss ein String sein.')
.isLength({ max: 40 }).withMessage('Der Titel darf maximal 40 Zeichen lang sein.'),
body('description')
.escape()
.isString().withMessage('Die Beschreibung muss ein String sein.')
.isLength({ min: 1, max: parseInt(process.env.MAX_DESCRIPTION_CHARACTER_INPUT, 10) || 1000 })
.withMessage(`Die Beschreibung muss zwischen 1 und ${process.env.MAX_DESCRIPTION_CHARACTER_INPUT || 1000} Zeichen lang sein.`),
body('category')
.escape()
.isString().withMessage('Ungültige Kategorie.')
.custom((value, { req }) => {
const validCategories = [
process.env.categoryName1, process.env.categoryName2, process.env.categoryName3,
process.env.categoryName4, process.env.categoryName5, process.env.categoryName6,
process.env.categoryName7, process.env.categoryName8
];
if (!validCategories.includes(value)) {
throw new Error('Ungültige Kategorie.');
}
return true;
}),
body('priority')
.escape()
.isInt({ min: 1, max: 1 }).withMessage('Priorität muss eine gültige Nummer sein.'),
body('statusId')
.escape()
.isInt({ min: 1, max: 4 }).withMessage('Status muss eine gültige Nummer (1-4) sein.'),
ensureValid,
utils.uploadStoring.array('input-folder-3[]', 5),
authController.postNewRequest
);
I can see that express-validator is doing something, but can't follow, why. The input is not passing, even though it should
my input:
title: asdasd23
description: aer
input-folder-3[]: (binary)
category[]: Funktionswunsch
statusId: 2
priority: 2
The validation error:
{
"errors": [
{
"type": "field",
"value": "",
"msg": "Die Beschreibung muss zwischen 1 und 1000 Zeichen lang sein.",
"path": "description",
"location": "body"
},
{
"type": "field",
"value": "",
"msg": "Ungültige Kategorie.",
"path": "category",
"location": "body"
},
{
"type": "field",
"value": "",
"msg": "Priorität muss eine gültige Nummer sein.",
"path": "priority",
"location": "body"
},
{
"type": "field",
"value": "",
"msg": "Status muss eine gültige Nummer (1-4) sein.",
"path": "statusId",
"location": "body"
}
]
}
Wired: I am logging my requests like this:
app.use((req, res, next) => {
console.log(`${req.method} ${req.url}`, req.body);
next();
});
Output: POST /auth/request/create {}
without express-validator middleware it's the same, but the following (request creating) middleware is successfully acessing the req.body
I tried different validation chains, inputs and express-validaor imports.
Upvotes: 1
Views: 124
Reputation: 1974
Agree with @traynor. req.body will come into existence only if the multer upload is invoked prior to express-validator.
One thing to note in this case is that even if the validations fail, the upload - the file, would remain uploaded in the server. This may not be the desired behaviour. The validation should have control over upload action. And the upload should abort if validation fails, it should complete if validations are successfully passed.
Multer has provided an option for this, it is fileFilter. The two posts referenced in the citations employ this option. For more, please refer to the third link.
Citations:
Trouble Accessing req.body in Middleware: Getting Empty Response
How to perform the form validation for title and description and then upload file using multer
Upvotes: 0
Reputation: 8752
On route /request/create
handler, express validator runs first, and as you're sending a multipart request, it cannot access req.body
, because it hasn't been processed yet, which is why it's an empty object {}
, causing validation errors.
So, on route handler, change the order of middleware so, that multer middleware runs first, i.e. before other middleware using req.body
, i.e. express validator:
router.post('/request/create',
// run multer middleware before other using req.body
utils.uploadStoring.array('input-folder-3[]', 5),
// now run express validator
body('title')
.escape()
//...
// rest...
ensureValid,
authController.postNewRequest
);
Upvotes: 1