اذهب إلى المحتوى

البحث في الموقع

المحتوى عن 'location'.

  • ابحث بالكلمات المفتاحية

    أضف وسومًا وافصل بينها بفواصل ","
  • ابحث باسم الكاتب

نوع المحتوى


التصنيفات

  • الإدارة والقيادة
  • التخطيط وسير العمل
  • التمويل
  • فريق العمل
  • دراسة حالات
  • التعامل مع العملاء
  • التعهيد الخارجي
  • السلوك التنظيمي في المؤسسات
  • عالم الأعمال
  • التجارة والتجارة الإلكترونية
  • نصائح وإرشادات
  • مقالات ريادة أعمال عامة

التصنيفات

  • مقالات برمجة عامة
  • مقالات برمجة متقدمة
  • PHP
    • Laravel
    • ووردبريس
  • جافاسكربت
    • لغة TypeScript
    • Node.js
    • React
    • Vue.js
    • Angular
    • jQuery
    • Cordova
  • HTML
  • CSS
    • Sass
    • إطار عمل Bootstrap
  • SQL
  • لغة C#‎
    • ‎.NET
    • منصة Xamarin
  • لغة C++‎
  • لغة C
  • بايثون
    • Flask
    • Django
  • لغة روبي
    • إطار العمل Ruby on Rails
  • لغة Go
  • لغة جافا
  • لغة Kotlin
  • لغة Rust
  • برمجة أندرويد
  • لغة R
  • الذكاء الاصطناعي
  • صناعة الألعاب
  • سير العمل
    • Git
  • الأنظمة والأنظمة المدمجة

التصنيفات

  • تصميم تجربة المستخدم UX
  • تصميم واجهة المستخدم UI
  • الرسوميات
    • إنكسكيب
    • أدوبي إليستريتور
  • التصميم الجرافيكي
    • أدوبي فوتوشوب
    • أدوبي إن ديزاين
    • جيمب GIMP
    • كريتا Krita
  • التصميم ثلاثي الأبعاد
    • 3Ds Max
    • Blender
  • نصائح وإرشادات
  • مقالات تصميم عامة

التصنيفات

  • مقالات DevOps عامة
  • خوادم
    • الويب HTTP
    • البريد الإلكتروني
    • قواعد البيانات
    • DNS
    • Samba
  • الحوسبة السحابية
    • Docker
  • إدارة الإعدادات والنشر
    • Chef
    • Puppet
    • Ansible
  • لينكس
    • ريدهات (Red Hat)
  • خواديم ويندوز
  • FreeBSD
  • حماية
    • الجدران النارية
    • VPN
    • SSH
  • شبكات
    • سيسكو (Cisco)

التصنيفات

  • التسويق بالأداء
    • أدوات تحليل الزوار
  • تهيئة محركات البحث SEO
  • الشبكات الاجتماعية
  • التسويق بالبريد الالكتروني
  • التسويق الضمني
  • استسراع النمو
  • المبيعات
  • تجارب ونصائح
  • مبادئ علم التسويق

التصنيفات

  • مقالات عمل حر عامة
  • إدارة مالية
  • الإنتاجية
  • تجارب
  • مشاريع جانبية
  • التعامل مع العملاء
  • الحفاظ على الصحة
  • التسويق الذاتي
  • العمل الحر المهني
    • العمل بالترجمة
    • العمل كمساعد افتراضي
    • العمل بكتابة المحتوى

التصنيفات

  • الإنتاجية وسير العمل
    • مايكروسوفت أوفيس
    • ليبر أوفيس
    • جوجل درايف
    • شيربوينت
    • Evernote
    • Trello
  • تطبيقات الويب
    • ووردبريس
    • ماجنتو
    • بريستاشوب
    • أوبن كارت
    • دروبال
  • الترجمة بمساعدة الحاسوب
    • omegaT
    • memoQ
    • Trados
    • Memsource
  • برامج تخطيط موارد المؤسسات ERP
    • تطبيقات أودو odoo
  • أنظمة تشغيل الحواسيب والهواتف
    • ويندوز
    • لينكس
  • مقالات عامة

التصنيفات

  • آخر التحديثات

أسئلة وأجوبة

  • الأقسام
    • أسئلة البرمجة
    • أسئلة ريادة الأعمال
    • أسئلة العمل الحر
    • أسئلة التسويق والمبيعات
    • أسئلة التصميم
    • أسئلة DevOps
    • أسئلة البرامج والتطبيقات

التصنيفات

  • كتب ريادة الأعمال
  • كتب العمل الحر
  • كتب تسويق ومبيعات
  • كتب برمجة
  • كتب تصميم
  • كتب DevOps

ابحث في

ابحث عن


تاريخ الإنشاء

  • بداية

    نهاية


آخر تحديث

  • بداية

    نهاية


رشح النتائج حسب

تاريخ الانضمام

  • بداية

    نهاية


المجموعة


النبذة الشخصية

