شرح وتجريب تابع التعديل Edit Method

في هذه التدوينة سنقوم بشرح توابع Edit وال views التابعة ل Book controller، بحيث نستعرض مجمل الخصائص وكيفية التعامل معها، خصوصاً التوجيه والمساعدات Helper وغيرها من الخواص الأساسية.

هذه المقالة جزء من سلسلة لتعلم أساسيات ASP.NET MVC للمبتدئين:

أولاً علينا بعمل تعديل صغير لجعل حقل PublishDate يعرض فقط التاريخ. قم بفتح الملف Models\Book.cs وقم بإضافة الكود الملون بالأصفر كما يلي:

using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Data.Entity;
using System.Linq;
using System.Web;

namespace ArabicArchive.Models
{
    public class Book
    {
      public int Id { set; get; }
      public  string Title { set; get; }
      public  string Description { set; get; }
        [Display(Name = "Publish Date")]
        [DataType(DataType.Date)]
        [DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)]
        public  DateTime PublishDate { set; get; }
     public   string Author { set; get; }
     public   int NumberOfPages { set; get; }
     public   double GoodreadsRate { set; get; }
     public   int TypeId { set; get; }
     public virtual Type Type { set; get; }
    }

يمكن أيضاً وضع أي صيغة نريدها للتاريخ على سبيل المثال:

[Display(Name = "Publish Date")]
        [DataType(DataType.Date)]
        [DisplayFormat(DataFormatString = "{0:d}", ApplyFormatInEditMode = true)]
        public  DateTime PublishDate { set; get; }

 إن خاصية  Display تحدد الاسم الذي سيظهر بدلاً من اسم الحقل، في مثالنا سيتم عرض Publish Date بدلاً من PublishDate. إن خاصية DataType تحدد نوع البيانات ، في مثالنا هو date حيث أن معلومات الوقت المخزنة في الحقل لن تظهر.

إن خاصية DisplayFormat مطلوبة من أجل عدم الحصول على خطأ في متصفح Chrome الذي يقوم بعرض تنسيقات التاريخ بشكل غير صحيح.

قم بتشغل التطبيق وانتقل الى المتحكم Books. ثم قم بإيقاف مؤشر الفأرة فوق الرابط Edit لرؤية الرابط الذي يشير إليه:

الرابط Edit تم توليده من قبل التابع Html.ActionLink الموجود في ملف Views\Books\Index.cshtml

  @Html.ActionLink("Edit", "Edit", new { id=item.Id })

إن Html object هو عبارة عن مساعد وهو عبارة عن خاصية في  System.Web.Mvc.WebViewPage class

التابع ActionLink هو عبارة تابع يبسط عملية التوليد الديناميكي لـ HTML hyperlinks التي تشير الى action methods في المتحكمات. حيث أن أول بارمتر لهذا التابع هو عبارة عن نص الرابط الذي سيعرض على سبيل المثال   (<a>Edit Me</a>)

البارمتر الثاني للتابع هو عبارة عن اسم action method التي سيتم استدعاؤها، أما البارمتر الأخير للتابع فهو عبارة عن anonymous object الذي يولد بيانات التوجيه في مثالنا Id=3.

الرابط المولد معروض في الصورة السابقة هو http://localhost:17446/Books/Edit/3

إن التوجيه الافتراضي (موجود في الملف App_Start\RouteConfig.cs)  يأخذ الشكل {controller}/{action}/{id}

لذلك فإن ASP.NET تترجم http://localhost:17446/Books/Edit/3 الى طلب ل  Edit action للمتحكم Books

مع قيمة للبارمتر Id تساوي 3.

قم بفتح الملف App_Start\RouteConfig.cs ولاحظ الكود التالي.

إن التابع MapRoute يستخدم لتوجيه طلبات HTTP الى المتحكم  و action method المناسبين  بالإضافة إلى تمرير قيمة البارمتر الاختياري Id الى action method. كما أن التابع MapRoute يستخدم أيضاً من قبل HtmlHelpers (على سبيل المثال تابع ActionLink  ) لتوليد URLs التي تحدد المتحكم و action method بالإضافة لبيانات التوجيه.

 public static void RegisterRoutes(RouteCollection routes)
        {
            routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

            routes.MapRoute(
                name: "Default",
                url: "{controller}/{action}/{id}",
                defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
            );
            routes.MapRoute(
          name: "Hello",
          url: "{controller}/{action}/{name}/{id}"
           );
        }

يمكن أيضاً تمرير بارمترات  action method باستخدام  query string على سبيل المثال فإن الرابط التالي: http://localhost:17446/Books/Edit?Id=3 أيضاً يمرر البارمتر Id بقيمة 3 ل Edit action في المتحكم Books

