איך להתמודד עם FormData מתוך קוד Express

14/06/2023

אקספרס היא עדיין פריימוורק מאוד פופולרית לבניית שרתים קטנים ב Node.JS ולכן קצת הפתיע אותי לגלות שאין לה תמיכה מהקופסה במידע שנשלח מטפסים בצורה אסינכרונית. בואו נראה את הבעיה בקצרה ופיתרון לא מסובך.

1. מאיפה מקבלים FormData

מנגנון fetch של דפדפנים יודע להוציא מידע מטפסים ואף לשלוח אותו לשרת בצורה אסינכרונית, בלי JSON ובלי jQuery. לדוגמה אם יש לי את הטופס הבא ב HTML:

<form>
  <label>Name
    <input type="text" name="name" />
  </label>
  <label>Favorite Color
    <input type="color" name="color" />
  </label>
  <input type="submit" value="Save" />
</form>

אני יכול לטפל באירוע submit ולקבל את כל השדות של הטופס לאוביקט מסוג FormData עם הקוד הבא:

function handleSubmit(ev) {
  ev.preventDefault();
  const form = ev.target;
  const fd = new FormData(form);
  for (let [k, v] of fd.entries()) {
    console.log(`${k} => ${v}`)
  }
}

document.querySelector('form').addEventListener('submit', handleSubmit);

בעבר אולי הייתם רוצים לעצור כאן, להפוך את ה FormData ל JSON ולשלוח לשרת, אבל היום עם Fetch API אפשר פשוט לשלוח את האוביקט עצמו והכל יעבוד:

fetch(url, { body: fd });

ונשאר רק לשכנע את השרת לקרוא את גוף ההודעה.

2. קוד צד השרת

באקספרס יש תמיכה מובנית במידע שמגיע בתור Url Encoded Form או בתור json, אבל FormData נשלח בתור multipart/form-data. לכן ניסיון רגיל לקרוא את ה req.body מתוך נתיב אקספרס לא יפרסר אותו.

הספריה multer, שבדרך כלל עוזרת לנו לקרוא קבצים שנשלחים בטופס, יכולה להציל את המצב. אחרי התקנת הספריה אני מוסיף את ה Middleware שלה לנתיב ומקבל את המידע מהטופס בתור אוביקט:

app.post('/tasks', multer().none(), (req, res) => {
  // body.req is now an object
  // whose keys are the "names" of the fields in the form
  console.log(req.body);
  res.sendStatus(200);
});

סך הכל המעבר ל FormData במקום לשליחת json-ים הוא נוח כי הוא חוסך תרגום של השדות בטופס ל JSON ואת התרגום מאוביקטים למחרוזות בשני הצדדים, ולמרות חוסר התמיכה המובנה באקספרס, הפיתרון עצמו בעזרת מולטר מאפשר קריאת המידע בדיוק כאילו קיבלנו JSON.