skip to Main Content

I am starting my end-of-year study project (due by mid-July :)).
I’m trying to make a small site, and to stand out I’m not doing it in php but with node.js.

I discovered this technology and I am already facing a first problem.
(After having trouble coding DB) I want to do an authentication but I am unable to retrieve the data from the <form>.
When I do a log of my req.body.password, it returns undefined and for req.body, {}.
I do not understand why.

Here is the code I already made:

index.js

require('dotenv').config();
const express = require("express");
const https = require("https");
const path = require("path");
const fs = require("fs");


const port = process.env.PORT || 4242;
const app = express();

// Analyser les corps de requête au format URL-encoded
app.use(express.json());
app.use(express.urlencoded({ extended: true }));

app.set('views', './views');
app.set('view engine', 'ejs');

app.use('/', require('./controllers/homeController')); 

app.use('/images', express.static(path.join(__dirname, 'images')));

const sslServer = https.createServer({
    key: fs.readFileSync(path.join(__dirname, 'cert', 'key.pem')),
    cert: fs.readFileSync(path.join(__dirname, 'cert', 'cert.pem'))
}, app);
sslServer.listen(port, () => {
    console.log("Le serveur fonctionne !");
    console.log(process.env.PORT)
});

home.ejs

...
<main class="main">
   <div class="parent">
      <form method="post" class="enfant" id="loginForm">
         <input type="password" name="password" placeholder="password" id="pwd" required>
         <button type="submit" id="bouton">valider</button>
      </form>
   </div>
</main>
...
<script>
   document.getElementById("loginForm").addEventListener("submit", function(event) {
      event.preventDefault();
      const formData = new FormData(this);
      fetch('/', {
         method: "POST",
         body: formData
      })
      .then(response => {
         if (response.ok) {
            window.location.href = "/dashboard"; // Rediriger vers la page du tableau de bord si la connexion réussit
         } else {
            return response.text();
         }
      })
      .then(message => {
         document.getElementById("message").innerText = message;
         //alert(message);
      })
      .catch(error => {
         console.error('Error:', error);
      });
   });
</script>

User.js

const mysql = require("mysql2/promise");
require('dotenv').config();

class User {

  constructor(){
    this.dbConnect = mysql.createPool({
      host: process.env.DB_HOST,
      user: process.env.DB_USERNAME,
      password: process.env.DB_PASSWORD,
      database: process.env.DB_NAME
    });
  }

  async get(){
    try{
      const connexion = await this.dbConnect.getConnection();
      const [res, fields] = await connexion.execute(`SELECT * FROM User;`);
      connexion.release();
      return await res;
    }
    catch(err){
      return await err;
    }
    finally{
      this.dbConnect.end();
    }
  }
}
module.exports = User;

homeController.js

const express = require('express');
const limiter = require('express-rate-limit');
const User = require('../models/User');

const app = express();
const homeRegex = /^/(home)?$/;

// Middleware pour analyser les corps de requête
app.use(express.json());
app.use(express.urlencoded({ extended: true }));

//limiter à 5 tentatives de log sur 5 minutes
const signInlimiter = limiter({ 
    windowMs: 5*60*1000, 
    limit: 1, 
    message: (req, res) => { 
        date = (new Date(req.rateLimit.resetTime)).toLocaleTimeString(); 
        return `Trop de tentatives ! Vous ne pourrez pas retenter avant ${date} ⏰.`; 
    },
    keyGenerator: (req, res) => {req.ip}
});

//REQUETES

app.get(homeRegex, (req, res) => { res.render("home.ejs", { message: "" }); });

// 429 par défaut
app.post(homeRegex, signInlimiter,  async (req, res) => {
    // Vérifier les informations de connexion et renvoyer une réponse appropriée (pour l'instant pwd = password)
    const password  = req.body.password;
    console.log(req.body)
    try {
        const userData = await getUser();
        const pwdData = userData[0].pwd;
        //non haché
        if (password === pwdData) {
            res.status(200).send("Bienvenue 💙🤍❤️");
        } else {
            res.status(401).send("Invalide 💀💀💀");
        }
    } catch (err) {
        
    }
});

async function getUser() {
    let user = new User();
    try {
        const userData = await user.get();
        return JSON.parse(JSON.stringify(userData, null, 2));
    } catch (err) {
        console.log(err);
        return null;
    }
}
module.exports = app;

I’ve already asked ChatGPT but obviously it’s not all powerful ;b
Can you explain to me why it doesn’t work.
Everything seems ok to me though (from my point of view, new to this tech).

Thanks.

2