تم العثور على 3 نتائج

  1. تقتصر بعض تطبيقات Angular على كونها أدوات مساعدة في صفحة ويب تقليدية، إلا أنّ الأغلبية العُظمى لتطبيقاتها هي التطبيقات وحيدة الصفحة (single-page applications)، التي تستبدل تصفح الويب المعتمد على المتصفّح بواجهاتٍ وانتقالاتٍ مشابهة في صفحةٍ واحدة، مقدمة للمستخدم تجربة تفاعليّة رائعة. إلّا أنّ كتابة تطبيقات وحيدة الصفحة بطريقةٍ بسيطة يجعلنا نخسر شيئًا قيّمًا في التطبيقات المعتمدة على المتصفّح وهو الرّابط URL. ففي تطبيقٍ بسيطٍ وحيد الصفحة، سيكون هناك رابط URL واحد فقط، ولن تكون هناك طريقة لمشاركة روابطَ مخصصة لكلّ مورد (resource) على حدة، كتعليق محدّد على تدوينة ما على سبيل المثال. وعلى العكس، فسيقوم تطبيق تقليدي في طرف المخدّم يتبع نمط REST (تبادل الحالة ضمن رابط HTML) بإدارة الواجهات كبنية هرميّة من الروابط المنظّمة سهلة القراءة والتي تظهر حالة التّطبيق الحاليّة بوضوح. تُقدّم هذه الروابط طريقةً للمستخدم ليعود إلى حالةٍ من حالات التطبيق في وقتٍ لاحق، أو ليشارك هذه الحالة مع الآخرين. يُعرَّف التوجيه في تطبيق ويب تقليديّ بأنّه صلة الوصل بين عمليّات HTTP مثل (GET وPOST وما إلى ذلك) وأنماط الروابط وبين المتحكّمات. إن لم تكن معتادًا على التوجيه من طرف المخدّم فسيقدّم لك دليل التوجيه السريع مقدّمةً سهلةً للتوجيه في JavaScript. على أي حال، فالتوجيه من طرف المستخدم يقلب الحالة رأسًا على عقب. فبدلًا من أن يتمّ التفاعل مع الأفعال التي ينقلها لنا المتصفّح، فسنحتاج إلى إبقاء المتصفّح متابعًا للتحديثات بينما يتفاعل التطبيق مباشرةً مع مدخلات المستخدم. كمثالٍ على ذلك، كيف يمكننا الإبقاء على شريط التّنقل محدّثًا بروابط تمثّل الحالة بدقّة، وفي نفس الوقت نستجيب لروابط تُدخل فيه أو تُحمّل من العلامات المرجعية؟ لحُسن الحظ، فقد تمّ إيجاد الوحدة ngRoute لتساعدنا في هذا العمل. لنتمكّن من استخدام هذه الوحدة، سنحتاج إلى تحميل ملف angular-route.js إضافةً إلى ملف مكتبة Angular الرئيسي. كما سنقوم بتحميل بيئة Bootstrap لاستخدام أنماط CSS الخاصة بها. <base href="/"> <link rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css"> <script src="//ajax.googleapis.com/ajax/libs/angularjs/1.4.2/angular.js"></script> <script src="//ajax.googleapis.com/ajax/libs/angularjs/1.4.2/angular-route.js"></script>لاحظ تعريف العنصر base في السطر الأول في المثال السابق. يُعرِّف هذا العنصر رابط URL القاعديّ لتطبيق Angular الحالي، وهذا العنصر يطلبه دعم Angular لـتوابع HTML5 الخاصة بالتاريخ (سنتحدث عنها أدناه). إن كنت تتساءل لمَ تم إسناد الخاصية href إلى مسار الجذر، بدلًا من أن تكون مسندةً إلى مسار الصفحة، فهذا لأنّ أمثلة هذه السلسلة تعمل داخل أُطُر iframe خاصة لكل واحد منها. <body ng-app="app"> <!-- كل الأمثلة توضع هنا --> </body>بعد ذلك، سيكون علينا جلب ngRoute عند تهيئة الوحدة الجذر، وقد قمنا بتغطية هذه الفكرة بعمق في فصل الوحدات. angular.module('app', ['ngRoute']);وهكذا نكون قد أتممنا تحميل الوحدة ngRoute في تطبيقنا. routeProvider$لنتمكن من استخدام ngRoute، سنحتاج إلى ربط مسار URL نسبي واحدٍ أو أكثر مع معالجاتٍ (handlers). يتيح لنا التابع module.config إعداد وحداتٍ مثل ngRoute بعد أن تقوم Angular بتحميلها. كل ما علينا هنا هو معرفة اسم الخدمة (service) أو المزوّد (provider) الذي سيقوم بإعداد الوحدة. في حالة ngRoute يُسمّى المزوِّد بـrouteProvider$. سنتمكن عند وضع هذا الاسم في تابع الاستدعاء الخلفي config من عمل سلسلةٍ من عمليّات ربط المسارات باستخدام التابع when. الوسيط الأول للتابع when هو المسار النسبي الخاص بوجهة التوجيه، أما الوسيط الثاني فهو كائن الإعدادات الذي يحدد الكيفية التي سيتم بها معالجة محتوى وجهة التوجيه. angular.module('app') .config(function($routeProvider) { $routeProvider .when('/', { template: "<a href='#/about'>About</a>" }) .when('/about', { template: "<a href='#/'>Home</a>" }); });يشبه الخيار template الذي يظهر في المثال السابق ذلك الذي استخدمناه في إعداد التوجيهات سابقًا. لا يحوي المثال السابق أي محتوىً متغيّر، فقط نص HTML ثابت. يُقدّم كلّ قالبٍ رابطًا إلى المسار النسبي للآخرين، لذا سنتمكن في المثال السابق من التبديل بين الاثنين. قد تتساءل أين سيتم وضع المحتوى المعالَج، حسنًا، تُقدّم الوحدة ngRoute توجيهًا مُميّزًا، هو ng-view، ويجب أن يكون هذا التوجيه موجودًا في وحدتنا لتعمل. تتكوّن القوالب الرئيسي في هذا الفصل كلّها من عنصرdiv خالٍ يوجد فيه التوجيه ng-view، وكلّ ما سوى ذلك سيقوم المعالج (handler) بمعالجته للمسار الحالي. <div ng-view></div>جرب النقر على الرابط الناتج لتتم معالجة الرابط للمورد الآخر. لم يعمل؟ حسنًا، لقد تجاهلنا أمرًا هامًا، إن نظرت إلى شريط التنقل الخاص بمتصفحك أثناء نقر الرابط فلن تراه قد تغير ليلائم المسار النسبي الصحيح، في الواقع، ليس هناك أي أمرٍ خاطئ في النص البرمجي الخاص بالمثال، وفي التطبيقات الحقيقية ستلاحظ التغيير، فكلّ ما في الأمر أن البيئة التفاعلية التي تمّ استضافة الأمثلة في داخلها ضمن هذه السلسلة لا تسمح لنا برؤية التغيير في الصفحة الرئيسية للفصل. الخدمة location$جميع أمثلة هذه السلسلة موضوعة داخل صناديق sandbox داخل أُطر iframe الخاصة بكل واحد منها. (يُمكنك إلقاء نظرة على الرابط التالي Codecademy/stuff.js إن كان لديك فضول لمعرفة طريقة القيام بذلك.) وهذا ممتازٌ لعزل البيئة البرمجية، إلا أنّه يمنعك من رؤية رابط الـURL الخاص بالأمثلة في شريط الانتقال. ومن حسن الحظ أنّه توجد طريقةٌ للتحايل على هذا الأمر، مع فائدةٍ إضافيّة في استكشاف خدمةٍ مفيدةٍ في Angular تسمح بمعاينة رابط URL الحالي، وهي الخدمة location$. angular.module('app') .controller('LocationController', function($scope, $location) { $scope.location = $location.absUrl(); });يتم في المثال السابق التصريح عن متحكم بسيطٍ يستقبل الخدمة location$ عن طريق حقن التبعية، ويستخدمها للدخول إلى رابط URL المُطلق الحالي للتطبيق، حيث تكون القيمة هي دمجًا للرابط الثابت للجذر الخاص بالصفحة المغلّفة للمثال مع رابط الامتداد الذي تُديره الوحدة ngRoute. المتحكميُمكننا أن نقوم بتحميل المتحكّم LocationController في المثال السابق بالطريقة المعيارية باستخدام التّوجيه ng-controller، إلّا أنّه بإمكاننا أيضًا أن نقوم بإعداد متحكّمٍ كخيار (option)، كما سنضيف خاصّيّة المجال location إلى قالبنا. angular.module('app') .config(function($routeProvider) { $routeProvider .when('/', { controller: 'LocationController', template: '<div class="well well-sm" ng-bind="location"></div>\ <a href="#/about">About</a>' }) .when('/about', { controller: 'LocationController', template: '<div class="well well-sm" ng-bind="location"></div>\ <a href="#/">Home</a>' }); });ها نحن ذا، صار لدينا الآن محاكٍ لشريط الانتقال لأمثلتنا، ولكن للأسف لن تتمكّن من تعديلها لرؤية الكيفية التي ستدير بها الوحدة ngRoute التغييرات بمعالجتها وإظهار محتوىً جديد، وسيكون عليك اختبار ذلك في تطبيقاتك الخاصة. رمز Hashbangتتوقّع وحدة ngRoute في الحالة الافتراضية أن تبدأ مسارات URL النسبيّة برمز hash (#)، ويُمكنك بسهولةٍ تغييره إلى البادئة hashbang متمثّلة بالرمز (!#) بإضافة إعداداتٍ إلى الخدمة locationProvider$ كما يبيّن المثال التالي. لاحظ أنّه يجب علينا أيضًا إضافة رمز ! إلى الروابط في قوالبنا. angular.module('app') .config(function($locationProvider) { $locationProvider.hashPrefix('!'); }) .config(function($routeProvider) { $routeProvider .when('/', { controller: 'LocationController', template: '<div class="well well-sm" ng-bind="location"></div>\ <a href="#!/about">About</a>' }) .when('/about', { controller: 'LocationController', template: '<div class="well well-sm" ng-bind="location"></div>\ <a href="#!/">Home</a>' }); });لا يزال رمز البادئة hashbang مصادقًا عليه من Google في دليل Webmasters الخاص بها، وتحديدًا في المقطع Making AJAX Applications Crawlable، الذي ينص على أنّ "أقسام الـhash يجب أن تبدأ بعلامة تعجّب"، على أيّ حالٍ، طالما أنّك تقوم بالخطوات اللازمة للتّأكّد من ملاءمة موقعك لقواعد SEO فقد تجد أنّه من الأفضل أن يكون تطبيقك مخدومًا بأسلوبٍ خالٍ من البادئة، وهذا الأسلوب مفعّلٌ في HTML5 الآن. الكائن History من HTML5يُقدّم الكائن window.history تحكّمًا بالتّنقّل في المتصفّحات الحديثة، مما يقدّم تجربة مستخدمٍ سلسة. angular.module('app') .config(function($locationProvider) { $locationProvider.html5Mode(true); });يُمكننا كتابة قيم href الخاصة بنا كمسارٍ بسيط، بدون البادئة # ولا البادئة !#. angular.module('app') .config(function($routeProvider) { $routeProvider .when('/', { controller: 'LocationController', template: '<div class="well well-sm" ng-bind="location"></div>\ <a href="/about">About</a>' }) .when('/about', { controller: 'LocationController', template: '<div class="well well-sm" ng-bind="location"></div>\ <a href="/">Home</a>' }); });استخدام الكائن history من HTML5 وسيلةٌ ممتازةٌ إن كنت غير مضطرٍّ لدعم متصفحاتٍ قديمة في تطبيقك. العنصر templateUrlلنقم الآن بتحليل تطبيقنا البسيط إلى مكوناته الأولية. كما قمنا سابقًا مع التوجيهات بجعل شيفراتنا البرمجية أكثر قابلية للإدارة عن طريق استخراج قوالبنا (حتى الصغيرة منها) إلى ملفاتٍ مستقلة، فسيكون علينا للقيام بذلك فقط استبدال العناصر template في إعداداتنا بالعناصر templateUrl. angular.module('app') .config(function($routeProvider) { $routeProvider .when('/', { controller: 'LocationController', templateUrl: '/views/index.html' }) .when('/about', { controller: 'LocationController', templateUrl: '/views/about.html' }); });دون تغييرٍ على صفحة About التي سنضعها في مجلد views. <div class="well well-sm" ng-bind="location"></div> <a href="/">Home</a> <h4>About</h4>سنتكلّم في الفقرات التالية عن فكرة التعامل مع روابط URL غير الصالحة، لذا لنقم بإضافة واحدٍ منها الآن. <div class="well well-sm" ng-bind="location"></div> <a href="/about">About</a> | <a href="/bad-path">Bad path</a> <h4>Home</h4>جرّب النقر على Bad path في المثال السابق، ستنتهي التّسلية للأسف، وستضطر لإعادة إنعاش الصفحة لاستعادتها. التابع otherwiseما الذي يمكننا فعله لتجنب النهايات الحزينة كما حدث قبل قليل؟ قد يكون أحد الإجابات هو تجنب تقديم روابط غير صالحة، ولكن في التطبيقات الحقيقيّة لا يمكننا إيقاف المستخدم عن الكتابة في شريط التنقل. يُمكننا أن نُعِدّ المُخدّم ليقوم بإعادة توجيهٍ للروابط غير الصالحة إلى رابط الجذر لتطبيق Angular، ولكن هذا يعني إعادة تحميل الصفحة من جديد، مما يعني إعادة تشغيل التطبيق من البداية. كيف يُمكننا بناء تطبيقٍ من طرف المستخدم ليقوم بإعادة توجيهٍ للروابط غير الصالحة؟ لو افترضنا بأننا نريد أن نُعلم المستخدم بأن الرابط غير صالح قبل أن نقوم بإعادة التوجيه، فالأمر الأول الذي نحتاجه هو الواجهة التي ستظهر لأي رابط غير صالح. سنسُمّي هذا القالب 404 ليتناسب مع معناه، وطبعًا يمكنك تسميته بأي اسمٍ آخر. <div class="well well-sm" ng-bind="location"></div> <a href="/">Home</a> <h4 class="text-danger">404 - Not Found</h4>كلّ ما نحتاجه الآن هو عبارة when أخرى لإضافة المسار 404/. ثم لربط أي رابط غير متوافق مع الروابط الموجودة بالمسار 404/، وسنقوم بذلك بإضافة استدعاءٍ للتابع otherwise الذي يقوم بإضافة المسار إلى العنصر redirectTo فيه. في المثال التالي، سيتم تشغيل الاستدعاء للتابع config إضافةً إلى كل الإعدادات السابقة. (يُمكنك تسجيل عددٍ غير محدودٍ من الاستدعاءات الخلفية (callbacks) للتابع config في المزوِّد (provider)) angular.module('app') .config(function($routeProvider) { $routeProvider .when('/404', { controller: 'LocationController', templateUrl: '/views/404.html' }) .otherwise({ redirectTo: '/404' }); });جّرب النقر الآن على Bad path. صحيحٌ أنّ بيئة الأمثلة لا تسمح لك بإدخال مسارٍ عشوائيّ في شريط التصفح، إلّا أنّه يمكنك تعديل "href="/bad-path في القالب السابق إلى أيّ قيمة تريدها لترى كيف سيتم التعامل معها. ربما يجب عليك دومًا إضافة معالجٍ (handler) ليتعامل مع كل الاحتمالات الباقية في إعدادات التوجيه الخاصة بك. الأحداث (Events)تُقدّم Angular تطبيقًا للرسائل من نمط (publish-subscribe) لأحداث التطبيق، وهي تعتمد على ثلاث توابع: emit$ وbroadcast$ وon$، حيث يقوم التابع emit$ و broadcast$ بتفعيل نشر أحداثٍ مخصصة، أما التابع on$ فيقوم بتسجيل معالجات (handlers) للأحداث التي تقوم بنشرها الوحدة ngRoute. فمثلًا، عندما يتمّ تغيير المسار بنجاح سينتج عن ذلك نشر الأحداث التالية: routeChangeStart$locationChangeStart$locationChangeSuccess$routeChangeSuccess$هذه الأحداث مرتبةٌ في القائمة بنفس ترتيب ظهورها أثناء دورة حياة عملية تغيير المسار، لنقُم بإثبات ذلك. تسجيل معالج للحدث (event handler)لنستخدم التابع on$ لتسجيل معالجٍ بسيط لأحداث المسار، حيث سيقوم هذا المعالج بجمع أسماء الأحداث في مصفوفة، ثم سنقوم لاحقًا بطباعة أسماء الأحداث في الصفحة بنفس الترتيب الذي حُفظت به. angular.module('app') .value('eventsLog', []) .factory('logEvent', function(eventsLog) { return function(event) { eventsLog.push(event.name); }; });في هذا المثال، سنقوم بتسجيل الأحداث أثناء انتقالنا من مسار الجذر (والمسؤول عنه هو المتحكم HomeController) إلى المسار events/ (والمسؤول عنه المتحكم EventsController). سنقوم أيضًا بجعل المتحكّم يضيف اسمه إلى القائمة أيضًا لنعرف وقت استدعاء هذا المتحكم. angular.module('app') .controller('HomeController', function($scope, $location, $rootScope, eventsLog, logEvent) { $scope.location = $location.absUrl(); $scope.link = {path: '/events', title: 'Events'}; eventsLog.push("HomeController: " + $location.path()); $rootScope.$on('$routeChangeStart', logEvent); $rootScope.$on('$locationChangeStart', logEvent); $rootScope.$on('$locationChangeSuccess', logEvent); $rootScope.$on('$routeChangeSuccess', logEvent); $scope.eventsLog = eventsLog; });يقوم المتحكم EventsController بإضافة اسمه إلى المصفوفة eventsLog ثم يقوم بكشف (expose) السجل للمجال بحيث يمكننا رؤيته في الصفحة. angular.module('app') .controller('EventsController', function($scope, eventsLog, $location) { $scope.location = $location.absUrl(); $scope.link = {path: '/', title: 'Home'}; eventsLog.push("EventsController: " + $location.path()); $scope.eventsLog = eventsLog; });نفس الواجهة ستعمل في كلا المتحكّمين. وسيتم عرض شريط انتقالٍ متغيّر إضافةً إلى محتويات السجل باستخدام التوجيه ng-repeat. <div class="well well-sm" ng-bind="location"></div> <a ng-href="{{link.path}}">{{link.title}}</a> <ol> <li ng-repeat="event in eventsLog track by $index"> {{event}} </li> </ol>كملاحظةٍ جانبية، يُعدّ المرشح json المبني في Angular مفيدًا في التنقيح وإنشاء السجلات، فإن أردنا رؤية ما هو أكثر من اسم الحدث، فيُمكننا استخدامه لعرض كامل المحتويات لكائن الحدث. سيكون علينا لإتمام المثال أن نكتب إعداداتٍ مباشرةً للمسارين. angular.module('app') .config(function($routeProvider) { $routeProvider .when('/', { controller: 'HomeController', templateUrl: '/views/events/index.html' }) .when('/events', { controller: 'EventsController', templateUrl: '/views/events/index.html' }); });انقر على الرابط في المثال السابق لرؤية الأحداث المسجلة في الصفحة. الموارد التي تتبع أسلوب RESTيُقدّم أسلوب REST في تطوير تطبيقات الويب مجموعةً من المعايير لتنظيم عمليات CRUD (إنشاء، قراءة، تحديث و حذف) على مجموعات الموارد والموارد المفردة أيضًا. لن تحتاج عند استخدامك لـ Angular أن تتّبع أسلوب REST في التوجيه إلا إذا قمت بتحميل الوحدة ngResource التي لم نقم بتغطيتها في السلسلة. على أيّ حال، سنختم هذا الفصل بالقليل من التوجيه المتّبع لأسلوب REST لتحصل على خبرةٍ في بعض الأمور العمليّة في التوجيه. سنحتاج إلى مجموعة موارد لنتمكّن من البدء، وسنعرض في الفصل التالي HTTP كيف نقوم بتحميل البيانات من مخدّمٍ في النهاية الخلفية (backend). أما الآن فسنقوم فقط بحقن مصفوفةٍ من كائناتٍ من نوع item في داخل المتحكّم. angular.module('app') .factory('Item', function(filterFilter) { var items = [ {id: 1, name: 'Item 1', color: 'red'}, {id: 2, name: 'Item 2', color: 'blue'}, {id: 3, name: 'Item 3', color: 'red'}, {id: 4, name: 'Item 4', color: 'white'} ]; return { query: function(params) { return filterFilter(items, params); }, get: function(params) { return this.query(params)[0]; } }; });لكلّ عنصرٍ في المصفوفة قيمة فريدة للخاصية id فيه، مما يسمح لنا بالتعامل معه كموردٍ منفرد. تقوم الخدمة التي يُنشئها التابع factory بتغليف الوصول إلى المصفوفة items بتابعين مفيدين هما: query لمجموعة الموارد، وget للموارد المنفردة. يستخدم التابع query المرشّح ذا الاسم سيء الحظ، filter (محقونٍ مع filterFilter) لتنفيذ جلبٍ حسب المثال باستخدام الوسيط params، أمّا التابع get فيقوم باستدعاء التابع query ليقوم بإعادة موردٍ واحد. (راجع فصل المرشحات إن كنت قد تجاوزته، لمعلوماتٍ إضافيّة عن المرشّحات في Angular.) أوّل شيءٍ نحتاجه في تطبيقٍ يتبع أسلوب REST هو دليل (index) أو مسارٌ للقائمة ليتم كشف المجموعة items كاملةً. وكلّ ما على المتحكم القيام به هو كشف كامل المحتويات للمصفوفة items باستدعاء التابع query دون وُسطاء. angular.module('app') .controller('ItemsController', function($scope, $location, Item) { $scope.location = $location.absUrl(); $scope.items = Item.query(); });واجهة هذا المتحكم سهلةٌ أيضًا، وسنستخدم التوجيه ng-repeat لإظهار العناصر. <div class="well well-sm" ng-bind="location"></div> <a ng-href="/">Home</a> <ol> <li ng-repeat="item in items"> {{item.name}} - {{item.color}} </li> </ol>في هذا المثال الأساسيّ، ستربط إعدادات التوجيه بين مسار الجذر (/) وبين واجهة المجموعة view. angular.module('app') .config(function($routeProvider) { $routeProvider .when('/', { controller: 'ItemsController', templateUrl: '/views/items/index.html' }); });لا يُحقّق المثال السّابق أسلوب REST فعلًا، وذلك لأنّ المسار ليس اسم مجموعة الموارد (items/). المشكلة هي أننا نحتاج إلى ننشئ شيئًا ما في مسار الجذر ليكون المكان الافتراضي الذي يتم تحميل تطبيق Angular منه. سنرى الحل في المثال التالي، وهو إعادة التوجيه الفوري من المسار الجذر إلى المسار items/. العنصر redirectToلنتمكّن من القيام بإعادة توجيهٍ تلقائيّة من مسارٍ لآخر، سيكون علينا استخدام العنصر redirectTo بدلًا من متحكّم وقالب. angular.module('app') .config(function($routeProvider) { $routeProvider .when('/', { redirectTo: '/items' }) .when('/items', { controller: 'ItemsController', templateUrl: '/views/items/index.html' }); });كما يُمكنك أن ترى في شريط الموضع في المثال السابق، فالمجموعة items يتم عرضها في المسار items/. ومهما كان عدد المرات التي تنقر بها على الرابط Home، فلن تتمكن من الانتقال إلى المسار الجذر / وسيتم دومًا إعادة توجيهك إلى المسار items/. هذا استخدامٌ بسيط للخيار redirectTo. إن كنت تريد تطبيق بعض العمليات على إعادة التوجيه، فيُمكنك توفير تابعٍ بدلًا من مسارٍ نصّيّ. التابع searchكما ناقشنا في بداية هذا الفصل، تقدّم روابط URL النصّية تمثيلًا لحالة التطبيق (مثل عملية ترشيحٍ للموارد) يُمكن أن يتم حفظها ومشاركتها بسهولة. كيف يُمكننا معالجة نصّ طلب (query) يطلب فقط الحصول على العناصر التي تكون قيمة العنصر color فيها هو لونًا معيّنًا؟ angular.module('app') .config(function($routeProvider) { $routeProvider .when('/', { controller: 'LocationController', template: '<div class="well well-sm" ng-bind="location"></div>\ <a ng-href="/items?color=red">Red items</a>' }) .when('/items', { controller: 'ItemsController', templateUrl: '/views/items/index.html' }); });في هذا المثال، تحتوي واجهة مسار الجذر الخاص بالتطبيق على رابط التّنقّل الحاوي على نصّ الطلب color=red. يُمكننا جلب قيمة هذا الوسيط بسهولةٍ باستخدام التابع search للخدمة location$. angular.module('app') .controller('ItemsController', function($scope, $location, Item) { $scope.location = $location.absUrl(); $scope.items = Item.query({color: $location.search().color}); });عند النّقر على الرابط Red items في المثال السابق، سترى كيف استخدم التابع query (الذي قُمنا بتعريفه سابقًا في الخدمة items) المُرشّح filter للقيام بعملية جلبٍ حسب المثال. الخدمة routeParams$يعتمد أسلوب REST نموذجيًّا على إلحاق معرّف المورد الفريد في نهاية المسار بدلًا من وضعه في نصّ الطلب. كيف يُمكننا القيام بمعالجةٍ صحيحة للمسار بحيث يحوي المعرّف الفريد؟ أو بعبارةٍ أدقّ، كيف يُمكننا استخراج المعرّف الفريد 3 من المسار items/3/ على سبيل المثال؟ لنقم بتحديث واجهة المجموعة لتتضمّن هذا الأسلوب من رابط التّنقّل لكلّ مورد مفرد على حدة. <div class="well well-sm" ng-bind="location"></div> <p class="lead">Items</p> <ol> <li ng-repeat="item in items"> <a ng-href="/items/{{item.id}}"> {{item.name}} </a> </li> </ol>تُقدّم الخدمة routeParams$ وصولًا ملائمًا للعناصر في المسار، حيث يقوم بكشفها (expose) كعناصر مُسمّاة. يُمثّل القسم الأخير من المسار المعرّف الفريد لموردٍ وحيد يتبع أسلوب REST. فمثلًا، يجب أن يعيد المسار items/3/ تمثيلًا للمورد Item بالمعرّف الفريد 3. في إعدادات التوجيه الخاصة بنا، يُمكننا استخدام البادئة الخاصّة : لتعريف متغيّراتٍ ذات أسماءٍ متغيرّة قابلةٍ للاستخراج من المسار. تمّ الاتّفاق على تسمية الوسيط المعرّف للمورد بالاسم id:، لذا سيكون نصّ المسار هو items/:id/. angular.module('app') .config(function($routeProvider) { $routeProvider .when('/', { redirectTo: '/items' }) .when('/items', { controller: 'ItemsController', templateUrl: '/views/items/linked-index.html' }) .when('/items/:id', { controller: 'ItemController', templateUrl: '/views/items/show.html' }); });تقوم الوحدة بوضع الوسطاء المستخرجة من المسار في الخدمة routeParams$ التي يجب أن نقوم بحقنها في المتحكّم ItemController جنبًا إلى جنبٍ مع الخدمة Item التي أنشأناها. angular.module('app') .controller('ItemController', function($scope, $location, Item, $routeParams) { $scope.location = $location.absUrl(); $scope.item = Item.get({id: $routeParams.id}); });سنقوم باستدعاء التّابع Item.get مستخدمين القيمة التي تمّ إسنادها في routeParams.id$، وهذا التّابع يستخدم المرشّح filter لإيجاد موقع النموذج الصحيح. (يُمكن للتابع ()Array.prototype.find الموجود في المكتبة المركزيّة أن يُقدّم طريقةً أفضل للقيام بذلك عندما ينتشر هذا التابع على نطاقٍ واسع.) طريقة عرض المورد item المفرد سهلةٌ ومباشرة، سنقوم بتضمين رابطٍ للعودة إلى المسار items/ بحيث نتمكّن من العودة إلى واجهة عرض القائمة. <div class="well well-sm" ng-bind="location"></div> <a ng-href="/items">Items</a> <p class="lead">{{item.name}}</p> <p>Color: {{item.color}}</p>صحيحٌ أنّ الخدمة routeParams$ والتابع ()location.search$ كلاهما سهل الاستخدام نوعًا ما، إلّا أنّهما معًا يقدّمان حجر بناءٍ هامًّا في أيّ عمليّة توجيه متضمّنةٍ لأسلوب REST. خاتمةتعلّمنا في هذا الفصل أساسيات طريقة Angular الخاصة بها في التوجيه. ورغم أنّ ngRoute لا تُقدّم بعض الميزات المعقّدة للتوجيه، كدعم الموارد المتداخلة، أو آلة الحالة من الدرجة الأولى، أو توليد مسارات URL، إلا أنّها تعطيك مقدّمةً ممتازةً للتوجيه في Angular. ستجد أنّ العديد من التطبيقات الحقيقية تستخدم UI-Router من مشروع AngularUI بدلًا من ذلك، ولن نتطرّق إليها في هذه السلسلة، بل سيكون الفصل الأخير مقدّمةً لتحميل البيانات من المخدّم في النهاية الخلفية (backend) باستخدام الخدمة http$ في Angular. ترجمة -وبتصرّف- للفصل الثاني عشر (Routing) من كتاب: Angular Basics لصاحبه: Chris Smith.
  2. تناول الجزء الأوّل من هذا الدّليل الخوارزميّة الّتي يتبعها خادوم ويب Nginx لاختيّار كتلة Server للإجابة على طلب العميل. سنتحدَّث في هذا المقال عن كتل Location وكيف يقرّر Nginx الكتلة الّتي ستُجيب على الطّلب. تحليل كُتَل Locationتوجد لدى Nginx آليّة لتقرير كتلة Location (الموقع) الّتي ستتولّى التّعامل مع الطّلب. تُشبه هذه الخوارزميّة في عملها خوارزميّة اختيّار كتلة Server المشروحة في الجزء الأوّل من هذا الدّليل. 1- صيّاغة Syntax كتلة Locationنبدأ، قبل شرح خوارزميّة اختيّار كتلة Location الّتي ستتولّى الإجابة على الطّلب، بشرح الصّيّاغة الّتي يستخدمها Nginx في تعريفات كتل الموقع. تُستخدَم كُتل Location الّتي تقبع ضمن كتلة Server (أو ضمن كتلة Location أخرى)، لتقرير كيف يُتعامل مع المعرّفات الكليّة للموارد Universal Resource Identifier, URI (الجزء الّذي يأتي بعد اسم النّطاق أو عنوان IP/المنفذ). تأخذ كتلة Location عمومًا الهيئة التّاليّة: location optional_modifier location_match { . . . }تُحدّد تعليمة location_match مالّذي سيُقارن به معرّف المورد الموجود في الطّلب. يُحيل optional_modifier إلى مُغيِّر اختيّاري يؤثّر على الطّريقة الّتي يبحث بها Nginx عن تطابق في كتلة Location. نميّز الحالات التّاليّة، حسب قيمة optional_modifier: لا يوجد متغيّر: إن لم تُذكَر قيمة للمغيِّر الاختيّاريّ فإنّ الموقع سيُحلّل حسب التّطابق المُبدَأ Prefix match. يعني هذا أنّ الموقع سيُقارَن ببداية معرّف المورِد (URI) في الطّلب، بحثًا عن تطابقات.=: إذا استُخدمت علامة التّساوي فإنّ الكُتلة لن تُأخذ في الحسبان إلّا إذا كانت مطابقة تمامًا لمعرّف المورِد في الطّلب.~: في هذه الحالة يُستخدَم تعبير نمطيّ Regular expression يتأثّر بحالة الأحرف (كبيرة Upper case أو صغيرة Lower case).*~: تختلف عن الحالة السّابقة في أنّ التّعبير النّمطيّ لا يتأثر بحالة الأحرف.~^: تُشير هذه القيمة إلى أنّه إن اختيرت هذه الكتلة في بوصفها أفضل تطابق دون الاعتماد على التّعابير النّمطيّة فإنّ هذه الأخيرة (أي التّعابير النّمطيّة) لن تُأخَذ بالحسبان.2- أمثلة توضّح صيّاغة كتلة Locationكتلة Location التّاليّة مثال على التّطابق المُبدَأ. ستُختار هذه الكتلة للإجابة على الطّلبات الّتي تتضمّن معرّفات الموارِد مثل site/page1/index.html/، site/ أو site/index.html/ (كلّها تبدأ ب site/): location /site { . . . }لتوضيح التّطابق التّام نأخذ الكتلة التّاليّة الّتي ستُستخدَم للإجابة على الطّالبات ذات معرّف المورد page1/. لن تُختار هذه كتلة Location هذه للإجابة على طلب بمعرّف مورد page1/index.html/. انتبه إلى أنّه في حال اختيّار هذه الكتلة وكان الطّلب يُنفَّذ باستخدام صفحة فهرس (Index page) فإنّه ستُجرى إعادة توجيه داخليّة Internal redirect إلى كتلة موقع أخرى تكون هيّ المُداول Handler الفعليّ للطّلب: location = /page1 { . . . }في الإعداد أدناه تعبيرٌ نمطيّ يتأثّر بحالة الأحرف. يُمكن أن تُستخدَم كتلة الموقع في هذا المثال للتّعامل مع الطّلبات على tortoise.jpg/ ولكنّها لا يُمكن أن تلبّي الطّلبات على FLOWER.PNG/. location ~ \.(jpe?g|png|gif|ico)$ { . . . }المثال التّالي لا يختلف عن المثال السّابق سوى في المغيّر الاختيّاريّ (~*) حيثُ إنّ التّعبير النّمطي هنا لا يتأثّر بحالة الأحرف؛ لذا يُمكن استخدام الكتلة للإجابة على كلّ من tortoise.jpg/ و FLOWER.PNG/. location ~* \.(jpe?g|png|gif|ico)$ { . . . }المثال الأخير للحالة الّتي تمنع فيها كتلة Location البحث عن تطابق عبر التّعابير النّمطيّة. تُحدّد الكتلة التّالية على أنّها أفضل تطابق لا يعتمد على التّعابير النّمطيّة للطّلبات على costumes/ninja.html/. location ^~ /costumes { . . . }رأينا أنّ المغيّرات تُحدّد كيف يجب أن تُفسَّر كتلة Location؛ إلّا أنّها لا تخبرنا عن ماهيّة الخوارزميّة الّتي يستخدمها Nginx لتقرير كتلة الموقع الّتي سيُرسِل إليها الطّلب، وهو ما سنتعرّض لها في الفقرات التّاليّة. 3- كيف يختار Nginx كتلة Location الّتي ستتعامل مع الطّلبات؟يستخدم Nginx آليّةً لاختيّار كتلة الموقِع الّتي ستُجيب على الطّلبات مُشابهةً لكيفيّة اختيّاره لكتلة الخادوم.فهم الإجراءات الّتي يتبعها Nginx لاختيّار الكتلة المناسبة أساسيّ جدًّا لتكون قادرًا على إعداد Nginx بوثوق ودقّة. يُقارن Nginx بين معرّف المورد في الطّلب وكتلة Location، مع احتساب المغيّرات الّتي ذكرناه في الفقرة السّابقة، من أجل تحديد أنسب كتلة موقع للإجابة على الطّلب؛ وفقًا للخوارزميّة التّاليّة: يبدأ Nginx بالتحقّق من كلّ المواقع الّتي لا تستعمل تعابير نمطيّة؛ فيُقارن كلّ كتلة موقع بمعرّف مورد الطّلب كاملًا. يبحث Nginx أوّلًا عن تطابق كامل. إن وُجدت كتلة Location تستخدم المغيِّر = وتُطابق تمامًا معرّف المورد في الطّلب؛ فإنّ كتلة الخادوم هذه سيقع عليها الاختيّار فورًا. إن لم يعثُر على تطابق كامل (باستخدام المغيّر =)، ينتقل Nginx إلى تقويم السّابقات (Prefixes) غير المُطابقة تمامًا؛ فيبحث عن أطول تطابق في الموقع مع معرّف المورد ثمّ يقوّمه بالطّريقة التّاليّة: إذا كانت كتلة الموقع الّتي يوجد بها أطول تطابق تستخدِم المغيّر ~^ فسيُنهي Nginx فورًا بحثَه ويختار هذه الكتلة للإجابة على الطّلب. إن لم تكُن الكتلة الّتي يوجد بها أطول تطابق تستخدِم المغيّر ~^ فسيحتفظ بها Nginx دون أن يُنهي البحث، بحيث يُمكن له اختيّارها عند الحاجة. ينتقل Nginx، بعد تحديد الكتلة ذات التّطابق الأطول والاحتفاظ بها، إلى تقويم كتل المواقع الّتي تستخدِم التّعابير النّمطيّة، سواء كانت تتأثّر بحالة الأحرف أم لا. يُقوِّم Nginx التّعابير النمطيّة بالتّسلسل، ويختار فورًا أوّل كتلة موقع يُوافق تعبيرها النّمطيّ معرّفَ مورد الطّلب. إن لم يعثُر خادوم الويب على موقع ذي تعبير نمطيّ يُوافق معرّف المورد المطلوب فإنّه يختار الكتلة المُحتفَظ بها سابقًا للإجابة على الطّلب. من المهمّ هنا أن نفهم أنّ Nginx، مبدئيًّا، يُفضّل كتل الموقع الّتي تُطابق الطّلب عبر تعابير نمطيّة؛ إلّا أنّه يبدأ بالبحث عن تطابق في كتل المواقع الّتي لا تستعمل تعابير نمطيّة، وهو ما يسمح لمسؤول خادوم الويب بتجاوز ميل Nginx إلى تفضيل التّعابير النّمطيّة عبر تحديد المغيِّرات = و~^. من المهمّ أيضًا الانتباه إلى أنّ الاختيّار في كتل المواقع الّتي تستخدِم التّطابق المُبدَأ يكون حسب التّطابق الأطول والأكثر تحديدًا غالبًا. في حين أنّ تقويم التّعابير النمطيّة (وبالتّالي الاختيّار) يتوقّف فور الحصور على تطابق وهو ما يعني أنّ ترتيب كتل المواقع الّتي تستخدم التّعابير النّمطيّة له تأثير كبير على الاختيّار. 4- متى يتجاوز تقويم كتلة الموقع إلى مواقع أخرى؟عند اختيّار كتلة Location للإجابة على طلب فإنّ التّعامل معه، ابتداءً من هذه النّقطة، يحدُث بالكامل ضمن إطار الكتلة المُختارة. فقط هذه الكتلة والتّعليمات المتفرّعة عنها هي من يُحدّد كيف يُتعامل مع الطّلب، دون تدخّل من كتل Location من نفس المستوى في البنية الشّجريّة. تسمح هذه القاعدة العامّة بتصميم كتل Location يُمكن التّنبّؤ بعملها. على الرّغم من ذلك، يجب الانتباه إلى وجود حالات تتسبّب تعليمات داخل الكتلة المُختارة في البحث من جديد عن موقع. يُمكن لهذا الاستثناء من القاعدة العامّة “كتلة موقع واحدة فقط” أن يُحدِث تأثيرات على كيفيّة الإجابة على الطّلب، بحيث يُخالف التّوقّعات الّتي كانت عندك أثناء تصميم كتل المواقع. في ما يلي بعض التّعليمات الّتي قد تؤدّي إلى إعادة توجيه من داخل الكتلة المختارة: indextry_filesrewriteserror_pageنعرض باختصار لكلّ واحدة من هذه التّعليمات. تتسبّب تعليمة index دائمًا في إعادة توجيه إن استُخدِمت للتّعامل مع الطّلب. يُستخدَم التّطابق التّام غالبًا لتسريع اختيّار كتلة الموقع عبر إنهاء الخوارزميّة فور حدوث هذا التّطابق. مع ذلك، إن استخدمت التّطابق التّامّ في حال الطّلب على مجلَّد فإنّه توجد إمكانيّة كبيرة لإعادة توجيه الطّلب إلى موقع آخر للإجابة الفعليّة عليه. تُطابق أوّل كتلة في المثال التّالي معرّف المورد exact/، إلّا أنّ تعليمة index الّتي ترثها هذه الكتلة تتسبّب في إعادة توجيه إلى الكتلة الثّانيّة: index index.html; location = /exact { . . . } location / { . . . }إذا احتجت أن يبقى تنفيذ الطّلب داخل الكتلة الأولى، في المثال أعلاه؛ فسيتطلّب ذلك منك استخدام وسيلة أخرى لتلبيّة الطّلب على المجلّد. يُمكنك مثلًا استخدامُ فهرس غير صالح لهذه الكتلة ثمّ تفعيل تعليمة autoindex: location = /exact { index nothing_will_match; autoindex on; } location / { . . . }هذه إحدى الوسائل لمنع index من تبديل السّيّاق، ولكنّها وسيلة غير ناجعة في أغلب الإعدادات. يُستخدَم التّطابق التّامّ في المجلّدات كثيرًا في بعض الأمور مثل إعادة كتابة الطّلب Request rewriting (الّذي ينتُج عنه البحث من جديد عن الموقع). قد تتسبّب تعليمة try_files هي الأخرى في إعادة تقويم عمليّة اختيّار الموقع. تطلُب هذه التّعليمة من Nginx التحقّق من وجود مجموعة مسمّاة من الملفّات أو المجلّدات؛ قد يكون المُعطى الأخير لتعليمة try_files معرّفَ مورد يُعيد Nginx التّوجيه إليه. فلنأخذ الإعداد التّالي مثالًا للشّرح: root /var/www/main; location / { try_files $uri $uri.html $uri/ /fallback/index.html; } location /fallback { root /var/www/another; }سيختار Nginx الكتلة الأولى، في المثال أعلاه، في حال الطّلب على blahblah/. تبحث الكتلة عن ملفّ blahblah في المجلّد var/www/main/، وإن لم تعثُر عليه تبحث عن ملفّ blahblah.html؛ فإن لم تجد هذا الأخير تبحث عن مجلَّد blahblah/ ضمن var/www/main/. إذا فشلت كلّ هذه المحاولات يُعيد Nginx توجيه الطّلب إلى fallback/index.html/ وهو ما يتسبّب في استدعاء كتلة موقع الأخرى، الثّانيّة في المثال. في المحصّلة فإنّ الملفّ var/www/another/fallback/index.html/ سيكون الإجابة على الطّلب. تعليمة rewrite أيضًا قد تتسبّب في تجاوز كتلة الموقع المختارة عبر الخوارزميّة المشروحة أعلاه. يبحث Nginx عند استخدام المعطى الأخير مع تعليمة rewrite أو في حالة عدم ذكر أيّ معطى على الإطلاق، يبحث عن تطابق جديد اعتمادًا على نتيجة rewrite. نُعيد استخدام المثال الأخير مع إضافة تعليمة rewrite للكتلة الأولى؛ يُمكن أن نلاحظ أنّ الطّلب يُمرَّر في بعض الحالات ماباشرةً إلى كتلة Location الثّانيّة دون تنفيذ تعليمة try_files: root /var/www/main; location / { rewrite ^/rewriteme/(.*)$ /$1 last; try_files $uri $uri.html $uri/ /fallback/index.html; } location /fallback { root /var/www/another; }تُجيب كتلة الموقع الأولى ابتداءً في المثال أعلاه على الطّلب rewriteme/hello/؛ ثمّ يأتي الطّلب لتعليمة rewrite الّتي تُعيد كتابته ليُصبح hello/ فيبدأ Nginx البحث من جديد عن موقع للإجابة على الطّلب ويعثُر من جديد على تطابق مع الكتلة الأولى ثمّ تُنفَّذ عليه تعليمة try_files وفقًا للآليّة المشروحة في مثال استخدام تعليمة try_files. أمّا إذا كان الطّلب على rewriteme/fallback/hello/ فستُطابق كتلة الموقع الأولى الطّلب ثمّ تنفَّذ عليه تعليمة rewrite الّتي تعيد كتابته فيصبح fallback/hello/ ممّا ينتُج عنه مطابقة مع كتلة الموقع الثّانيّة وبالتّالي تُجيب الطّلب. تحدُث حالة مُشابهة مع تعليمة return عند إرسال رمز الحالة Status code رقم 301 أو 302. الفرق في هذه الحالة هي أنّ النتيجة عبارة عن طلب جديد كلّيًّا على هيئة إعادة توجيه خارجيّة External redirect مرئيّة. نفس الشيء يُمكن أن يحدُث مع تعليمة rewrite عند استخدام عَلَميْ redirect و permanent. إلّا أنّ هذه الحالات يجب ألا تكون غير متوقّعة، فإعادة التّوجيه الخارجيّة المرئيّة ينتُج عنها دومًا طلب جديد. ملحوظة 1: الفرق بين إعادة التّوجيه الدّاخليّة والخارجيّة هو أنّ الأولى لا تُنهي طلب العميل الحاليّ بل تُحيله إلى مسار آخر ضمن خادوم الويب؛ أمّا الخارجيّة فتُنهي طلب العميل مع إخباره بمعرّف المورد الجديد الّذي يجب عليه إرسال طلب آخر للحصول عليه. ملحوظة 2: رموز الحالة هي أعداد يستخدمها بروتوكول HTTP ليُشعِر بالحالة الّتي انتهى عليها تنفيذ الطّلب. تُقسَّم إجابات HTTP إلى خمس مجموعات: إجابات بمعلومات، إجابات بنجاح الطّلب، إعادات توجيه، أخطاء من جانب العميل و أخطاء من جانب الخادوم. يُمكن أن تؤدّي تعليمة error_page إلى إعادة توجيه داخليّة مشابهة لتلك الّتي تُنشئها تعليمة try_files. تُستخدَم تعليمة error_page لتحديد ما يجب أن يحدُث عند تلقّي رموز حالات معيَّنة. على الأرجح لن تُنفَّذ تعليمة error_page إذا كانت تعليمة try_filesمضبوطة، فهذه الأخيرة تتعامل مع كامل دورة حياة الطّلب. فلنأخذ المثال التّاليّ: root /var/www/main; location / { error_page 404 /another/whoops.html; } location /another { root /var/www; }تستجيب الكتلة الأولى الّتي تقدّم ملفّات من المجلَّد var/www/main/ لجميع الطّلبات، ما عدا تلك الّتي تبدأ بanother/. إن لم يُعثَر على الملفّ المطلوب ضمن المجلَّد المذكور (رمز الحالة 404) فسيُعاد توجيه الطّلب إلى الملفّ another/whoops.html/ ممّا ينتُج عنه البحث عن كتلة موقع تتعامل مع الطّلب الجديد. يرسو البحث على كتلة الموقع الثّانيّة الّتي تُجيب بالملفّ var/www/another/whoops.html/. يُساعد فهمُ الظّروف الّتي تؤثّر على Nginx وتجعله يبحث عن كتلة موقع جديدة تلبّي الطّلب على توقّع سلوك Nginx عند إنشاء طلبات. خاتمةيُسهّل فهمُ الإجراءات الّتي يتبعها Nginx في الاستجابة لطلبات العملاء كثيرًا من عمل مسؤول الموقع، فيمكنك معرفة كتلة الخادوم الّتي سيختارها Nginx للإجابة حسب كلّ طلب على حدة. كما ستكون لديك القدرة على معرفة كتلة الموقع الّتي سيختارها خادوم الويب اعتمادًا على معرّف المورد المذكور في الطّلب. على العموم تُمكنّك معرفة الطّريقة الّتي يختار Nginx وفقها الكتل المختلفة من تتبّع السّيّاقات الّتي يطبّقها Nginx من أجل الإجابة على كلّ طلب. ترجمة بتصرّف لمقال Understanding Nginx Server and Location Block Selection Algorithms.
  3. يمكِن لـِ Nginx، أحد أكثر خواديم الويب انتشارًا؛ التّعاملُ بنجاح مع عملاء عدّة يتّصلون بالتّزامن، كما يُمكنه العمل بوصفه خادوم ويب، خادوم بريد أو وسيطًا عكسيًّا Reverse proxy. سنتطرّق في هذا الدّليل إلى بعض كواليس الآليّة الّتي تحدّد كيف يتعامل Nginx مع طلبات العملاء Client requests. يُساعد فهمُ هذه الآليّة في معرفة كيف تعمل إعدادات الكتلة (Block configurations) في خادوم ويب Nginx وخصوصًا كتلتَيْ Server (الخادوم) وLocation (الموقع). كما أنّه يجعل من تعامل Nginx مع طلبات العملاء أكثر قابليّةً للتّوقّع والتخمين. إعدادات كتلة Nginxيقسّم Nginx إعداداتِ تقديم المحتوى إلى كُتل تنتظِم في بنية شجريّة Hierarchical. يبدأ Nginx عندما يتلقّى طلبًا إجراءاتِ تقرير كُتَل الإعداد الّتي يجب استخدامُها للتّعامل مع الطّلب. سنتحدّث في هذا الدّليل عن آليّة التّقرير هذه. يتحدّث الجزء الأوّل من هذا الدّليل عن كتلة Server، أمّا الثّاني فيتناول كتلة Location. تعرّف كتلة Server، وهي مجموعة فرعيّة من إعدادات Nginx، خادومًا افتراضيًّا يتعامل مع طلباتٍ من نوع محدَّد. يعرّف مدراء الخواديم عادةً كتلَ خادوم عدّة ثمّ يضبطونها للتّعامل مع الطّلبات حسب اسم النّطاق Domain name، أو المنفذ Port، أو عنوان IP الذي يأتي عبره الطّلب. تقبع كتلة Location ضمن كتلة من نوع Server (كتلة خادوم)، وتُستخدَم لتعريف الكيفيّة الّتي سيتعامل بها خادوم ويب Nginx مع الطّلبات على موارد الخادوم والمعرّفات الكليّة لهذه الموارد Universal Resource Identifier, URI. يُمكن أن تُقسَّم مساحة المعرّفات الكليّة للموارد URIs بالطّريقة الّتي يراها المدير عن طريق استخدام كتل Location، فنموذج الكتل مرن للغاية. كيف يقرّر Nginx كتلة Server الّتي ستتعامل مع الطّلب؟يحتاج Nginx، ما دام يسمح بتعريف كُتَل Server عدّة تعمل على أنّها خواديم ويب افتراضيّة متفرّقة، يحتاج لوسيلة يعرف بها كتلة Server الّتي ستتولّى تلبيّة الطّلب. يُعرّف خادوم ويب Nginx نظامَ فحص يُستخدَم للعثور على كتلة Server الأكثر مُطابقةً للطّلب. يهتم Nginx أثناء عمليّة الفحص بتعليمتَيْن Directives على مستوى كتلة الخادوم، وهما listen وserver_name. 1- تحليل تعليمة listen بحثًا عن تطابقات ممكنةينظُر Nginx أوّلًا إلى عنوان IP الطّلب ومنفَذه؛ ثمّ يبحث عن تطابق بين هاتين المعلومتيْن مع محتوى تعليمة listen في كلّ كتلة خادوم. يُنشئ Nginx إثر هذه الخطوة قائمةً بكُتل الخواديم الّتي يُمكن أن تلبّي الطّلب. تعرّف تعليمة listen عنوان IP والمنفَذ الّذيْن ستُجيب كتلة الخادوم الطّلبات الآتيّة منهما. إذا لم تتضمّن كتلة الخادوم تعليمة listen فإنّ المعطيات 0.0.0.0:80 تُمنح للتّعليمة بشكل افتراضيّ (أو 0.0.0.0:8080 إذا كان مستخدم عاديّ غير المستخدم الجذر هو من يشغّل خادوم ويب Nginx). تسمح هذه المعطيات لكتلة الخادوم بالإجابة عن طلبات عبر المنفذ 80 على كلّ الواجهات Interfaces؛ إلّا أنّ المعطيات الافتراضيّة لا تمثّل ثقلًا كبيرًا في عمليّة اختيّار الخادوم. يُمكن ضبطُ تعليمة listen: بذكر عنوان IP ومنفَذ.بذكر عنوان IP فقط؛ في هذه الحالة يُستخدم المنفذ الافتراضيّ 80.بذكر منفَذ فقط؛ في هذه الحالة تستجيب لأي طلب يأتي عبر هذا المنفذ على كلّ الواجهات.بتحديد مسار إلى مقبس يونكس Unix socket.سيقتصر تأثير الخيّار الأخير - غالبًا - على الطّلبات الّتي تمرّر بين مجموعة خواديم. يقرّر Nginx كتلة الخادوم الّتي سيُرسل إليها الطّلب اعتمادًا على خصوصيّة تعليمة listen وذلك وفقًا للقواعد التّاليّة: تُكمَّل الأجزاء غير المكتملة من تعليمة listen بالقيّم الافتراضيّة. الأمثلة التّاليّة توضّح كيف تُكمّل تعليمة listen: كتلة خادوم لا توجد بها تعليمة listen ستستعمل القيمة 0.0.0.0:80.كتلة خادوم مضبوطة على العنوان 111.111.111.111 ولا تعرّف منفذًا تستعمل القيمة 111.111.111.111:80.كتلة خادوم مضبوطة على المنفذ 8888 دون عنوان IP، تستخدم القيمة 0.0.0.0:8888.يكوّن Nginx قائمة بكتل الخواديم الّتي تُطابق الطّلب، وبالتحديد عنوان IP والمنفذ. يعني هذا أنّ أيّة كتلة تستخدم عنوان 0.0.0.0 (مطابقة كلّ الواجهات) لن تُختار إن وُجدت كتلة تستخدم عناوين IP تُطابق الطّلب. في جميع الحالات يجب أن تُطابقَ الكتلة المنفذ بالضّبط. إذا وُجدت كتلة واحدة تُطابق الطّلب، ولم توجد أخرى بنفس المستوى من المُطابقة، فستُستخدَم للإجابة على الطّلب؛ أمّا إذا وُجدت عدّة كتل مع نفس المستوى من المطابقة فإنّ Nginx يبدأ بالنّظر في قيمة تعليمة server_name بالنّسبة لكلّ كتلة خادوم. يجب الانتباه إلى أنّ Nginx لن ينظُر في قيمة تعليمة server_name إلّا إذا احتاج للتّفريق بين كتل خواديم لديها تعليمة listen بنفس المستوى من المطابقة للطّلب. على سبيل المثال؛ إذا كان example.com مُستضافًا على المنفذ 80 من 192.168.1.10 فـإنّ كتلة الخادوم الأولى فقط في المثال أدناه، هيّ من سيستجيب للطّلبات الموّجّهة إلى example.com، على الرّغم من أنّ تعليمة server_name في الكتلة الثّانيّة تحوي القيمة example.com. server { listen 192.168.1.10; . . . } server { listen 80; server_name example.com; . . . }أمّا إذا وُجِدت أكثر من كتلة خادوم تُطابق بنفس المستوى من التّحديد الطّلب؛ فإنّ الخطوة التّاليّة هي فحص محتوى تعليمة server_name. 2- تحليل تعليمة server_name للاختيّار بين التّطابقاتالخطوة المواليّة، في حال وجود كتل خواديم عدّة بنفس المستوى من التّحديد توافق الطّلب، هي تحليل تعليمة server_name. يتحقّق Nginx من ترويسة Header في الطّلب تُسمّى Host (المستضيف). تحوي ترويسة Host اسم النّطاق أو عنوان IP الّذي يُريد العميل الوصول إليه. يُحاول Nginx إيجاد كتلة الخادوم الأكثر مطابقةً لترويسة Host من بين كتل الخواديم المتبقيّة بعد تحليل تعليمة listen. يتبع Nginx الوصفة التّاليّة لإيجاد الكتلة المناسبة: يبحث خادوم الويب عن كتلة خادوم تكون قيمة تعليمة server_name مطابقةً تمامًا لترويسة Host الموجودة في الطّلب. إذا عثر على تعليمة server_name يتحقّق فيها الشّرط فإنّ الكتلة المرتبطة بها هي الّتي تُستخدَم. إذا كانت هناك عدّة تعليمات مطابقة فإنّ أوّل كتلة خادوم هيّ الّتي ستُختار. إن لم يعثُر على مطابقة تامّة يبحث Nginx عن كتلة خادوم تبدأ قيمة تعليمة server_name فيها بحرف بَدَل Wildcard (يُشار إليه بعلامة * في بداية الاسم) بحيث توافق التّعليمة ترويسة المستضيف في الطّلب. إذا عثر على تعليمة server_name يتحقّق فيها الشّرط فإنّ الكتلة المرتبطة بها هي الّتي تُستخدَم. أمّا إذا كانت هناك عدّة تعليمات فإنّ التّعليمة ذات التّطابق الأطول (عدد الأحرف) هيّ الّتي ستُختار. إن لم يُعثُر على مُطابقة باستخدام حرف بدل في بداية قيمة التّعليمة فإنّ Nginx يبحث عن تعليمة server_name تنتهي بحرف بدل (يُشار إليه بعلامة * في نهاية الاسم) بحيث توافق التّعليمة ترويسة المستضيف في الطّلب. إذا عثر على تعليمة server_name يتحقّق فيها الشّرط فإنّ الكتلة المرتبطة بها هي الّتي تُستخدَم. أمّا إذا كانت هناك عدّة تعليمات فإنّ التّعليمة ذات التّطابق الأطول (عدد الأحرف) هيّ الّتي ستُختار. إن لم يعثُر Nginx على مُطابقة باستخدام حرف بدل في نهاية قيمة التّعليمة فإنّه يبحث عن كتلة خادوم تعرّف تعليمة server_name عن طريق تعبير نمطيّ Regular expression (يُشار إليه بعلامة ~ قبل الاسم). يستخدِم Nginx أوّل كتلة خادوم يُوافق تعبير تعليمة server_name النّمطيّ فيها ترويسة المستضيف. إن لم يوجد تطابُق باستخدام التّعبير النّمطيّ يختار Nginx كتلة الخادوم الافتراضّية لعنوان IP والمنفَذ المستخدَم. يوجد لكلّ ثنائيّ عنوان IP/منفذ كتلة خادوم مبدئيّة Default تُستخدَم إن لم تؤدِّ الآليّة الموصوفة أعلاه للعثور على كتلة خادوم. كتلة الثّنائيّ عنوان IP/منفذ المبدئيّة هيّ الكتلة الأولى في الإعداد أو تلك الّتي تحوي خيّار default_server ضمن تعليمة listen (يغلِب مفعول خيّار default_server، إذا كان موجودًا، مفعول الكتلة الأولى). لا يُمكن أن يوجد خيّار default_server في أكثر من مرّة بالنّسبة لكلّ عنوان IP/منفذ. نشرح في الفقرة التّاليّة كلّ نقطة من آليّة العمل أعلاه. 3- أمثلةإذا وجدت تعليمة server_name تُطابق تمامًا ترويسة المستضيف فسيقع على الكتلة المرتبطة بها الاختيّار للإجابة على الطّلب. إذا كانت ترويسة Host الموجودة في الطّلب تحوي host1.example.com فإنّ الخادوم الثّاني في المثال أدناه هو الّذي سيقع عليه الاختيّار: server { listen 80; server_name *.example.com; . . . } server { listen 80; server_name host1.example.com; . . . }إن لم يوجد تطابق كامل يبحثُ Nginx عن تعليمة server_name تبدأ بحرف بَدَل ويتحقّق من توافقها مع المستضيف في الطّلب. يختار خادوم الويب أطول قيمة في server_name من بين تلك الّتي تُطابق الطّلب. في المثال التّالي يختار Nginx كتلة الخادوم الثّانيّة إذا كان العميل يطلُب المستضيف www.example.org: server { listen 80; server_name www.example.*; . . . } server { listen 80; server_name *.example.org; . . . } server { listen 80; server_name *.org; . . . }ملحوظة: كلّ من الكتلة الثّانيّة والثّالثة توافق المستضيف في الطّلب (www.example.org)؛ إلّا أنّ قيمة server_name في الكتلة الثّانيّة أطول. تُقرأ قيمة الكتلة الثّانيّة في المثال أعلاه “جميع المستضيفات الّتي تنتهي بexample.org. أمّا الكتلة الثّالثّة فتقرأ قيمة server_name “جميع المستضيفات الّتي تنتهي بorg.. يُمكن ملاحظة أنّ قيمة server_name في كتلة الخادوم الثّانيّة أكثر تحديدًا. إذا لم يجد Nginx كتلة الخادوم المناسبة في قيّم server_name الّتي تبدأ بحرف بدل، فإنّه يبحث في كتل الخادوم الّتي تنتهي قيّم server_name فيها بحرف بدل. مثل ما يحدُث في الحالة السّابقة، يختار خادوم الويب أطول قيمة في server_name من بين تلك الّتي تُطابق الطّلب. إذا كان المستضيف المحدّد في الطّلب هو www.example.com فإنّ Nginx سيختار، في المثال التّالي، كتلة الخادوم الثّالثة. server { listen 80; server_name host1.example.com; . . . } server { listen 80; server_name example.com; . . . } server { listen 80; server_name www.example.*; . . . }ملحوظة: تُقرأ قيمة server_name في كتلة الخادوم الثّالثة في المثال أعلاه “جميع المستضيفات الّتي تبدأ ب.www.example. مجدّدًا، إذا لم يعثُر Nginx على تطابق باستخدام أحرف البدل في نهاية قيمة التّعليمة فإنّه سينتقل إلى محاولة مطابقة قيمة Host في ترويسة الطّلب بقيّم server_name المعرّفة بتعابير نمطيّة ويختار أوّل كتلة خادوم تُطابق فيها قيمة server_name قيمة Host. إذا كانت قيمة Host هي www.example.com فإنّ Nginx سيختار، في المثال التّالي، كتلة الخادوم الثّالقة للإجابة على الطّلب: server { listen 80; server_name example.com; . . . } server { listen 80; server_name ~^(www|host1).*\.example\.com$; . . . } server { listen 80; server_name ~^(subdomain|set|www|host1).*\.example\.com$; . . . }ملحوظة: يُقرأ التّعبير النّمطيّ أعلاه “يبدأ اسم المستضيف بإحدى الكلمات التّاليّة set، www، host1 أو subdomain؛ قد يتبعها حرف أو عدد غير محدّد من الحروف، ثمّ نقطة ثم كلمة example ثمّ نقطة ثمّ com.“. للمزيد حول التّعابير النّمطيّة راجع درس مقدمة في التعابير النمطية Regular Expressions. إذا لم يتمكّن Nginx من العثور على أي تعليمة server_name مُطابقة لقيمة Host عبر الإجراءات السّابقة فإنّه يختار الخادوم المبدئي الموافق للثّنائي عنوان IP/المنفَذ الّذي أتى عليه الطّلب.
×
×
  • أضف...