Tags: , | Categories: MVC Posted by talgiladi on 2/27/2009 1:33 PM | Comments (0)
Technorati Tags: ,

כתבתי בעבר על איך ליצור captcha control פשוט, וגם כתבתי אחד קצת יותר מתקדם,

אבל עכשיו כשאני בתהליכי מעבר ל mvc הקונטרולים הקודמים לא כל כך מועילים, שהרי הם משתמשים ב webforms.

אז כמובן שאי אפשר פשוט לזרוק כאן קונטרול על הדף וזה יעבוד, זה חלק מהמחיר שאנו משלמים עם mvc,

אז נצטרך לכתוב כמה שורות קוד, לא יותר מדי…

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

    קודם כל יש הקוד המקורי שיוצר את התמונה, זו מחלקה אחת לא גדולה בשם CaptchaImage

  2. אחר כך יצרתי מחלקה סטטית בשם CaptchaManager שתעטוף את הפונקציונליות בארבע פונקציות:

    א. GetCaptchaString – מייצר טקסט רנדומלי ושומר אותו בסשן להשוואה.

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

    ג. RenderImage מייצרת את התמונה עבור הקונטרולר שאמור לשלוח אותה לדפדפן.

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

     

    public class CaptchaManager

    {

        public static string GetCaptchaString()

        {

            string result;

            if (HttpContext.Current.Session["captchaText"] != null)

            {

                result = HttpContext.Current.Session["captchaText"] as string;

            }

            else

            {

                result = Guid.NewGuid().ToString("N").Substring(0, CaptchaImage.TextLength);

                HttpContext.Current.Session["captchaText"] = result;

            }

            return result;

        }

     

        public static void ResetCaptcha()

        {

            HttpContext.Current.Session["captchaText"] = null;

        }

     

        public static Bitmap RenderImage()

        {

            CaptchaImage ci = new CaptchaImage()

            {

                Text = GetCaptchaString()

            };

            return ci.RenderImage();

        }

     

        public static bool IsValid

        {

            get

            {

                string request = HttpContext.Current.Request["captchaText"];

                string user = HttpContext.Current.Session["captchaText"] as string;

                return request == user;

            }

        }

    }

  3. יצרתי מחלקה שתשמש כ ActionFilter , למרות שאתם יכולים כמובן לדלג עליה ולדחוף את הקוד שבה ישירות לקונטרולר שבו אתם רוצים את הבדיקה.

    הדבר היחיד שאנו עושים כאן זה לתפוס את הבקשה לפני שהיא מגיעה ל action, ואם ה captcha לא תקין, להחזיר הודעת שגיאה לדפדפן. במקרה שלי זה הודעה בפורמט JSON לפניה מאג'קס, אבל כמובן שהתוצאה יכולה להיות כל ActionResult אחר.

      public class ValidateCaptchaAttribute : ActionFilterAttribute

        {

            public override void OnActionExecuting(ActionExecutingContext filterContext)

            {

                    if (!CaptchaManager.IsValid)

                    {

                        filterContext.Result = new JsonResult() { Data = new { error = "true" } };

     

                        return;

                    }

                base.OnActionExecuting(filterContext);

            }

        }

  4. מה נשאר לנו? נשאר לכתוב את קוד ה Html ולשלוח את התמונה פיזית לדף. לצורך יצירת התמונה ושליחתה לדף, יצרתי קונטרולר נוסף שזה כל הקוד שלו:

     public class CaptchaController : Controller

        {

            public FileResult Index()

            {

                using (Bitmap b = CaptchaManager.RenderImage())

                {

                    var ms = new MemoryStream();

                    b.Save(ms, ImageFormat.Gif);

                    return File(ms.ToArray(), "image/png");

                }

            }

     

            public JsonResult Render()

            {

                CaptchaManager.ResetCaptcha();

                return Json(new { success = "true" });

            }

        }

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

    א. פונה לשרת כדי לאפס את הטקס (זו הפעולה שאנו רואים פה Render)

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

  5. בואו נראה את קוד ה HTML

    <table>

    <tr>
                   <td>
                       <img src="<%=Url.Action("Index", "Captcha") %>" alt="captcha"  name="captchaImages" /><a href="javascript:void(0)" onclick="renderCaptcha();return false;">a</a>
                   </td>
                   <td>
                       <input type="text" id="txtReplyCaptchaText" name="captchaText"/>
                       <span>Enter the text in the image</span>
                   </td>
               </tr>

    </table>

    6. ונשאר רק פונקציה ב javascript למי שרוצה אפשרות לרנדר מחדש את התמונה

    <script type="text/javascript">
        <!--
         function renderCaptcha() {
             $.ajax({
                 type: 'post',
                 url: '<%=Url.Action("Render", "Captcha") %>',
                 success: function(j) {
                     var src = '<%=Url.Action("Index", "Captcha") %>?r=' + Math.random();
                   document.getElementById('captchaImage').src=src;
                 }, error: function(err) {
                     alert('Error');
                 }
             })
         }
        -->
    </script>

     

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

    שימו לב ששם תיבת הטקסט הוא קבוע “captchaText” , כדי שהפילטר שלנו  ידע מה לחפש

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

להורדת פרוייקט דוגמה

Currently rated 4.0 by 1 people

  • Currently 4/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5
Tags: | Posted by talgiladi on 2/15/2009 2:38 PM | Comments (0)

אם לקוח גדול היה מסכים לקנות את האפליקציה רק בתנאי שחלק מהמודולים יעבוד עם דוט נט 2.0 כי הוא לא יכול להתקין את גירסה 3.5?

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

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

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

אחר כך חשבתי פשוט שהמודולים האלה יפנו ישירות ל repository, שוב, אני יודע שזה לא נכון, אבל אין ברירה, וחוץ מזה אין בהם כל הרבה business logic שאני צריך את כל הפניות לשיכבת  ה services, ואכן התחלתי לבצע, עד שהבנתי שלא רק שאני אצטרך לשכפל קוד (!!!) אלא שה IOC שלי שנמצא בשכבת ה services יצטרך להשתכפל גם!! ואיפה אני אשים אותו?

בסוף אמרתי, גם ככה אני אצטרך לעשות עבודה של חמור, אז כנראה שהכי טוב זה פשוט לגרום ל services לעבוד עם גירסה 2... וככה עשיתי, התחלתי מה Linq היקרים שהפכו לשורות קוד שאני כתבתי, וכלה ב extension methods שפשוט הפכתי למתודות סטטיות ועל שאר הדברים הקטנים פשוט ויתרתי... 

Technorati Tags:

 

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5