عند النقر على الرابط Edit نلاحظ وجود حقل TypeId  هو عبارة عن قائمة منسدلة تحتوي على جميع أنواع الكتب التي قمنا بإضافتها حيث أننا نقوم عن طريق هذه اللائحة باختيار النوع و يتم أخذ قيمة الخاصية TypeId للعنصر الذي تم اختياره من اللائحة ليكون TypeId للكتاب الذي نقوم بتعديل بياناته:

سنقوم بتغيير اسم الحقل من TypeId إلى Type دون تغيير اي شيء في طريقة العمل الموضحة سابقا لاختيار النوع للكتاب

 كما فعلنا في الدرس السابق عندما قمنا بتعديل اسم الحقل من TypeId إلى Type في الملف Index.cshtml

قم بالذهاب الى الملف Views\Books\Edit.cshtml وقم بإيجاد ال TypeId label كما هو موضح بالصورة التالية:

قم بتغيير النص الذي يظهر في ال label من “TypeId” ليصبح “Type” كما هو موضح بالصورة التالية:

إن هذه الطريقة تؤدي الى تغيير اسم الحقل من TypeId إلى Type في ملف Edit.cshtml فقط.

قم بتشغيل التطبيق وانتقل الى الرابط http://localhost:17446/Books/Edit/3 فنجد أن اسم الحقل تحول الى Type

قم بفتح المتحكم Books نلاحظ وجود تابعيين Edit action كما هو موضح

// GET: Books/Edit/5
        public ActionResult Edit(int? id)
        {
            if (id == null)
            {
                return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
            }
            Book book = db.Books.Find(id);
            if (book == null)
            {
                return HttpNotFound();
            }
            ViewBag.TypeId = new SelectList(db.Types, "Id", "Title", book.TypeId);
            return View(book);
        }

        // POST: Books/Edit/5
        // To protect from overposting attacks, please enable the specific properties you want to bind to, for 
        // more details see http://go.microsoft.com/fwlink/?LinkId=317598.
        [HttpPost]
        [ValidateAntiForgeryToken]
        public ActionResult Edit([Bind(Include = "Id,Title,Description,PublishDate,Author,NumberOfPages,GoodreadsRate,TypeId")] Book book)
        {
            if (ModelState.IsValid)
            {
                db.Entry(book).State = EntityState.Modified;
                db.SaveChanges();
                return RedirectToAction("Index");
            }
            ViewBag.TypeId = new SelectList(db.Types, "Id", "Title", book.TypeId);
            return View(book);
        }

نلاحظ أن تابع Edit الثاني مسبوق بالخاصية HttpPost وهذه الخاصية تحدد أن بأن هذا التابع يستدعى فقط من أجل طلبات Post. يمكن أيضا تطبيق الخاصية  HttpGet للتابع Edit  الأول ولكن هذا ليس ضروري لأن هذه الخاصية مطبقة بشكل افتراضي على التابع Edit  الأول.

ملاحظة:سنقوم بالإشارة لجميع توابع action المطبق عليها الخاصية  HttpGet بشكل افتراضي ب توابع HttpGet.

الخاصية Bind هي آلية حماية مهمة لمنع الهاكرز من إرسال بيانات الى ال Model(هجمات overposting). يجب دائما أن تضمين الخصائص التي نريد تعديلها في الخاصية Bind.

إن الخاصية ValidateAntiForgeryToken تستخدم لمنع الطلبات المزورة وهذه الخاصية مقترنة مع ()Html.AntiForgeryToken@ في ملف Views\Books\Edit.cshtml 

 

إن ()Html.AntiForgeryToken@ يولد رمز لكشف التزوير وهذا الرمز يجب أن يطابق التابع Edit للمتحكم Books.

إن التابع HttpGet Edit يأخذ بارمتر واحد وهو Id الكتاب حيث يتم البحث عن الكتاب باستخدام التابع Find ويعيد الكتاب ل Edit view إذا لم يتم إيجاد الكتاب يتم إرجاع HttpNotFound.

عندما يتم توليد Edit view فإنه يقوم بذلك بالاعتماد على  Book class  حيث يقوم بإضافة كود لإظهار عناصر  

الكود التالي هو ل Edit view الذي تم توليده:


 

نلاحظ وجود العبارة model ArabicArchive.Models.Book@ في بداية الملف، وهذه العبارة تحدد أن ال view تقبل ال model من النوع Book. نلاحظ أيضاً أنه في الكود يتم استخدام العديد من توابع helper لتسهيل عملية كتابة كود HTML. مثال:

  • التابع Html.LabelFor الذي يعرض اسم الحقل (مثال “Title”).
  • التابع Html.EditorFor يعرض عنصر  <HTML <input في الصفحة.
  • التابع Html.ValidationMessageFor  يعرض رسائل التحقق المرتبطة بالخاصية.

 

قم بتشغيل التطبيق و انتقل الى الرابط Books/  قم بالنقر على الرابط Edit لأي كتاب. ثم في المتصفح قم بعرض كود الصفحة Page Source. الكود التالي هو  كود Html لعنصر form:


