בסוף זה תמיד הטיפול בשגיאות

18/09/2024

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

create_user(!!params[:quickjoin]) do |user|
  user.after_confirmation
end

נו, למעשה המימוש נמצא בתוך הבלוק after_confirmation אבל זה לא חשוב לשיחה שלנו כרגע. ועכשיו שאני קורא את זה אני גם לא בטוח מה תפקיד הפרמטר quickjoin, אבל גם זה לא חשוב לשיחה שלנו כרגע.

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

clean_up_passwords resource
set_minimum_password_length
return redirect_to quickjoin_path, alert: resource.errors.full_messages.join(', ') if params[:quickjoin] == '1'

respond_with resource

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

ב Rails הפונקציה respond_with היא קיצור לזה:

def create
  @user = User.new(params[:user])
  respond_to do |format|
    if @user.save
      flash[:notice] = 'User was successfully created.'
      format.html { redirect_to(@user) }
      format.xml { render xml: @user }
    else
      format.html { render action: "new" }
      format.xml { render xml: @user }
    end
  end
end

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

תיקונים? קל. קודם כל ב create_user עדיף להחליף את respond_with בגירסה הארוכה שלה, ואז בכישלון גם להפעיל redirect לקישור ממנו הוא ניסה להירשם. בנוסף להוסיף בדיקות צד-לקוח בטופס הרישום. בדיקות אלה אומנם לא יתפסו את כל הבעיות אבל ישפרו את החוויה למי ששכח למלא תוכן באחד השדות.