… تحدثتُ في المقالة السابقة لي، عن كيفية تهيئة الـ Service من خلال استخدام منصة الـ ASP.NET Api وعرضت مشروع مبسط يختص بتجديد واصدار جوازات المواطنين، كما عرضت كيفية اختبار الشفرات بإستخدام منصة مايكروسوفت Unit Test لجميع الأساليب التي أنشأناها في فئة التحكم PersonsController وكما اسلفت في تلك المقالة ان فئات التحكم في مشروع الـ Web Api ترث من فئة الـ ApiController ولا تقوم بإرجاع صفحات Html بل تقوم بإرجاع عناوين نقل المعلومات والتي يتولاها برتوكول الـ HTTP؛ بمعنى آخر يستقبل العميل صيغتان وهما – Xml و json ويستطيع العميل تهيتة تلك الصيغتان في بيئته البرمجية…(وهذا ما سنراه في هذه المقالة)!
بإمكانك مراجعة المقالة السابقة من هنا
مفهوم الـ WEB API … تهيئتهُ … إستدعاءهُ … إختبار الشفرات عن طريق الـ Unit Testing
…. ويجب عليك مراجعتها (المقالة) ان أردت ان تواصل في هذه المقالة الجديدة لأنها تعتمد على المقالة الأولى.
وكما موضح بالصورة ادناه جميع البيانات التي يظهرها متصفح الكروم بصيغة XML والتي تم عرضها من المقالة السابقة
في هذه المقالة سنقوم ببناء تطبيق بسيط لبنك يختص بفتح حسابات جديدة للمواطنين، وفتح الحساب يعتمد على ان يكون جواز المواطن ساري المفعول وهو يعتمد على الأسلوب ValidToOpenAccountForCustomer الذي أنشأناه في المشروع السابق (مشروع الـ Api) فإذا كان جواز المواطن ساري المفعول سيتم فتح حساب جديد…اما اذا كان غير ذلك فلن يستطيع موظف البنك فتح حساب لأن الخدمة عبارة عن Readonly يتم قراءتها عبر رابط من الـ Services داخل شفرة تطبيق العميل.
البيئة البرمجية التي استخدمها في هذه المقالة هي Visual Studio 2017 بالنسبة لتطبيق العميل Client او لنقل الـ Consume وبالنسبة للخدمة نفسها (Api) كما في المقالة السابقة موضحة، قمت بإستخدام Visual Studio 2015 .. لكن أثناء كتابتي وتحرير لشفرة تطبيق العميل استخدمت Visual Studio Express 2013 For Web لفتح تطبيق الـ Web API.
ملاحظة – يمكنك استخدام Visual Studio 2013 اذا اردت تنفيذ هذا المشروع، سواء إعادة بناء الخدمة Web API وبناء شفرة العميل.
في المقالة الأولى التي أشرت لرابطها آنفا توجد لدينا قاعدة يبانات باسم WebAPI_DB وبها جدول واحد بإسم AccountDetails وداخل هذا الجدول يوجد حقل بإسم IsProcessFininshing ويُعني بإنتهاء عملية تجديد الجواز ففي حالة True – تم تجديد الجواز. وفي حالة False – الجواز لم يتم تجديده بعد.
مشروع العميل Client/Consume سيعتمد على هذا الحقل لفتح حساب بنكي لمواطن ما عن طريق رقمه الوطني وفتح الحساب البنكي يعتمد على القيمة True في الحقل المعني.
… سأقوم بعمل التطبيق خطوة بخطوة وسأقوم بشرح ما سنكتبه من شفرات بعد الإنتهاء من كل خطوة.
في المشروع السابق الذي أشرت لرابطه أعلاه قم بعمل تعديل بسيط على أحد حقول البيانات للحقل IsProcessFininshing ليصبح False.
قم بفتح الفيجوال ستديو 2017 أو 2015 أو 2013 لديك وقم بعمل تطبيق ويب جديد من النوع الفارغ (Empty) وقم بتسمة المشروع ASPNETConsumeWebApi كما مبين في الصورة ادناه.
بيئة العمل لدى تعمل على فيجوال ستديو 2017 واقوم بإستخدام الـ Dot Net Frameworm 4.7.1
من الصفحة التي ستظهر بعد انشاء المشروع قم بإختيار النوع Empty ولا تنسى تحديد نوع المشروع ليكون MVC وكما موضح بالصورة أدناه.
قم بتثبيت الحزم التالية على من على الـ Nuget داخل المشروع:
System.Net.Http Microsoft.Net.Http System.Net.Http.Formatting
هذه المكتبات ستساعدنا على في مشروعنا والتي مرتبطة بالعمل مع الـ Web API …
قم بإنشاء فئة بإسم AccountDetail داخل ملف الـ Models وألصق بها الشفرة التالية:
[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; } }
لاحظ ان تلك الفئة هي نفسها الفئة الموجودة بمشروع الـ Web service والتي ستقوم بإرجاع العناصر الموجودة داخل قاعدة البيانات…(حيث لاتوجد قاعدة بيانات داخل مشروع العميل Client / Consume).
قم بإنشاء فئة تحكم جديدة بإسم BankAccountController وتلك الفئة بها اسلوبين Two Action Result الاول يقوم بإرجاع جميع المواطنين من خدمة الـ Web API والثاني يقوم بإرجاع مواطن واحد لفتح حساب بنكي لهُ ويعتمد فتح الحساب البنكي للمواطن على الإسلوب ValidToOpenAccountForCustomer والموجود بفئة التحكم PersonsController والتي موجودة بمشروع الـ Web API Service ، الآن قم بلصق الشفرة التالية داخل الفئة التي اشئتها اخيرا:
using System; using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; using System.Web; using System.Web.Http; using System.Net.Http; using System.Net.Http.Headers; using System.Web.Mvc; using ASPNETConsumeWebApi.Models; String myBaseAddress = "http://localhost:3570/"; public async Task<ActionResult> GetAllPersons() { IEnumerable<AccountDetail> acc = null; HttpClient client = new HttpClient(); client.BaseAddress = new Uri(myBaseAddress); client.DefaultRequestHeaders.Accept.Clear(); client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); HttpResponseMessage response = await client.GetAsync("api/persons/getpersons"); if (response.IsSuccessStatusCode) { var persons = response.Content.ReadAsAsync<IEnumerable<AccountDetail>>().Result; acc = persons; } return View(acc); } public async Task<ActionResult> OpenAccountForPersonHas(int id) { AccountDetail acc = null; HttpClient client = new HttpClient(); client.BaseAddress = new Uri(myBaseAddress); client.DefaultRequestHeaders.Accept.Clear(); client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); HttpResponseMessage response = await client.GetAsync($"api/persons/ValidToOpenAccountForCustomer/{id}"); if (response.IsSuccessStatusCode) { acc = await response.Content.ReadAsAsync<AccountDetail>(); return View(acc); } else { return View("InvalidDocument", response.StatusCode); } }
هل تدري ان هذا كل ما سنحتاجه للقيام بفتح حساب بنكي جديد للمواطن يعتمد على تنفيذه على خدمة خارجية عن طريق رابط يأتي من Web Server آخر غير الذي موجود به تطبيق العميل Hosted..
فقط سنقوم ببناء صفحات العرض Html views بالنسبة لتطبيق العميل Client/Consume
حسنا في الإسلوب الأول GetAllPerson قمنا بعمل مثيل من الواجهة IEnumerable والتي تقوم بإرجاع البيانات في شكل مصفوفة هذا المثيل instance قمنا بإعطاءه القيمة null والمثيل هذا (acc) سنقوم بإسناد جميع البيانات فيه لنقوم بعرضها على صفحة الـ view الخاصة بإرجاع جميع المواطنين.
في السطر الثاني في ذلك الاسلوب قمنا بعمل مثيل instance من الفئة HttpClient:
HttpClient client = new HttpClient();
فئة الـ HttpClient هي محور استدعاء البيانات من مشروع الـ Web Service ، هذه الفئة بها خاصية تساعدنا على تحديد الرابط الذي سنستخدمه في جلب البيانات..او قل بمعنى آخر الرابط الذي سيوفره لنا المسؤول من تطبيق الـWeb API. وإذا لاحظت قمنا بتعريف الرابط في أعلى الفئة وتأخذ النوع النصي String وتوضح رقم المنفذ لدىّ 3570 والذي يعمل على بيئة التشغيل لدىّ work station من خلال الـ IIS Express
String myBaseAddress = "http://localhost:3570/";
وهذه الخاصية من النوع الذي يمكن تغييره وجلبه:
public Uri BaseAddress { get; set; }
في السطر الرابع قمنا بإستدعاء (Property) الخاصية DefaultRequestHeaders والتي من خلالها سنوضح أي صيغة من صيغ البيانات نود ارجاعهُ من خلال مشروع الـ Web API حيث قمت بعمل تنظيف لجميع الصيغ اولاً وفي السطر الذي يليه قمت بتحديد نوع الصيغة التي أود إرجاعها وهي من النوع json ، والخاصية DefaultRequestHeaders عبارة عن قارئ فقط اي ReadOnly وهي كالتالي:
public HttpRequestHeaders DefaultRequestHeaders { get; }
في السطر السادس في الاسلوب GetAllPersons قمنا بإستخدام فئة HttpRsponseMessage والتي من خلاها سنستدعي الأسلوب الموجود بمشروع الـ Web API نفسه من خلال الرابط الذي قمنا بتعريفه في المثيل client من خلال الفئة HttpClient ولو لاحظت هنا قمت بإستخدام كلمة await والتي ستقوم بعمل قفل حتى يتم الانتهاء من استدعاء جميع المواطنين للطلب Request ومن خلال المثيل response سنختبر نجاح العملية عن طريق الخاصية IsSuccessStatusCode ومن ثم سنقوم بإرجاع الصفحة التي سيتم عليها عرض جميع المواطنين…لأن جميع البيانات ستضمّن في المثيل response والذي بدوره سينقل البيانات للمثيل الذي أنشأناه أولا acc.
حسنا قم بالضغط بالزر الأيمن للمؤشر (ماوس) على الاسلوب GetAllPersons وقم بإختيار Add View وقم بالضغط على Add مباشرة..سيقوم الفيجوال ستديو بإنشاء صفحة Html داخل الملف Views داخل الملف BankAccount لذلك الإسلوب… وقم بلصق الشفرة التالية داخل الصفحة:
@model IEnumerable<ASPNETConsumeWebApi.Models.AccountDetail> @{ ViewBag.Title = "GetAllPersons"; } <table> <tr> <td colspan="2" style="background-color:#808080;text-align:center;color:white;">جميع المواطنين</td> </tr> @foreach (var person in Model) { <tr> <td>@Html.ActionLink("قم بفتح حساب جديد", "OpenAccountForPersonHas", new { id = person.PersonID })</td> <td><h6>@person.Name</h6></td> </tr> } </table>
هذه صفحة Html عادية جدا..بها جدول يوجد به اسماء المواطنين وامام كل مواطن يوجد رابط سنقلنا للإسلوب OpenAccountForPersonHas والذي مهمته فتح الحساب البنكي
ملاحظة: دائما ما اكتب فتح حساب بنكي؛ فهذه طريقة إجرائية للشرح…مهمتي هنا ليست لإنشاء قاعدة بيانات لفتح الحسابات لبنك ما؛ بل لشرح إستدعاء مشروع الـ Web API من خلال روابط محددة تأخذ جميع الإجراءات من التطبيق…فإن أردت ان تقوم بعمل قاعدة بيانات داخل مشروع العميل Client/Consume فبإمكانك عمل هذا بإتمام عملية فتح الحساب البنكي…لكن الإجراء هنا اذا كان جواز المواطن ساري قم بفتح حساب واذا غير ذلك لن يتم فتح حساب.
الإسلوب الآخر OpenAccountForPersonHas لا يختلف كثيرا عن الإسلوب الاول فقط يكمن الفرق في هذا الاسلوب يأخذ معامل Parameter ليقوم بإرجاع مواطن واحد من الاسلوب ValidToOpenAccountForCustomer والذي يوجد بمشروع الـ Web API
HttpResponseMessage response = await client.GetAsync($"api/persons/ValidToOpenAccountForCustomer/{id}");
لقد قمت بإستخدام الـ String Interpolation بإستخدام علامة الدولار $ قبل فتح علامتي تنصيص ” “ والتي ستساعدني في القوائم المنسدلة التي يقوم الفيجوال بإستدعائها اثناء كتابة الجملة النصية نفسها..لاحظ الصورة ادناه.
يقوم ذلك الاسلوب بإرجاع صفحة لفتح حساب المواطن في حال كان الجواز ساري المفعول…وإلا سيقوم بعرض رسالة توضح عدم سريان الجواز للتمكن من فتح حساب بنكي جديد في صفحة InvalidDocument
الآن قم بإنشاء صفحة Html View لهذا الاسلوب وقم بلصق الشفرة التالية داخلها
@model ASPNETConsumeWebApi.Models.AccountDetail @{ ViewBag.Title = "OpenAccountForPersonHas"; } <h2>فتح حساب لعميل </h2> <table> <tr> <td><input type="button" value="Open new account" style="width:200px;"/></td> <td><h4>@Model.Name</h4></td> </tr> </table>
أيضا قم بعمل صفحة Html View وأعطها الاسم InvalidDocument وألصق بها الشفرة التالية:
@{ Layout = null; } <!DOCTYPE html> <html> <head> <meta name="viewport" content="width=device-width" /> <title></title> </head> <body> <div> <h1>عفوا لا تستطيع فتح حساب لهذا المواطن نسبة لعدم صلاحية وثيقته الرسمية</h1> </div> </body> </html>
الآن قم بتشغيل التطبيقين تطبيق الـ Web API والتطبيق الذي تعمل عليه…تطبيق Web API سيتيح لنا قراءة المنفذ الذي موضحّ في السطر الأول من فئة BackAccountController الذي يأخذ النوع String وسيكون نشط دائما وإلا لن نستطيع إستدعاء البيانات من خلاله داخل مشروع العميل Client/Consume
قم بكتابة الرابط التالي داخل متصفحك
http://localhost:1788/BankAccount/GetAllPersons
لا تنسى بأن المنفذ سيكون مختلفا لديك..وذلك لأنني أستخدم الـ IIS Express والذي يتم تثبيته أثناء تثبيت الفيجوال ستديو…لذلك قم بتشغيل التطبيق أولا ومن ثم؛ قم بلصق اسم فئة التحكم والاسلوب المعني.
هذا الرابط مهمته إرجاع جميع المواطنين…وسيقوم بإظهارهم لنا في صفحة الـمتصفح في صورة منسقة عكسا من عرضها على صيغة XML أو json التي تظهر من خلال مشروع الـ Web API service
ستظهر معك تلك البيانات كما موضح أدناه بالصورة:
في حال تمريرك للمؤشر (ماوس) فوق الرابط قم بفتح حساب جديد للمواطن الذي رقمه 1 أنظر لأسفل الشاشة كما موضح بالصورة التالية
ستوضح لك ماهو الأسلوب الذي سياخذه الرابط … عند ضغطك على الرابط ستظهر معك بيانات المواطن والذي اسمه Person one ويوجد زر سينقلك لصفحة فتح حساب بنكي جديد.
قم بالضغط على الرابط الثالث للمواطن Person Three سيعرض لك الصفحة التالية:
ولأن ذلك المواطن جوازه غير ساري المفعول ستظهر معنا الرسالة الموضحة في الصورة اعلاها والتي تقرأ من صفحة InvalidDocument. لأن الحقل IsProcessFininshing لهذا المواطن False كما قمنا بتعديله في بداية تلك المقالة.
في حال أردت ان تقوم بإتمام عملية فتح الحساب البنكي بإمكانك إنشاء قاعدة بيانات داخل مشروع ASPNETConsumeWebApi وداخل قاعدة البيانات قم بإنشاء جدول به تفاصيل رئيسية لمعلومات فتح الحساب…بعدها قم بعمل أسلوب (Action Result) داخل فئة BankAccountController مهمتها عمل تنفيذ لفتح الحساب وقم بتعديل طفيف داخل صفحة OpenAccountForPersonHas لتنفيذ الجملة المنطقية Logic لزر فتح الحساب لينقلك لصفحة فتح الحساب.
أيضا إذا أردت ان تقوم بإختبار الشفرات فمن السهولة بمكان عمل الاختبار فقط قم بعمل مثيل instance لفئة الـ HttpClient من خلال المكتبة Moq لتستطيع العمل بصورة غير حقيقية virtual داخل بيئة إختبار الأكواد من خلال الفيجوال ستديو.
تحياتي – محمد
مقال مهمه جدا شكراً على المقالة