Answers


  1. Chosen as BEST ANSWER

    With your answers I was able to modify the code so that everything works. Thanks.

    Here are the changes made:

    home.ejs

    <script>
       document.getElementById("loginForm").addEventListener("submit", function(event) {
          event.preventDefault();
          fetch("/", {
             method: "POST",
             body: JSON.stringify({
                //`this` = instance HTMLFormElement de l'élément <form>
                password: this.elements["password"].value,
             }),
             headers: {
                "Content-Type": "application/json"
             }
          })
          .then(response => {
             if (response.ok) {
                window.location.href = "/dashboard"; // Rediriger vers la page du tableau de bord si la connexion réussit
             } else {
                return response.text();
             }
          })
          .then(message => {
             document.getElementById("message").innerText = message;
             //alert(message);
          })
          .catch(error => {
             console.error('Error:', error);
          });
       });
    </script>
    

  2. To explain the issue, I’ll first explain some details about how the express.json() and express.urlencoded() middleware work. Or just skip to the solution.

    Background

    When you make a HTTP POST request, the body data itself is basically just text or bytes, and the Content-Type header is used to tell the server how it should interpret that data. The various body parsing middleware that Express provides look at this header to decide whether they need to take action or not.

    For example, looking at the ExpressJS docs, the default Content-Type values they’re looking for are the following:

    Middleware Content-Type What it does
    express.json() application/json Parses JSON text into an object to req.body
    express.urlencoded() application/x-www-form-urlencoded Parses URL params (think: the part after ? in an URL, aka "query string") into an object to req.body
    express.raw() application/octet-stream Reads the body as bytes into a Buffer
    express.text() text/plain Reads the body as text into a string to req.body

    Typically if using a <form> element to make a POST (i.e. allowing the browser to submit the form), you can specify one of the following via its enctype attribute, and the browser will handle the encoding before sending the request:

    1. application/x-www-form-urlencoded (default if not specified)
    2. multipart/form-data – used when uploading files as it allows you to send parts of the data with different Content-Types per part
    3. text/plain

    When using fetch, you can manually specify a headers object in the options and set e.g. "Content-Type": "application/x-www-form-urlencoded", but there are also some classes with special handling that set it for you and encode the data for you. These are:

    1. FormData, which you’ve used here, sets Content-Type: multipart/form-data
    2. URLSearchParams which sets Content-Type: application/x-www-form-urlencoded

    The issue

    Onto the issue with your snippet. The <script>‘s JavaScript code uses FormData with fetch. However, none of the middleware you’re using handle this type.

    The solution

    You can keep using FormData if you intend on uploading files with this form, but you’ll need to install and use e.g. the multer package, which handles parsing of multipart/form-data data.

    If you don’t intend on adding files to this form, I would recommend using either JSON or urlencoded data, as you already have the middleware for parsing those:

    JSON

    Reference: Uploading JSON data

    fetch("/", {
      method: "POST",
      body: JSON.stringify({
        // inside this event handler, `this` refers to the <form> element's HTMLFormElement instance
        // see https://developer.mozilla.org/en-US/docs/Web/API/HTMLFormElement/elements
        password: this.elements["password"].value,
      }),
      headers: {
        "Content-Type": "application/json"
      }
    }).then((response) => {
      if (response.ok) {
        window.location.href = "/dashboard"; // Rediriger vers la page du tableau de bord si la connexion réussit
      } else {
        return response.text();
      }
    });
    

    urlencoded

    fetch("/", {
      method: "POST",
      body: new URLSearchParams({
        password: this.elements["password"].value,
      })
      // no `headers` because `fetch` recognizes `URLSearchParams` and handles it for us
    }).then((response) => {
      if (response.ok) {
        window.location.href = "/dashboard"; // Rediriger vers la page du tableau de bord si la connexion réussit
      } else {
        return response.text();
      }
    });
    

    For a list of what fetch accepts into its body, see: Body – Using the Fetch API.

    Other notes

    Other notes about your snippets:

    • in homeController.js, don’t create a new Express app, but use express.Router() (replace const app = express() with const app = express.Router())
    • Both the index and homeController files apply the .json() and .urlencoded() middleware. This is not necessary, as the middleware applied before app.use("/", require(...)) are also applied to that router (homeController.js). Consider only doing it before routes that you actually expect to get such input in, so only in homeController.
    • the keyGenerator of your rate limiter middleware always returns undefined so every user is probably subject to the same shared limit. This is because (req, res) => {req.ip} has {} so it has a block body. Either change it to (req, res) => req.ip or (req, res) => { return req.ip }
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search