• בלוג
  • על שליחת פרמטרים בלי Content Type (ולמה התקלקל לי הדמו בוובינר אתמול)

על שליחת פרמטרים בלי Content Type (ולמה התקלקל לי הדמו בוובינר אתמול)

04/02/2022

במהלך וובינר על Next.JS שהעברתי אתמול ניסיתי לשלוח הודעה לשרת מתוך curl עם הפקודה הבאה:

$ curl -X POST -d hello 'localhost:3000/api/todos'

קוד צד השרת שקיבל את ההודעה נראה כך:

export default function handler(req, res) {
  console.log(`add ${JSON.stringify(req.body)}`);
  addTodo(req.query.text);
  console.log(`todos = ${JSON.stringify(getTodos())}`);
  res.status(200).json({});
}

וכמו שקורה לעתים קרובות כשמנסים לאלתר במהלך Live Coding הקוד לא עבד. במקום ש req.body יכיל את המילה hello כמו שהתכוונתי שיקרה, הוא הכיל אוביקט. תוצאת ההדפסה היתה:

add {"hello":""}

כלומר req.body הכיל אוביקט עם מפתח יחיד בשם hello וערך ריק.

מה?

בחזרה ל curl, המתג -d אומר שמה שיבוא אחריו הוא גוף ההודעה, והמתג -X POST אומר שאני שולח בקשה מסוג POST. בסך הכל יש לנו בקשת POST שגוף ההודעה שלה מורכב מהמילה הבודדת hello. אז איך req.body הגיע להיות אוביקט, ואיך hello הגיע להיות המפתח?

תוכן עניינים

  1. החשיבות של Contet Type

1. החשיבות של Contet Type

התשובה נעוצה באיך ש next.js, וכמוהו Web Frameworks רבים בצד השרת, מפענח את גוף ההודעה שמקבל והופך אותו מטקסט פשוט לתוכן של req.body. בתיעוד של next מסבירים לנו ש req.body הוא:

req.body - An object containing the body parsed by content-type, or null if no body was sent

וזה מסביר למה בתוך req.body לא קיבלתי בדיוק את הטקסט שאותו שלחתי בתוכן הבקשה. אותה מילה, hello, עוברת פיענוח והופכת לאוביקט לפי כללי פיענוח שנקבעים לפי הכותרת content-type. בהמשך אותו דף תיעוד מסבירים לנו גם שכל נתיב API יכול לייצא אוביקט בשם config בעזרתה אפשר לבטל את מנגנון פיענוח ה body האוטומטי. אבל ביטול מנגנון הפיענוח האוטומטי לא ממש יעזור לי כאן. בלי מנגנון זה אני אקבל את גוף ההודעה בתור Stream ואצטרך לקרוא קטעים ממנו ולחבר אותם יחד לטקסט אחד ארוך כשההודעה מסתיימת, וחבל לכתוב קוד למנגנון שכבר עובד בפריימוורק.

במקום לבטל את מנגנון הפיענוח אני יכול להגיד לו בצורה מדויקת יותר מה אני רוצה. העברה של המילה application/json בתור content-type תגרום למפענח להתיחס לגוף ההודעה בתור JSON Object. העברה של כל מילה שהוא לא מכיר כמו foobar או text תגרום למפענח להסתכל על גוף ההודעה בתור טקסט ולהחזיר בתוך req.body מחרוזת פשוטה עם התוכן - שזה בעצם מה שרציתי שיקרה. והעברה של המילה application/x-www-form-urlencoded, שהיא גם ברירת המחדל כלומר היא שיטת העבודה שתיבחר אם לא כתבתי שום ערך ל content-type, תגרום למפענח להפוך את גוף ההודעה לאוסף של פרמטרים בפורמט של key=value.

אז הנה הסיכום של שלוש פקודות curl שאני מריץ והתוצאה שאני מקבל ב req.body עבור כל אחת:

$ curl -X POST -H 'Content-Type: application/x-www-form-urlencoded' -d hello 'localhost:3000/api/todos'
# req.body = {"hello":""}

$ curl -X POST -H 'Content-Type: text/plain' -d hello 'localhost:3000/api/todos'
# req.body = "hello"

$ curl -X POST -H 'Content-Type: application/json' -d hello 'localhost:3000/api/todos'
# Error - Invalid JSON

חוץ מזה אפשר לתפוס הקלטה של הוובינר בערוץ יוטיוב של ניר (מומלץ לבקר שם בכל מקרה) בקישור https://www.youtube.com/watch?v=OjMqzBWuTl0 ובימים הקרובים היא גם תעלה פה לאתר לאזור הקלטות מוובינרים.