إن الخاصية action للعنصر form موضوعة بالقيمة post والرابط الذي سيعالج هذا الطلب Books/Edit. وإن بيانات ال form  سيتم إرسالها إلى السيرفر عندما يتم الضغط على زر Save.

السطر الثاني يعرض رمز XSRF المولد من قبل استدعاء  التابع  ()Html.AntiForgeryToken@

معالجة طلب Post

الكود التالي يعرض تابع  HttpPost Edit:

// POST: Books/Edit/5
        // To protect from overposting attacks, please enable the specific properties you want to bind to, for 
        // more details see http://go.microsoft.com/fwlink/?LinkId=317598.
        [HttpPost]
        [ValidateAntiForgeryToken]
        public ActionResult Edit([Bind(Include = "Id,Title,Description,PublishDate,Author,NumberOfPages,GoodreadsRate,TypeId")] Book book)
        {
            if (ModelState.IsValid)
            {
                db.Entry(book).State = EntityState.Modified;
                db.SaveChanges();
                return RedirectToAction("Index");
            }
            ViewBag.TypeId = new SelectList(db.Types, "Id", "Title", book.TypeId);
            return View(book);
        }

تقوم الخاصية ValidateAntiForgeryToken بالتحقق من رمز  XSRF المولد من قبل استدعاء، ()Html.AntiForgeryToken@ في ال view. ثم يقوم ASP.NET MVC model binder بأخذ قيم ال form المرر ويضيف Book object الذي يمرر كبارامتر للتابع.  بعدها يقوم التابع ModelState.IsValid بالتحقق من أن البيانات المقدمة من ال form يمكن استخدامها لتعديل Book object.

فإذا كانت البيانات صالحة( صحيحة) فإن بيانات الكتاب يتم حفظها  في لائحة  Books لل   db object (هو عبارة عن object من النوع BookDBContext للتعامل مع قاعدة البيانات).

بعد ذلك يتم حفظ التعديلات التي أجريناها على بيانات الكتاب في قاعدة البيانات باستدعاء التابع SaveChanges لـ db object. بعد حفظ البيانات يتم إعادة توجيه المستخدم إلى التابع Index في المتحكم BooksController حيث يتم عرض لائحة بجميع الكتب الموجودة في قاعدة البيانات بما في ذلك التعديلات التي قمنا بها.

في حال قام المستخدم بإدخال قيم غير صالحة في أحد الحقول أو مجموعة من الحقول فإن client side validation  يقوم بعرض رسالة خطأ اسفل الحقل الذي تم فيه ادخال قيم غير صالحة. في حال تعطيل JavaScript فإن client side validation لن تعمل ولكن المخدم سيحدد  بأن القيم المررة غير صالحة حيث يتم إعادة عرض ال form مع رسائل خطأ. لاحقاً سنتكلم عن التحقق بشكل مفصل عن ذلك.

إن Html.ValidationMessageFor helpers في  Edit.cshtml view تقوم بعرض رسائل الخطأ المناسبة:

جميع توابع HttpGet تتبع نفس النمط حيث تقوم بجلب book object (أو لائحة من الobjects في حالة تابع Index) حيث يتم تمرير ال model إلى ال view.

إن جميع التوابع التي تضيف أو تعدل أو تحذف أو تقوم بأي عملية تعديل على البيانات فإنها تقوم بذلك من خلال تابع التحميل الزائد HttpPost overload.

تعديل البيانات في تابع  HTTP GET هو خطر أمني أيضا تعديل البيانات في تابع HTTP GET يخالف ماهو مستحسن في HTTP و يخالف معمارية REST والتي تحدد أن طلبات GET يجب ألا تعدل حالة التطبيق.

بمعنى آخر تنفيذ عملية GET يجب أن تكون آمنة بحيث لاتملك أي تأثيرات جانبية ولاتعدل البيانات الموجودة.

في الدرس التالي سوف نقوم بتطبيق عملية البحث

تغذية راجعة

اتمنى عدم التردد في الاستفسار عن أي مفهوم تم ذكره في هذا التدوينة، وأرجو تجربة ماورد في التدوينة بشكل فعلي وعدم الاكتفاء بالقراءة لتحقيق أكبر فائدة ممكنة، وكالعادة سأكون سعيد إن شاركتموني تجربتكم وتصويباتكم في حال وجود أخطاء كتابية.

 

هذه المقالة مستندة إلى سلسلة دروس مايكروسوفت الرسمية للـ ASP.NET MVC، وذلك لترتيب الدروس المناسب واعتقادي بسلاستها وأهمية نقلها إلى العربية بأسلوب مناسب وتجربة تتوافق مع الأدوات المتاحة لنا والمتوفرة في منطقتنا.

مدير تقني وشريك مؤسس لـ فسيلة تِك، مبرمج متعدد المهارات، مهتم في إنجاز أمور استثنائية في مجال التكنولوجيا وأعمل جاهداً لترك أثر إيجابي في الحياة