יום חמישי, 26 באפריל 2012

שימוש נכון ב-Handler


שימוש נכון ב-handler

כאשר אנחנו רוצים להעביר מידע או להפעיל פקודה מה-client ל-server, נעשה זאת בשיטת ajax. (העדיפות היישומית שלי היא jQuery)

נניח שאנחנו רוצים לייצור משתמש חדש לאתר.
יש לנו את הפקדים בדף ופונקצייה רגילה register() ב-js שמתוכה אנחנו קוראים ל-service שיטפל בבקשה של יצירת משתמש.

עכשיו צריך לייצור את ה-service – הבעיה היא במה נבחר?

WCF vs HttpHandler vs WebService.

WCF– הדרך הכי טובה לקריאה מ-client ל-server. החיסרון שלה בעיקר שלא כולם יודעים איך להשתמש בזה. ולכן לא נעסוק בנושא הזה.
חיסרון נוסף זה בשימוש ב-NHibernate בגלל ה-session (שגם לזה יש פיתרון אבל כמו שציינתי לא הרבה מכירים את זה).


HttpHandler vs WebService

ב-WebService יש מתודה שמוגדרת ע"י אטריביוט [WebMethod()]
לכן הפנייה אליה תהיה: MyWebService.asmx/Register

אבל ידוע שרמת העיבוד של HttpHandler הרבה יותר מהירה מאשר WebService. (הסיבה לכך אני אסביר בפעם אחרת)
נוחות השימוש ב-HttpHandler כרגע הרבה יותר מסובכת!

ב-Handler אנחנו מקבלים QueryStrings למשל כזה:


עכשיו אנחנו נצטרך לנתח את הפנייה באמצעות האובייקט QueryStrings ולעשות switch לפי הפנייה לפונקצייה המבוקשת.

נצטרך גם לכתוב פונקצייה לכל בקשה ברמת ה-handler
לנתח את הפרמטרים של ה-QueryStrings

ואולי אף להפנות משם לפונקציות שהם ברמת ה-BL.

למשל:


        public void ProcessRequest(HttpContext context)
        {
            string request = context.Request.QueryString["request"] as String;
            context.Response.ContentType = "text/json";
            context.Response.StatusCode = 200;
            try
            {
                switch (request)
                {
case "register": context.Response.Write(Register(context)); break;
                }
            }
            catch (Exception ex)
            {
                context.Response.StatusCode = 500;
                context.Response.Write(ex.Message);
            }
        }

 private string Register(HttpContext context)
        {
            string username = context.Request.QueryString["username"] as String;
            string email = context.Request.QueryString["email"] as String;
            string reemail = context.Request.QueryString["reemail"] as String;
            string password = context.Request.QueryString["password"] as String;

            var status = new HandleUser().CreateUser(username, email, reemail, password);

            if (!status.Equals( MembershipCreateStatus.Success) )
            {
                context.Response.StatusCode = (int)status;
                return new JavaScriptSerializer().Serialize(status.ToString());
            }

            return new JavaScriptSerializer().Serialize("checkemail.aspx?e=" + email);
        }


הקוד הנ"ל הוא לא ג'נרי לא אחיד ואין הפרדה של קוד. במקרה של שינוי יש יותר ממקום אחד לטפל בו.


לכן כתבתי פיתרון ג'נרי כזה:

הגישה: קריאה ל-handler תעשה כמו קריאה לפונקצייה.

הנה דוגמא למימוש:



  public class SimpleHandler : IHttpHandler
    {
        public const string request = "request";

        public void ProcessRequest(HttpContext context)
        {
            context.Response.ContentType = "text/plain";
          
            NameValueCollection qs = new NameValueCollection(context.Request.QueryString);
            string methodname = qs[request];
            qs.Remove(request);

            object[] objs = new object[qs.Count];
            qs.CopyTo(objs, 0);

            new Services()
                .GetType()
                .InvokeMember(methodname,
                              BindingFlags.InvokeMethod | BindingFlags.Static | BindingFlags.Public,
                              null,
                              null,
                              objs);

        }

        public bool IsReusable
        {
            get
            {
                return false;
            }
        }
    }


מחלקה שמטפלת בפונקציות של ה-services.


public class Services
{
    public static void Register(string username, string email, string reemail, string password)
    {
          HttpContext.Current.Response.Write("in register! username: " + username);
    }
}


הפונקצייה של js לקריאת ה-handler:


function register(em) {

    var $register = $(em).closest('#register');
    var request = "request=register&" + $register.find('> *').serialize();

    // request == request=Register&username=shlomi&email=wizardnet972%40gmail.com&reemail=wizardnet972%40gmail.com&password=123

    $.getJSON('/ SimpleHandler.ashx', request, function (data, textStatus, xhr) {
        debugger;
        //location = data;
    })
    .error(function (xhr) {

        alert(xhr.status + ": " + xhr.responseText);

    });

    return false;
}


סיכום:
1.      קוד ברור, ג'נרי ויעיל וה-session קיים לאורך כל התוכנית.
2.      הפרדה ברורה של הקוד. (ORM)
3.      עפ"י המימוש הזה, אין צורך לדאוג לכך לנתב את הבקשות לפונקצייה, מה שצריך לעשות זה להוסיף פונקצייה ב-service וזה מוכן.
4.      הקריאה ל-handler תתבצע כמו קריאה לפונקצייה, ואם אחד הפרמטרים לא תואם נקבל exception מתאים.
5.      אין צורך להתעסק ב-handler עצמו, רק במחלקת ה- Servicesבלבד.
6.      רמת הסיכון של הקוד שואפת לאפס, אין חשיפת קוד בכלל.

--
זה פוסט ראשון שלי.
קצת עליי - זה יהיה כבר בפוסט הבא.

לכל שאלה ניתן לפנות בתגובה או באמצעות המייל:
wizardnet972@gmail.com
אז מה תגובתכם על המאמר הזה?
 

אין תגובות:

הוסף רשומת תגובה