במערכת ה-CMS החדשה שאנו מפתחים, הוחלט שבעוד שהמערכת תהיה ב-dotnet, האתרים עצמם יהיו HTML טהור. כלומר, אנו זקוקים למנגנון שיהפוך את דפי ה-aspx של כל אתר לדפי html.
התחלה חשבנו שעבור כל דף במערכת ניצור כפתור “פרסום”, שבהקלקה עליו יופעל מנגנון ייצוא ל-HTML. אז הלכנו בכיוון הזה, ומצאנו קוד שהראה איך לעשות Override למתודה Render (זו המתודה שבעצם מציירת את דף ה-HTML), ללכוד בה את מחרוזת ה-HTML של הדף, ולעשות איתה מה שאנחנו רוצים (שזה להכניס את זה לקובץ עם סיומת HTML ולשים את הקובץ בכתובת שהמשתמש בחר לאתר שלו). וזה עבד יפה מאד.
כמובן שלא בכל כניסה לדף בוצעה הפעולה הזאת. בכל כניסה, ישנה בדיקה: האם כבר קיים קובץ HTML לדף הזה? אם כן – אל תיצור חדש.
ואז עלתה השאלה איך נעדכן את דף -HTML כשמתעדכנים תכנים. הרי פונקציית ה-render מופעלת רק בכניסה לדף, ולא לאחר כל עדכון (עריכת התכנים מתבצעת ב-AJAX). ברור, אם כך, שצריך טכניקה אחרת לייצוא ה-HTML של התכנים הערוכים. הוחלט על פונקציה נוספת, שתופעל בעת עריכת תכנים. כאשר מתשמש עורך טקסט ושומר אותו, פעולת השמירה לא רק תכניס אותו ל-DB, אלא גם תעדכן את דף ה-HTML הרלוונטי. תהיה מתודה שתקבל את פרטי הטקסט, והדף, והיא תמצא את קובץ ה-HTML הרלוונטי, תקרא אותו ל-string, תבצע replace ב-string לפי ה-ID של פריט הטקסט, ותחזיר את ה-string לתוך הקובץ.
אלא שב-CMS שלנו אנחנו עושים עוד משהו מיוחד: מיד עם יצירת האתר המערכת מייצרת אוטומטית אתר שלם עם מבנה מומלץ של דפים. וכמובן כל הדפים צריכים להיות HTML-ים. כלומר, לא מדובר כאן על משתמש שנכנס לדף ASPX ואז מופעלת מתודת ה-render, אלא מדובר על כך שהמערכת עצמה צריכה “להכנס” לדפי ה-aspx ולהפעיל בהם את מתודת ה-render.
הממ… זה כבר נראה יותר מורכב. בהתחלה חשבנו להשתמש ב-Response.Redirect. כלומר, בעת יצירת אתר, מתודת היצירה ב-BL תעשה redirect לדף הראשון ואז תופעל מתודת ה-render של הדף ההוא ותייצא אותו ל-HTML, ונשלח גם פרמטר מסוים שיאמר לדף ההוא לעשות redirect לדף הבא, וכן הלאה.
מצד אחד, זה נראה הפתרון היחיד. מצד שני, מדובר על יצירת למעלה מ-10 דפים. רק הרעיון לנהל את ה-redirect-ים האלה כבר עייף אותנו…
בשלב הזה היה ברור שעלי לשוטט שוב ברחבי הרשת ולחפש. בדיוק בתקופה הזו נפתח אתר השו”ת למתכנתים stackoverflow. אז הלכתי לשם לשאול . התשובות שם היו בעיקרן להשתמש בכלי חיצוני, ולא לעשות את זה מתוך הקוד. על אף שאנחנו חייבים שזה יהיה מתוך הקוד, החלטתי לבדוק מהי אחת מהתוכנות החיצוניות האלה – wget. הרצתי חיפוש ב-searchdotnet והתוצאה הראשונה היתה שאלה שהתאימה לי בדיוק: האם יש מחלקה דוטנטית המיישמת את את wget. התשובה היתה: webclient. מצוין. התחלתי לחקור את webclient. [בשלב הזה התברר לי שיש שם לפעולה שאנחנו מנסים לעשות. אנחנו קראנו לזה ייצוא ל-HTML, אבל מתברר שקוראים לזה screen scraping.] במחלקת webclient ישנה מתודה שנקראת downloadString, המקבלת URL, ומחזירה מחרוזת של ה-HTML שלו. מצוין! בדיוק מה שאנחנו צריכים. הרצתי את זה אצלנו, ונתקלתי בבעיה: בגלל שהמערכת שלנו מצריכה לוג-אין, כל הזמן קיבלתי בחזרה את המחרוזת של דף הלוג-אין. ניסיתי להכנס קודם לדף הלוג-אין עם פרטי הלוג-אין שלי, ורק אז לדפים שאני צריכה, אבל זה לא עזר. עדיין קיבלתי את דף הלוג-אין. שוב הלכתי לאתר השו”ת החביב ושאלתי שם איך להתקדם. אבל אף אחד לא ידע להסביר לי. אז חזרתי ל-searchdotnet וחיפשתי שם webclient. ואז הגעתי סוף סוף למאמר שהביא לי את הפתרון ב-3 שורות קוד. שם נאמר שאם דף ה-ASPX שאנחנו צריכים נמצא בתוך הפרוייקט שלנו, אז אפשר להשתמש במתודה שנקראת Server.Execute. וואלה. כמה פשוט.
ל-Server.Execute יש כמה overload-ים. בהתחלה ניסינו את זאת שמקבלת רק URL. הרי מבחינתנו, מספיק לגשת ל-URL הזה בלי לקבל חזרה את ה-HTML שלו, כי כל עבודת הייצוא ל-HTML נעשית בדף עצמו. אבל זה התברר כלא מוצלח מפני שכשהמתודה הזאת מקבלת רק URL, היא מחליטה לצייר את הדף ממש (כלומר, אחרי שלחצתי על שיגור בטופס יצירת האתר, קיבלתי את הטופס בחזרה עם 13 דפי HTML מצויירים בו…) וזה ממש לא מה שרצינו.
אז השתמשתי ב-overload שהודגם במאמר: שמקבל גם TextWriter. כך כל ה-HTML נכנס ל-TextWriter, והדף לא מצטייר. בעקבות כך החלטנו להעביר את כל הטיפול בייצוא מהדף למתודה שקוראת לדף. יש בזה גם הגיון מבחינה תחזוקתית, שאין צורך לשכפל את ה-Override של Render בכל דף חדש שיוצרים.
חוץ מזה, ישנו פרמטר נוסף שאפשר להעביר: preserveForm. הפרמטר הזה מציין האם לשלוח את ערכי ה-form וה-request כשניגשים לדף. ברירת המחדל היא true, ואני השתמשתי ב-overload הזה כדי להעביר false מפני שאחרת הקריאה העבירה את ערכי הטופס של יצירת אתר, וזה שיבש את הפעולה של דף ה-ASPX עצמו.
בסופו של דבר זה האופן שבו אנו יוצרים דפי HTML מהמערכת שלנו – דרך מאד פשוטה ונחמדה, אחרי שמוצאים אותה כמובן 🙂