مقدمة – برتوكول الـ HTTP – عمل صفحة الـ ASP.NET:
جميعنا نستخدم الشبكة العنكبوتية (الإنترنت) في جُل معاملاتنا اليومية؛ الرسمية أو الترفيهية، نقوم بالدخول على المواقع الإلكترونية وننتظر نتائج ذلك الدخول، نقوم بحفظ المعلومات واستجلابها عن طريق عناوين الوصول URL التي نقوم بكتابتها في مربع بحث الروابط في المتصفحات… كل تلك العمليات تتم عن طريق بروتوكول يُدعى HTTP وهو اختصار لـ Hypertext Transfer Protocol ومهمة هذا البروتوكول جميع ماذكرته آنفا من استدعاء للبيانات وتوزيعها حسب النطاق المطلوبة فيه تلك البيانات.
برتوكول الـ HTTP يعمل من خلال خاصيتين وهما الطلب والإستجابة Request – Response وعمليتا الطلب والإستجابة دائما ما تتم بين جهاز المستخدم وجهاز الخادم (المزوّد – سيرفر) حيث يقوم المستخدم من خلال المتصفح الذي مثبّت على جهازه بطلب صفحة معينة – هنا تسمى العملية تلك عملية الطلب Request ، عملية الطلب ترسل لجهاز الخادم (مزوّد – سيرفر) يقوم جهاز الخادم بعدها بالإستجابة للطلب وإرساله Response للمستخدم. أيضا البرتوكول HTTP يستخدم في خدمات الويب او ما تعرف بالإنكليزية Web Services.
حتى أكون أكثر وضوحا معك عزيزي القارئ (المطور)، في هذا العرض لن أتطرق لكيفية عمل الويب من خلال برتوكول الـ HTTP وكما ترى لقد قمت بشرح سريع جدا وموجز في المقدمة لكيفية عمل هذا البرتوكول، ولأكون أكثر وضوحا معك موضوع المقالة هذا (WEB API) يحتاج منك ان تكون ملمّاً بمعرفة هذا البرتوكول HTTP، لكن لا تقلق سأقوم بإرفاق رابط لك هنا يشرح مفهوم برتوكول الـ HTTP بالتفصيل مصحوبا بعدد من المواضيع المرتبطة بهذا البرتوكول والشرح باللغة العربية … سيقوم الرابط بتوجيهك لموقعنا هذا (انوفورماتيك) حيث من قام بكتابة المقال وجدي عصام، بإمكانك قراءة المقال من هنا
طريقة عمل صفحة الـ ASP.NET
تقنية الـ WEB API تعمل مع تقنية الـ ASP.NET حيث قامت مايكروسوفت بإصدارها مع نسخة الفيجوال ستديو 2012 وجميع النسخ التي تلت تلك النسخة وحتى يتسنى لنا وضع الـ WEB API في سياقه لابد لنا أن نشرح طريقة عمل صفحة الـ ASP.NET.
عندما تقوم بكتابة الرابط www. informatic-ar.com في متصفحك يقوم المتصفح بعمل طلب لتلك الصفحة من الخادم (مزوّد – سيرفر) وتتم تلك العملية عن طريق برتوكول الـ HTTP حيث ينشأ إتصال بين جهاز المستخدم Web-Client وجهاز الخادم Web-Server وفي حال كان الخادم يعمل، سيقوم بعمل ارسال Response للصفحة المطلوبة للمتصفح الذي تمت من خلاله عملية الطلب، وإرجاع تلك البيانات دائما ما يكون على صيغتي الـ JSON أو الـ XML. وكما واضح ان موقعنا informatic-ar.com يعمل على خادم إستضافة Web-Server لتتاح عملية الوصول إليه من جميع إنحاء العالم.
مشروع الـ ASP.NET دائما ما يتكون من ملفات الموقع نفسه وصور ومستندات وملفات ساكنة Static file تلك الملفات الساكنة دائما تكون موجودة في جهاز الخادم عند طلبها يقوم الخادم بإرسالها مباشرة للمستخدم (Request) أما إذا كانت الملفات ديناميكية سيقوم خادم الويب بعمل بعض العمليات عليها وخادم الويب هذا يعمل مع الـ ASP.NET Run-time والذي يتم تثبيته من خلال تثبيت مكتبة الـ .NET Framework وخادم الويب الذي اقصده هنا هو الـ IIS وهو الذي يتم عليه نشر جميع ملفات مشروع الـ ASP.NET.
الآن بعد ان عرفنا كيفية عمل صفحة الـ ASP.NET سننتقل لمشروعنا الذي سنقوم ببناءه وتهيئته واستدعاءه من خلال الـ WEB API وعمل اختبار لشفراته التي سنكتبها.
دائما ما أقوم بعمل إختبار شفرات مشاريعي التي أكون مسؤول عنها سواء صغيرة أو كبيرة ومن خلال خبرتنا عزيزي القارئ لقد عملنا على مشاريع كبيرة دائما تُظهر مشاكل أثناء عمل نشر المشروع عند المستخدم فبالتالي كلما قمت بإختبار شفرات برمجياتك ستسهل عليك معرفة هيكلية برنامجك ودورة حياة تلك البرمجيات..
أيضا اختبار البرمجيات أثناء كتابة الشفرات يجنُبك تشغيل مشروعك على المتصفح كثيرا للتأكد من عمل الشفرة التي قمت بكتابتها هل هي تعمل بصورة كما الصورة المتوقعة ام لا؟ بالتالي كتابة شفرات الإختبار تختصر لك الزمن..وعند استدعاء مشروعك من خلال المتصفح لديك ستكون متأكدا من عمله بصورة صحيحة من مرة واحدة.
سنقوم ببناء مشروع من الصفر خطوة بخطوة لتسليم المواطنين جوازاتهم أو تجديدها حيث سيقوم المواطن بعمل طلب لتجديد أو أصدار جواز جديد…سيقوم المسؤول بحفظ بيانات المواطن…يقوم المواطن بالمراجعة للتأكد من إنتهاء إجراء الجواز ام لا..في حال تم الإنتهاء من الإجراء المطلوب سيقوم المسؤول بتحديث بسيط يوضح ان المواطن قام بإستلام جوازه.
هذا المشروع لقد قمت ببرمجته من قبل بإستخدام الـ ASP.NET MVC وتم نشره على موقعنا informatic-ar.com وبإمكانك مطالعته من الرابط التالي ان إردت
(وسيكون جميلا الإطلاع عليه لتفهم طريقة عمل الـ MVC لأن الـ WEB API شبيهة في عملها للـ MVC فقط سيكون الفرق في عملية الإستجابة Response من خلال السيرفر بالنسبة للـ WEB API حيث يقوم بإرجاع للبيانات بصيغتي الـ XML أو الـ JSON)
وفي مشروع الـ WEB API هذا لقد قمت بإضافة حقل جديد على قاعدة البيانات يوضح هل انتهى الجواز من الاصدار او التجديد من عدمه (IsProcessFinishing) عكس مشروع الـ ASP.NET MVC الموضح في الرابط السابق والذي كان لا توجد به تلك الخاصية، فقط كانت عملية التأكد تتم بإستخدام جملة منطقية في فئة التحكم PersonController بإستخدام جملة If المنطقية توضح هل المواطن استلم جوازه ام لا؟ وفي حالة الإستلام يقوم الـ ASP.NET Run-time بتوجيه المستخدمم لرابط التفاصيل (ستجد هذا التحديث في اول تعليق على المقالة المشار لرابطها في الأعلى).لكن هنا في مشروع الـ WEB API سنقوم بتحديد تلك العملية من قاعدة البيانات من خلال الحقل (IsProcessFinishing).
لماذا نستخدم الـ WEB API
إذا كنت من عشاق موقع امازون وتستخدم وسيط في الشراء والطلب من هذا الموقع ستسأل نفسك كيف قام الموقع الوسيط هذا بالشراء بدلا مني؟ امازون لديها خدمة تسمى AWS وهي اختصار لـ Amazon Web Services تقوم بإرجاع البيانات في عدة صيغ تستخدمها مواقع عديدة مثلا كموقع (sa.edfa3ly.co) والذي يتيح لك نسخ رابط المنتج من موقع اماوزن ووضعه في مربع نصي Textbox في موقعهم وبوضعك لرابط المنتج من امازون سيقوم موقع sa.edfa3ly.co بقراءة تفاصيل المنتج كاملة (بإمكانك تجربته).
الـ WEB API هي منصة تساعدك على إنشاء برمجيات بهذا الشكل … خذ مثالا آخر لدينا الرقم الوطني في السودان هذا الرقم قامت بإنشاءه وزارة الداخلية وهي المسؤولة عنه وإذا كان لديك انت المطور برنامج (برنامج لمؤسسة حكومية) يعتمد على الرقم الوطني ستقوم بمخاطبة المسؤولين بوزاة الداخلية لطلب رابط يتيح لك تزويدك بالرقم الوطني للمواطن واسمه وعمره وصورته ان أردت (كما في التقديم لحج هذا العام 2018) ويعتبر هذا الرابط كقناة Channel تقوم أنت بإدراجه في تطبيقك كتنفيذ Implementation لأي جزء في تطبيقك يحتاج التعامل مع بيانات الرقم الوطني. خذ مثالا أيضا البنوك لدينا تقوم بإستخدام الـ Web Services في حال أردت فتح حساب لا بد أن يكون لديك رقم وطني أو لابد أن يكون لديك جواز ساري المفعول فتطبيقات البنوك أصبحت مربوطة بقاعدة بيانات الرقم الوطني.
الآن نحن سنقوم بعمل تطبيق بإمكانه أن يكون كقناة Channel يمكن إستدعائه من تطبيقات أخرى حيث سنقوم بعمل استجابة Response للتطبيقات الأخرى ودعمهم بصيغة للبيانات المطلوبة في شكل JSON أو XML المشروع هنا بسيط جدا..(أي ليس بإمكانك بيعه بتلك الطريقة ! لكن سأحاول بقدر الإمكان أن أضع تلك التقينة في سياقها الصحيح من خلال هذا المشروع..لكن أن كنت تعمل على نطاق رسمي فستحتاج للكثير لتقوم بتطويره من حيث عدة أجزاء خصوصا من ناحيتي الأمان Security والأداء).
بناء مشروع ASP.NET WEB API
لقد قمت بإستخدام الفيجوال ستديو 2015 لكن بإمكانك العمل على الفيجوال ستديو 2013 أو فيجوال اكسبريس 2013 للويب…كما قمت بإستخدام الـ SQL Server Express LocalDB والتي تتيح لي العمل داخل بيئة الفيجوال ستديو نفسه… الآن بإمكاننا أن نبدأ..
قم بفتح الفيجوال ستديو 2015 قم بإنشاء مشروع جديد من على يسار الشاشة التي ستفتح لك قم بالضغط على Other Project Solutions قم بإختيار Blank Solution وقم بتسميته DiscoverWebApi وحدد مسار حفظ المشروع على أي قرص صلب على جهازك كما موضح بالصورة التالية:
بعد ان يقوم الفيجوال ستديو بإنشاء الـ Solution سنقوم بإنشاء مكتبتين للفئات Class Library وسنستخدم لغة الـ C# .. قم بإنشاء مكتبة الفئات الأولى وأعطها الاسم DiscoverWebApi.Models وقم بإنشاء مكتبة الفئات الأخرى وأعطها الاسم DiscoverWebApi.Repository. سيقوم الفيجوال ستديو بإنشاء فئة بإسم Class1 قم بحذفها في المكتبتين. انظر للصورة في الاسفل لمعرفة كيفية انشاء مكتبة الفئات:
*** انتبه من ان تقوم بإختيار Class Library Portable فتلك مكتبة تستخدم لإنشاء الـ DLL الخاصة بك أولا وبإمكانك إستدعائها لأي نوع مشروع سواء Javascript – WCF – Win APP – ASP.NET لكن لا نريد تلك المكتبة فقط قم بإختيار الـ Class Library.. وإستخدامنا لتلك الطريقة في إنشاء المشروع هذا تعني اننا سنقوم بإستخدام أنماط التصميم Design patterns والنمط الذي سنستخدمه سيكون النمط المستودعي Repository Pattern.
بعد إنشائنا لمكتبات الفئات تلك سنقوم بإنشاء قاعدة البيانات والتي سنقوم بحفظ بيانات المواطنين عليها…
قم بفتح الـ Server Explorer لديك من خلال الفيجوال ستديو 2105 أو 2013 وقم بالضغط على شعار موصل الكهرباء والذي توجد به علامة الجمع (+) في فيجوال ستديو 2013 اما الفيجوال ستديو 2015 سيكون بالشكل التالي كما موضح بالصورة التالية:
قم بإختيار Microsoft SQL Server من شاشة Choose Data Source وقم بالضغط على Continue كما موضح بالصورة (إذا كنت تستخدم الـ SQL Server دائما قم بالتعليم على المربع الصغير اسفل الشاشة..وذلك سيكون إختيار افتراضي للفيجوال ستديو عند انشاء مشاريعك بدلا من فتح تل الشاشة كل مرة لعمل اتصال بين المشروع وقاعدة البيانات):
قم بكتابةlocaldb)\v11.0) في خانة اسم الخادم Server Name (إذا كنت تعمل على خادم مختلف قم بكتابته بدلا من الخادم الذي استخدمه) وقم بكتابة WebAPI_DB في خانة Select or enter a database name وقم بالضغط على OK سيقوم الفيجوال ستديو بتنبيهك بإنه لا توجد قاعد بيانات بإسم WebAPI_DB هل تريد إنشائها قل له نعم…سيقوم الفيجوال بإنشاء قاعد البيانات.
قم بالضغط بالزر الأيمن للمؤشر (ماوس) على قاعدة البيانات التي أنشئتها قبل قليل ومن القائمة المنسدلة التي ستظهر لك قم بإختيار New Query ستفتح لك صفحة خالية وقم بلصق الإستعلام التالي عليها:
CREATE TABLE [dbo].[AccountDetails] ( [PersonID] INT IDENTITY (1, 1) NOT NULL, [Name] NVARCHAR (50) NOT NULL, [NID] NVARCHAR (50) NOT NULL, [Received ] BIT NULL, [ReceivedDate] DATETIME NULL, [IsProcessFinishing] BIT DEFAULT ((0)) NOT NULL, PRIMARY KEY CLUSTERED ([PersonID] ASC) );
وكما موضح بالصورة التالية؛ قم بتحديد قاعدة البيانات التي أنشئتها قبل قليل وقم بالضغط على زر البدء…وتلك الجملة الإستعلامية تقوم بإنشاء جدول بإسم AccountDetails وعدد 6 حقول في هذا الجدول (رقم الحقل – اسم المواطن – الرقم الوطني للمواطن – تحديد استلام الجواز من عدمه – تاريخ استلام الجواز – وهل انتهى الجواز من الاصدار او التجديد من عدمه).
في نفس الصفحة التي قمت بفتحها في الخطوة السابقة قم بلصق تلك الجملة الإستعلامية الأخرى، وهي جملة تقوم بإنشاء بيانات داخل قاعدة البيانات التي أنشأناها:
SET IDENTITY_INSERT [dbo].[AccountDetails] ON INSERT INTO [dbo].[AccountDetails] ([PersonID], [Name], [NID], [Received ], [ReceivedDate], [IsProcessFinishing]) VALUES (1, N'Mohammed', N'123456', NULL, NULL, 0) INSERT INTO [dbo].[AccountDetails] ([PersonID], [Name], [NID], [Received ], [ReceivedDate], [IsProcessFinishing]) VALUES (2, N'Ahmed', N'1234567', NULL, NULL, 0) INSERT INTO [dbo].[AccountDetails] ([PersonID], [Name], [NID], [Received ], [ReceivedDate], [IsProcessFinishing]) VALUES (3, N'Omram', N'12345678', NULL, NULL, 0) INSERT INTO [dbo].[AccountDetails] ([PersonID], [Name], [NID], [Received ], [ReceivedDate], [IsProcessFinishing]) VALUES (4, N'Ghassan', N'123456789', NULL, NULL, 0) SET IDENTITY_INSERT [dbo].[AccountDetails] OFF
لقد أنشئت في البداية المشروع الآن قم بالضغط بالزر الأيمن للمؤشر (ماوس) على اسم الـ Solution لديك DiscoverWebApi وقم بإختيار Add ومن ثم New Project ستفتح معك نافذة قم بإختيار Web من على يسار الشاشة وقم بإختيار الـ ASP.NET WEB Application وقم بإعطاء المشروع إسم DiscoverWebApi.Web.API واضغط على OK ستفتح معك نافذة أخرى قم بإختيار الخيار Empty منها وأسفل الشاشة قم بالتعليم على منصة Web API ولا تنسى ان تحذف العلامة التي من على يمين الشاشة من الخيار Host in the cloud وقم بالضغط على OK كما موضح بالصورة التالية:
الأن سيكون شكل المشروع بالصورة التالية حيث لدينا مساحة عمل (Solution) بها مكتبتي فئات ومشروع WEB API (انظر للصورة التالية):
قم بالضغط بالزر الأيمن للمؤشر (ماوس) على مشروع DiscoverWebApi.Web.API ومن القائمة المنسدلة قم بإختيار Set as Startup Project وذلك يعني عند تشغيل المشروع على المتصفح سيكون هذا هو المشروع الرئيسي…
الآن سنقوم بربط الفئات ببعضها..قم بالضغط على مشروع DiscoverWebApi.Web.API بالزر الأيمن للمؤشر (ماوس) ومن القائمة المنسدلة قم بإختيار ADD ومنها ADD Reference ستفتح معك شاشة على يسارها مجموعة خيارات، يوجد خيار بإسم Solution قم بالضغط عليه، ستظهر معك مكتبات الفئات الموجودة بمساحة العمل، قم بالتعليم عليهم الأثنين واضغط OK. قم أيضا بعمل تلك العملية لمكتبة الفئات DiscoverWebApi.Repository وقم بربطها مع مشروع DiscoverWebApi.Models فقط.
لاحقا سنقوم بإنشاء مشروع Unit Testing لا تقلق..الآ تركيزنا في إنشاء أجزاء المشروع.
قم بفتح مشروع الـ DiscoverWebApi.Models وقم بإنشاء فئة (Class) وأعطها الإسم AccountDetail.cs وقم بلصق الشفرة التالية عليها وأحفظ المشروع :
using System.ComponentModel.DataAnnotations.Schema; using System.ComponentModel.DataAnnotations; [Table("AccountDetails")] public class AccountDetail { [Key] public Int32 PersonID { get; set; } public String Name { get; set; } public String NID { get; set; } public Boolean? Received { get; set; } public Nullable<DateTime> ReceivedDate { get; set; } public Boolean IsProcessFinishing { get; set; } }
هذه الفئة ستكون محرك العمل حيث سنقوم بتحديدها كأنها ستمثل الجدول الذي أنشأناه سابقا في قاعدة البيانات وإذا لاحظت أن خواص Properties هذه الفئة هي مثلها حقول الجدول AccountDetails الذي أنشأناه سابقا.
إذا لا حظت في جدول AccountDetails الذي لدينا في قاعدة البيانات توجد حقول تأخذ القيمة NULL كحقلي الـ Received و ReceivedDate وحتى أقوم بالتبين للفئة Person.cs بفراغ هاذين الحقلين قمت بإستخدام العلامة ؟ بالنسبة للحقل Received واستخدام كلمة <Nullable<T بالنسبة للحقل ReceivedDate وحرف الـ T يدل على نوع البيانات تلك وهي من النوع DateTime وستقرأ بالصورة التالية – Type Of DateTime، أيضا إذا لاحظت في تلك الفئة قمت بتعريف خواص كتحديد الخاصية PersonID بأنها حقل أساسي…وقمت بتحديد اسم الجدول أيضا.
أيضا إذا لا حظت قد قمت بإستخدام نوع البيانات الخاصة بالـ FCL مباشرة بدلا من التي توفرها الـ C# والتي تدعى primitive type وذلك لعدة اعتبارات منها على سبيل المثال اختلاف بعضها والتداخل الذي يوجد بين عدة لغات كـ C++ مثلا. فلو لاحظت لقد قمت بكتابة Int32 بدلا من int وهي في الاساس سيقوم الـ CLR بترجمتها لـ Int32 في حال قمت بكتابتها int (هذه تفاصيل ليست ضرورية لفهم الـ WEB API ولن تحتاجها فقط اكتبها بالطريقة التي تحبذها انت…لكن ان كنت محترفا وتعمل على مشاريع كبيرة بالتأكيد ستحتاج لمعرفة كيفية عمل الـ CLR).
نرجع لمشروعنا….(أهـ) …قم بتبيثت مكتبة الـ Entity Framewrok على جميع المشاريع الموجودة بالـ Solution لديك من خلال أداة الـ Nuget package Manager.
قم بإنشاء فئة أخرى داخل مشروع DiscoverWebApi.Models أعطها الاسم EFDbContext.cs وقم بلصق الشفرة التالية داخلها:
using System.Data.Entity; public class EFDbContext : DbContext { public DbSet<AccountDetail> AccountDetails { get; set; } }
هذه الفئة مهمتها أنشاء جدول وهمي Virtual table بإسم AccountDetails وهو نفس اسم الجدول الموجود بقاعدة البيانات و يساعدنا على تنفيذ الجمل الإستعلامية التي سنحتاجها للعمل مع قاعدة البيانات…حيث ترث هذه الفئة من فئة أخرى وهي DbContext وهذه الفئة بها جميع خصائص الحفظ والتعديل والمسح والقراءة التي نستخدمها في الـ SQL.
قم بإنشاء واجهة Interface بإسم IPersonRepository.cs داخل مكتبة الفئات DiscoverWebApi.Models وقم بلصق الشفرة التالية عليها:
public interface IPersonRepository { IQueryable<AccountDetail> GetAllPersons { get; } AccountDetail GetPerson(int id); void SavePerson(AccountDetail person); AccountDetail DeletePerson(int id); }
وبما أننا سنسخدم الـ Dependency Injection بالتالي قمت بإستخدام الواجهات والتي ستحقن في فئة أخرى ستقوم بإنشاءها في الخطوة التالية.
قم بإنشاء فئة جديدة بإسم PersonRepository.cs داخل مشروع الـ DiscoverWebApi.Repository وقم بلصق الشفرة التالية داخلها:
using System.Data.Entity; using DiscoverWebApi.Models; public class PersonRepository : IPersonRepository { private EFDbContext _context = new EFDbContext(); public IQueryable<AccountDetail> GetAllPersons { get { return _context.AccountDetails; } } public AccountDetail GetPerson(int id) { return _context.AccountDetails.FirstOrDefault(p => p.PersonID == id); } public void SavePerson(AccountDetail person) { if (person.PersonID == 0) { person.Received = false; person.ReceivedDate = null; person.IsProcessFinishing = false; _context.AccountDetails.Add(person); } else { AccountDetail entry = _context.AccountDetails.Find(person.PersonID); if (entry != null) { entry.Name = person.Name; entry.NID = person.NID; entry.Received = person.Received; entry.ReceivedDate = person.ReceivedDate; entry.IsProcessFinishing = person.IsProcessFinishing; } } _context.SaveChanges(); } public AccountDetail DeletePerson(int id) { AccountDetail person = _context.AccountDetails.Find(id); if (person != null) { _context.AccountDetails.Remove(person); _context.SaveChanges(); } return person; } }
في هذا المشروع سنستخدم الـ Dependency Injection وهي تقينة تتيح لمشروع الـ WEB API بإنشاء مثيل Instance لفئة الـ PersonRepository وذلك بدلا من إنشائها في كل طلب أثناء عمل التطبيق (Services) لأنها ستقلل من كفاءة الذاكرة وستحتاج الذاكرة لعملية الـ Dispose من خلال الـ GC والذي يقوم الـ CLR run-time بتنفيذه…بالتالي استخدام مكتبات الـ DI ستقلل من تلك العملية الطويلة وتحافظ على كفاءة النظام…في هذا المشروع سأقوم بإستخدام مكتبة الـ Unity.بإمكانك مراجعة هذا مقالي الذي كتبته في موقع انفورماتيك والذي يوضح مفهموم الـ DI بإستخدام مكتبة الـ Ninject من على الرابط التالي:
كيفية تهيئة مكتبة الـ Ninject للعمل مع مشروع الـ ASP.NET MVC
قم بتثبيت مكتبة الـ Unity.Web.Api من خلال الـ Nuget package Manager. كما موضح بالأسفل في الصورة التالية:
سيقوم الفيجوال ستديو بإنشاء فئة بإسم UnityConfig.cs داخل ملف App_Start قم بفتح تلك الفئة وقم بلصق الشفرة التالية عليها داخل الإسلوب RegisterComponents() :
using DiscoverWebApi.Models; using DiscoverWebApi.Repository; container.RegisterType<IPersonRepository, PersonRepository>();
قم بفتح ملف الـ Gloabal.asax وقم بلص السطر التالي داخل الإسلوب Application_Start() كالتالي:
using DiscoverWebApi.Web.API; UnityConfig.RegisterComponents();
الآن قم بفتح ملف الـ web.Config وسنقوم بتعريف جذع الإتصال مع قاعدة البيانات..قم بكتابة الشفرة التالية في سطر واحد:
<connectionStrings> <add name="EFDbContext" connectionString="Data Source=(localdb)\v11.0;Initial Catalog=WebAPI_DB;Integrated Security=True" providerName="System.Data.SqlClient" /> </connectionStrings>
لاحظ إذا كان اسم الخادم لديك يختلف فقم بإدخاله صحيحا.
الخطوة المهمة الآن هي إنشاء ملف فئة التحكم Controller ..قم بالضغط بالزر الأيمن للمؤشر (ماوس) على ملف الـ Controller وقم بإختيار Add ومن ثم Controller ستفتح معك شاشة قم بإختيار Web API2 Controller – Empty وقم بالضغط على OK ستفتح معك شاشة صغيرة أخرى قم بإعطاء تلك الفئة اسم PersonsController واضغط Ok…ثم قم بنسخ الشفرة التالية داخل تلك الفئة (سنشرح هذه الفئة بالتفصيل):
using System.Web.Http.Description; using DiscoverWebApi.Models; using DiscoverWebApi.Repository; public class PersonsController : ApiController { private IPersonRepository _repository; public PersonsController(IPersonRepository repository) { _repository = repository; } // GET: // http://localhost:3570/api/persons/getpersons public IEnumerable<AccountDetail> GetPersons() { return _repository.GetAllPersons.AsEnumerable(); } [ResponseType(typeof(Person))] public IHttpActionResult GetPerson(int id = 0) { AccountDetail person = _repository.GetPerson(id); if (person == null) { return NotFound(); } return Ok(person); } [ResponseType(typeof(AccountDetail))] public IHttpActionResult PostPerson(AccountDetail person) { if (!ModelState.IsValid) { // you can accept ModelState itself as message parameter. return BadRequest("Sorry There is somthing ERROR"); } _repository.SavePerson(person); return CreatedAtRoute("DefaultApi", new { id = person.PersonID }, person); } [ResponseType(typeof(AccountDetail))] public IHttpActionResult DeletePerson(int id) { AccountDetail person = _repository.DeletePerson(id); if(person == null) { return BadRequest(); } return Ok(person); } }
كل العمل يتركز على تلك الفئة..فما قمنا بفعله سابقا لا يعدو كونه إجراءات روتينية لإنشاء ملفات المشروع وربطها بقاعدة البيانات…أما الآن فتلك الفئة هي التي ستتحكم في المشروع بصورة كلية…أولا لاحظ أن تلك الفئة ترث من فئة اسمها APIController عكسا من الـ MVC التي ترث من الـ Controller.
في الـ WEB API لا توجد صفحات HTML فقط يتم إرجاع البيانات للمستخدم Client في صيغتي JSON و XML حيث يستخدم الـ IE صيغة الـ JSON والـ Google Chrome صيغة الـ XML … إذا نظر لأول اسلوب وهو GetPerson حيث يقوم بإرجاع البيانات في شكل مصفوفة بدون عرضها على صفحة HTML أيضا في الإسلوب الثاني GetPerson يأخذ معامل واحد من النوع Int32 ويقوم ذلك الإسلوب بإرجاع HTTP 404 إذا لم يجد أي بيانات وإذا وجد بيانات يقوم بإرجاع الإسلوب OK ويترجم للـ HTTP 200 أيضا في الإسلوبين PostPerson و DeletePerson إذا كان هناك أي خطأ سيقوم الـ WEB API بإرجاع اسلوب يُسمى BadRequest وهو عبارة عن HTTP 400 ويحتوي على معامل أختياري Optional parameter يأخذ إذا أردت رسالة نصية توضح نوع الخطأ وهو يعتمد على ModelStateDictionary وقد قمت بتوضيح ذلك في comment في الإسلوب PostPerson.
أيضا لاحظ ان جميع الأساليب تأخذ النوع IHttpActionResult وهي تقوم بتسهيل مجموعة من العمليات التي تقوم بإرجاع البيانات للمستخدم أيضا كما تقوم بتسهيل عملية إختبار الشفرات كما سنرى في الجزء الأخير من هذا المشروع.
أخيرا وليس آخرا قم بفتح ملف الـ WebApiConfig.cs وقم بتحديث عنوان الوصول ليصبح كالتالي:
config.Routes.MapHttpRoute( name: "DefaultApi", routeTemplate: "api/{controller}/{action}/{id}", defaults: new {controller="Persons",action="GetPersons" ,id = RouteParameter.Optional } );
هنا قمت بإستخدام عنوان الوصول الإفتراضي لكن بالتأكيد في المشاريع الكبيرة والمتوسطة لن تقوم بفعل هذا .. فيتوجب عليك إنشاء عناوين وصول لجميع الأساليب التي تقوم الـ IHttpActionResult بإرجاعها
الآن قم بتشغيل المشروع على أي متصفح لديك وقم بلصق الرابط التالي على صندوق عناوين الوصول:
http://localhost:3570/api/persons/getpersons
لا تنسى سيختلف المنفذ لديك (لأنن استخدم الـ IIS Express) ستظهر معك تلك البيانات كما في الصورة التالية:
الآن الحمدلله لقد أنتهينا من المشروع كليا…ومهمتي الأخيرة هل عمل اختبار للشفرات التي كتبناها في فئة التحكم PersonsController ومهمتك أنت أن تقوم بإنشاء مشروع الـ WEB Application الذي يستدعي مشروع الـ WEB API ولو فرضنا اننا نريد أن نقوم بتشغيل الـ WEB API هذا لدى إحدى البنوك بحيث لن يستطيع أي عميل فتح حساب مالم يكون جوازه غير منتهي الصلاحية…عندها بالإمكان إضافة إسلوب للـ PersonsController كالتالي:
public IHttpActionResult ValidToOpenAccountForCustomer(int id) { var customer = _repository.GetPerson(id); // var account = customer.IsProcessFinishing == true; if (customer.IsProcessFinishing == true) { return Ok(); } else { return BadRequest("Sorry This person his Passport NOT valid"); } }
الآن بإمكان البنك أن يستخدم ذلك الإسلوب لفتح حسابات للعملاء عن طريق جوازاتهم..فاذا كان الجواز صالح قم بإنشاء حساب OK – IHttpActionResult وان كان غير ذلك لا تقم بإنشاء حساب.
أرايت كيف تعمل تلك العملية بسهولة…بالتأكيد لن تذهب للبنوك وتقول لهم لقد قمت بإنشاء Services لفتح الحسابات بتلك الصورة…هذه صورة تقديرية فقط..لكنها تعمل – فط تحتاج لكثير من العمل..وكلي ثقة بأنك تستطيع تطويرها..فقط أردت توضيح الفكرة لديك.
عمل إختبار للشفرات
قم بإنشاء مشروع جديد داخل الـ Solution وقم بإعطاءه الإسم DiscoverWebApi.UnitTests وقم بربط المشروع بالمشاريع الثلاثة التي أنشأتها داخل الـ Solution وقم بتثبيت مكتبة الـ Microsoft.AspNet.webapi.core من خلال Nuget package Manager وستقوم تلك المكتبة بتثبيت مكتبات أخرى معها.أيضا قم بتثبيت مكتبة الـ Moq.
بعد إنشائك لمشروع اختبار الأكواد يقوم الفيجوال ستديو بإنشاء فئة بإسم UnitTest1.csقم بفتحها وقم بلصق الشفرة التالية داخلها:
using System; using Microsoft.VisualStudio.TestTools.UnitTesting; using System.Collections.Generic; using System.Web.Http.Results; using DiscoverWebApi.Web.API.Controllers; using DiscoverWebApi.Models; using DiscoverWebApi.Repository; using Moq; using System.Linq; using System.Web.Http; [TestClass] public class UnitTest1 { [TestMethod] public void GetAllPersons_Should_ReturnAllPersons() { Mock<IPersonRepository> mock = new Mock<IPersonRepository>(); mock.Setup(m => m.GetAllPersons).Returns(new AccountDetail[] { new AccountDetail { IsProcessFinishing = true, Name = "Mohammed", NID = "123456", PersonID = 1, Received = true, ReceivedDate = DateTime.Now }, new AccountDetail { IsProcessFinishing = true, Name = "Ahmed", NID = "1234", PersonID = 2, Received = true, ReceivedDate = DateTime.Now }, new AccountDetail { IsProcessFinishing = false, Name = "Hussien", NID = "126", PersonID = 3, Received = true, ReceivedDate = DateTime.Now }, new AccountDetail { IsProcessFinishing = true, Name = "Babiker", NID = "12", PersonID = 4, Received = false, ReceivedDate = DateTime.Now }, new AccountDetail { IsProcessFinishing = false, Name = "weeeeeYaaaaa", NID = "12345698", PersonID = 5, Received = false, ReceivedDate = DateTime.Now } }.AsQueryable()); PersonsController controller = new PersonsController(mock.Object); var result = controller.GetPersons(); Assert.AreEqual(5, result.Count()); } [TestMethod] public void GetPerson_Should_ReturnOnePerson() { var mock = new Mock<IPersonRepository>(); mock.Setup(x => x.GetPerson(40)).Returns(new AccountDetail { PersonID = 40 }); PersonsController controller = new PersonsController(mock.Object); IHttpActionResult actionResult = controller.GetPerson(40); var contentResult = actionResult as OkNegotiatedContentResult<AccountDetail>; Assert.IsNotNull(contentResult); Assert.IsNotNull(contentResult.Content); Assert.AreEqual(40, contentResult.Content.PersonID); } [TestMethod] public void WhenGiveWrongId_ShouldReturn_NotFound() { var mock = new Mock<IPersonRepository>(); PersonsController controller = new PersonsController(mock.Object); IHttpActionResult actionResult = controller.GetPerson(400); Assert.IsInstanceOfType(actionResult, typeof(NotFoundResult)); } [TestMethod] public void DeletePerson_ShouldReturn_Ok() { var mock = new Mock<IPersonRepository>(); mock.Setup(x => x.GetPerson(40)).Returns(new AccountDetail { PersonID = 40 }); PersonsController controller = new PersonsController(mock.Object); IHttpActionResult actionResult = controller.DeletePerson(40); Assert.IsInstanceOfType(actionResult, typeof(BadRequestResult)); } [TestMethod] public void PostPerosn_ShuoldReturn_DefaultUrl() { var mock = new Mock<IPersonRepository>(); var controller = new PersonsController(mock.Object); IHttpActionResult actionResult = controller.PostPerson(new AccountDetail { PersonID = 1, Name = "Mohammed" }); var createResult = actionResult as CreatedAtRouteNegotiatedContentResult<AccountDetail>; Assert.IsNotNull(createResult); Assert.AreEqual("DefaultApi", createResult.RouteName); Assert.AreEqual(1, createResult.RouteValues["id"]); } }
لدينا جميع الإحتمالات الواردة والتي يمكننا عمل إختبار لها وبعد تنفيذك لهذا الإختبار ستظهر معك الصورة التالية والتي يوجد بها العلامة الخضراء والتي تبين لك أن مشروع جاهز للنشر بعد فحص جميع أجزاءه وهي تعمل بصورة صحيحة.
الآن مهمتك هي عمل اختبار للأسلوب الأخير الذي قمنا بإضافته للتأكد من أن جواز المواطن صالح حتى يتسنى له فتح حساب بنكي…اختبرها الآن..بإمكانك.
مهلا … ما رأيك نقوم بتنفيذه مع بعضنا البعض الأسلوب لدينا يقوم بإرجاع الـ IHttpActionResult من النوع OK وبالتالي سنوقم بالتأكد من صحة نوع البيانات التي سيقوم الخادم بإرجاع Response من النوع OK... قم بلصق تلك الشفرة على فئة UnitTest1.cs
[TestMethod] public void ValidToOpenAccountForCustomer_ShouldReturnOk_WhenThePassportIsValid() { var mock = new Mock<IPersonRepository>(); mock.Setup(x => x.GetPerson(40)).Returns(new AccountDetail { PersonID = 40,IsProcessFinishing=true }); PersonsController controller = new PersonsController(mock.Object); IHttpActionResult actionresult = controller.ValidToOpenAccountForCustomer(40); Assert.IsInstanceOfType(actionresult, typeof(OkResult)); }
الآن انظر للصورة التالية…لقد قمنا بعمل فحص للأسلوب ValidToOpenAccountForCustomer وقد نجح الفحص…(الآن هل يمكنك عرض المشروع لأحد البنوك…بالتأكيد يحتاج للكثير!!).
تحياتي – محمد
كيف يتم استدعاء او حصول بيانات موقع و كيف معرفة اذا الموقع بيقدم لي انا كـ مطور أحقية للوصول للبيانات بشكل الـ (api) ؟