يعتمد هذا التطبيق والبرنامج التعليمي على المثال الوارد في مقالة Quelques cadriciels Web C++ المنشورة على LinuxFr.org.
يوجد حاليًا العديد من اللغات والأطر المثيرة للاهتمام لتطوير الويب من جانب الخادم. في هذا المجال، C++ ليست اللغة الأكثر شيوعًا، ولكنها تحتوي على بعض الأصول المثيرة للاهتمام. بالفعل:
الغرض من هذا البرنامج التعليمي هو توفير:
يتم تضمين رموز المصدر لـ Cutelyst هنا. تتوفر أكواد المصدر لأطر عمل الويب C++ الأخرى في مستودع Git هذا. تم تلخيص الأطر المختلفة المستخدمة في الملحق. أخيرًا، تتوفر قائمة بمكتبات C++ على Awesome C++.
نريد تنفيذ تطبيق يعرض صور الحيوانات المخزنة على الخادم. يتم استخدام نموذج للإشارة إلى بداية اسم الحيوانات المراد عرضها. يمكنك عرض الصورة بالحجم الكامل من خلال الضغط على الصورة المصغرة ويمكنك عرض صفحة المعلومات عبر رابط في أسفل الصفحة. يتم تخزين بيانات الحيوانات (الأسماء ومسارات الملفات) في قاعدة بيانات SQLite على الخادم.
هنا، يتم إنشاء صفحات HTML على الخادم، على الرغم من أن الاتجاه الحالي هو بالأحرى توفير واجهة برمجة التطبيقات من جانب الخادم وإنشاء HTML من جانب العميل.
بطريقة تقليدية للغاية، يمكن للمرء تنظيم كود هذا التطبيق وفقًا لبنية من نوع MVC، أي من خلال التمييز بين البيانات (النموذج)، وعرضها (العرض)، وإدارتها (وحدة التحكم).
بالنسبة لتطبيقنا، الصور متاحة على الخادم ونستخدم قاعدة بيانات SQLite التي تحتوي على جدول بأسماء ومسارات ملفات الحيوانات. ملف animals.sql
:
CREATE TABLE animals (
id INTEGER PRIMARY KEY ,
name TEXT ,
image TEXT
);
INSERT INTO animals (name, image) VALUES ( ' dolphin ' , ' dolphin-marine-mammals-water-sea-64219.jpg ' );
INSERT INTO animals (name, image) VALUES ( ' dog ' , ' night-garden-yellow-animal.jpg ' );
INSERT INTO animals (name, image) VALUES ( ' owl ' , ' owl.jpg ' );
...
يأتي جزء النموذج بعد ذلك إلى نوع Animal ووظيفة getAnimals التي تستعلم عن قاعدة البيانات وترجع سجلات النوع Animal التي يبدأ اسمها بالبادئة المحددة. ملف Animal.hpp:
يحتوي جزء العرض على وظيفتين لإرجاع الصفحات بتنسيق HTML: يقوم renderAbout بإرجاع صفحة المعلومات ويقوم renderHome بإرجاع الصفحة الرئيسية بالحيوانات التي طلبها المستخدم. عرض الملف.hpp:
وأخيرًا، يقوم جزء التحكم باسترداد أحداث العميل ثم يقوم بتحديث النموذج وطريقة العرض. بالنسبة لتطبيقنا، لا توجد معالجة معقدة، فقط لاسترداد طلبات HTTP واستدعاء الوظائف السابقة.
لا يبدو أن لغة C++ تمتلك أدوات ناجحة لإنشاء مستندات HTML مثل Lucid في Haskell. يتم استخدام مكتبة CTML لتحديد البنية الشجرية للمستند ثم إنشاء كود HTML المقابل. ومع ذلك، فإن بناء الجملة مطول تمامًا ولا يوجد تحقق من العلامات.
تتكون هذه الأنظمة من كتابة قوالب قابلة للتخصيص، أي كود HTML الذي يتم فيه استخدام المعلمات والتي سيتم استبدالها بالقيم المشار إليها عند عرض القالب.
تقدم أطر عمل MVC عادةً أنظمة أنماط متقدمة، ولكن هناك أيضًا أدوات مستقلة، على سبيل المثال الشارب. Moustache هي صيغة شكلية لها تطبيقات في العديد من اللغات، بما في ذلك العديد من لغات C++. على سبيل المثال، يستخدم Animal-pistache/src/View.cpp تطبيق kainjow moustache والكود التالي ( Animals-crow/src/View.cpp) لتنفيذ إطار عمل الغراب:
const string css = ...
string renderHome ( const string & myquery, const vector<Animal> & animals) {
// create the template
const string homeTmpl = R"(
<html>
<head>
<style>
{{mycss}}
</style>
</head>
<body>
<h1>Animals (Crow)</h1>
<form>
<p> <input type="text" name="myquery" value="{{myquery}}"> </p>
</form>
{{#animals}}
<a href="static/{{image}}">
<div class="divCss">
<p> {{name}} </p>
<img class="imgCss" src="static/{{image}}" />
</div>
</a>
{{/animals}}
<p style="clear: both"><a href="/about">About</a></p>
</body>
</html>
)" ;
// create a context containing the data to use in the template
crow::mustache::context ctx;
ctx[ " mycss " ] = css;
ctx[ " myquery " ] = myquery;
for ( unsigned i= 0 ; i<animals. size (); i++) {
ctx[ " animals " ][i][ " name " ] = animals[i]. name ;
ctx[ " animals " ][i][ " image " ] = animals[i]. image ;
}
// render the template using the context
return crow::mustache::template_t (homeTmpl). render (ctx);
}
string renderAbout () {
...
}
من السهل نسبيًا أيضًا إنشاء كود HTML يدويًا، باستخدام تدفقات قناة C++. ومع ذلك، لا تسهل هذه الطريقة إعادة استخدام التعليمات البرمجية أو التحقق من تعليمات HTML البرمجية المنتجة. مثال على التوليد اليدوي ( Animals-silicon/src/main.cpp ):
string renderHome ( const string & myquery, const vector<Animal> & animals) {
// create a string stream
ostringstream oss;
// generate some HTML code, in the stream
oss << R"(
<html>
<head>
<link rel="stylesheet" type="text/css" href="mystatic/style.css">
</head>
<body>
<h1>Animals (Silicon)</h1>
<form>
<p> <input type="text" name="myquery" value=" )" << myquery << R"( "> </p>
</form>
)" ;
for ( const Animal & a : animals) {
oss << R"(
<a href="mystatic/ )" << a. image << R"( ">
<div class="divCss">
<p> )" << a. name << R"( </p>
<img class="imgCss" src="mystatic/ )" << a. image << R"( " />
</div>
</a> )" ;
}
oss << R"(
<p style="clear: both"><a href="/about">About</a></p>
</body>
</html>
)" ;
// return the resulting string
return oss. str ();
}
string renderAbout () {
...
}
إنها تجعل من الممكن إنشاء استعلامات SQL بشكل صريح وإرسالها إلى نظام قاعدة البيانات واسترداد النتيجة. تعتبر موصلات SQL سهلة الاستخدام بشكل عام (فقط تعرف لغة SQL) ولكنها لا تتحقق من صحة الاستعلامات.
توفر العديد من الأطر موصلات SQL. على سبيل المثال، cppcms (انظر الحيوانات-cppcms/src/Animal.cpp)، وtntnet (انظر الحيوانات-tntnet/src/Animal.cc) والسيليكون (انظر الحيوانات-silicon/src/main.cpp). هناك أيضًا موصلات مستقلة، على سبيل المثال sqlite_modern_cpp (راجع Animals-pistache/src/Animal.cpp):
# include " Animal.hpp "
# include < sqlite_modern_cpp.h >
using namespace sqlite ;
using namespace std ;
vector<Animal> getAnimals ( const string & myquery) {
vector<Animal> animals;
try {
// open database
database db ( " animals.db " );
// query database and process results
db << " SELECT name,image FROM animals WHERE name LIKE ?||'%' "
<< myquery
>> [&](string name, string image) { animals. push_back ({name, image}); };
}
catch ( exception & e) {
cerr << e. what () << endl;
}
return animals;
}
يتم استخدام التعيين العلائقي للكائنات (ORM) لتحويل البيانات من جدول في قاعدة بيانات إلى فئة C++، والعكس صحيح. وهذا يجعل من الممكن استخدام قاعدة البيانات بشكل أكثر أمانًا لأنه يتم فحص البيانات بواسطة نظام الكتابة ويتم فحصها في وقت الترجمة نظرًا لأن الطلبات تتم بواسطة وظائف C++. ومع ذلك، يقوم ORM بتعريف طبقة التجريد الخاصة به والتي تعادل SQL، ولكنها بالضرورة أقل شهرة.
هناك أنظمة ORM مختلفة لـ C++، على سبيل المثال wt (انظر Animals-wt/src/main.cpp)، أو sqlpp11 (انظر Animal-crow/src/Animal.cpp) أو sqlite_orm (انظر Animals-cpprestsdk/src/Animal.cpp):
# include " Animal.hpp "
# include < sqlite_orm/sqlite_orm.h >
using namespace std ;
using namespace sqlite_orm ;
vector<Animal> getAnimals ( const string & myquery) {
vector<Animal> animals;
// open database and map the "animals" table to the "Animal" datatype
auto storage = make_storage (
" animals.db " ,
make_table ( " animals " ,
make_column ( " name " , &Animal::name),
make_column ( " image " , &Animal::image)));
// query database
auto results = storage. get_all <Animal>( where ( like (&Animal::name, myquery+ " % " )));
// process results
for ( auto & animal : results)
animals. push_back (animal);
return animals;
}
تهدف أطر عمل الويب الصغيرة، مثل Sinatra في Ruby أو Flask في Python، إلى أن تكون بسيطة وخفيفة. إنها توفر بشكل أساسي ميزات للتعامل مع طلبات HTTP بالإضافة إلى آلية توجيه URL. إذا لزم الأمر، يمكن إكمالها بواسطة مكتبات أخرى (إنشاء HTML، الوصول إلى قاعدة بيانات في SQL...).
هناك العديد من الأطر الدقيقة لـ C++، على سبيل المثال الغراب (انظر الحيوانات-الغراب) أو السيليكون (انظر الحيوانات-السيليكون).
هنا، تجعل ميزات C++ الحديثة التعليمات البرمجية موجزة وممتعة إلى حد ما للقراءة.
في مرحلة ما قبل المعالجة، يقوم Silicon بإنشاء ملف الرموز.hh، الذي يعلن عن الرموز المحددة بواسطة المبرمج، بما في ذلك المسارات ( _about، _home، _mystatic...). وهذا يجعل من الممكن التحقق بشكل ثابت من استخدام المسارات بشكل صحيح في التعليمات البرمجية. تستخدم اللغات الأخرى الاستبطان لإجراء هذا النوع من التحقق، لكن لغة C++ لا تحتوي على هذه الميزة.
توفر الأطر غير المتزامنة، مثل Node.js/Express في JavaScript، نفس الوظائف التي توفرها الأطر الصغيرة التقليدية ولكن عبر وظائف غير محظورة. وبالتالي، إذا كان الطلب يحتاج إلى مورد، فيمكن للتطبيق التبديل إلى طلب آخر أثناء انتظار توفر المورد. يؤدي هذا إلى تحسين الأداء العام للتطبيق ولكنه يتطلب أسلوب برمجة معينًا، استنادًا إلى الوعود المرتبطة بوظائف رد الاتصال بحلول ذلك الوقت لتشكيل سلسلة من المعالجة غير المتزامنة.
هناك أطر عمل غير متزامنة مختلفة في لغة C++، على سبيل المثال cpprestsdk (انظر الحيوانات-cpprestsdk) والفستق (انظر الحيوانات-الفستق).
نجد هنا إدارة كلاسيكية للمسار (مع اسم المسار ووظيفة المعالجة الخاصة به). ومع ذلك، لدينا الآن عملية غير متزامنة، عبر وظائف غير محظورة. على سبيل المثال، بالنسبة للمسار "الثابت"، تقوم الدالةservFile بإرجاع وعد متصل بوظيفة رد الاتصال، والتي تعرض رسالة سجل بمجرد حل الوعد.
تعد أطر عمل الويب MVC، مثل Ruby on Rails أو Python Django من الأدوات الكلاسيكية التي تهدف إلى تنفيذ أي نوع من تطبيقات الويب. عادةً ما توفر جميع الميزات الضرورية: توجيه URL ونظام القالب والوصول إلى قواعد البيانات ونظام المصادقة... لا يبدو أن أطر عمل MVC هي المجال المفضل لـ C++، ولكن لا تزال هناك بعض الأدوات المثيرة للاهتمام، بما في ذلك cppcms amd Cutelyst .
بالإضافة إلى الميزات الكلاسيكية لإطار عمل MVC، يقدم cppcms نظام قوالب متقدمًا إلى حد ما مع توريث العرض وإدارة المحتوى. على سبيل المثال، يمكن تحديد طريقة العرض الرئيسية MasterView واستخلاص طرق العرض منها. يرث AboutView وHomeView خصائص MasterView ويكملانها. أخيرًا، يمكننا ربط المحتوى بهذه المشاهدات (معلمات القوالب)، وأيضًا بنظام الميراث. باستخدام المثال السابق، يمكننا تحديد المحتوى MasterContent للعرض MasterView، واشتقاقه HomeContent للعرض HomeView واستخدام MasterContent مباشرة للعرض AboutView (لا توجد معلمة جديدة في القالب).
تعد أطر عمل MVC أدوات فعالة لتنفيذ التطبيقات المعقدة. ومع ذلك، فهي تتطلب الكثير من التدريب ويمكن أن تكون كبيرة الحجم لتطبيقات صغيرة وبسيطة.
يقدم إطار عمل tntnet نظامًا قائمًا على القالب، مشابهًا لـ PHP. حتى لو كان هذا الإطار عبارة عن قصصية إلى حد ما في النظام البيئي لـ C++، فإنه يبدو فعالًا إلى حد ما في منهجه: كتابة كود HTML الكلاسيكي وإضافة أقسام من كود C++ حيثما كان ذلك ضروريًا.
لاحظ أن هذا النوع من إطار العمل ربما يكون أقل ملاءمة لتطوير التطبيقات المعقدة (سهولة قراءة القوالب، وإعادة الاستخدام...).
هذه الأدوات مستوحاة من الأطر الرسومية لسطح المكتب، مثل Qt أو gtkmm، والتي تعتمد على تسلسل هرمي من عناصر واجهة المستخدم التي تشكل الواجهة وتتفاعل عبر آلية فتحة الإشارة.
من المثير للدهشة أن الأدوات المستندة إلى الويب لا تحظى بشعبية كبيرة، حتى في جميع اللغات، في حين أن إمكاناتها تبدو مهمة. في الواقع، فهي تسمح بتطوير تطبيق متكامل لخادم العميل باستخدام مكتبة واجهة رسومية كلاسيكية ودون الحاجة إلى القلق كثيرًا بشأن بنية الشبكة الخاصة بالتطبيق.
في C++، إطار العمل الأكثر نجاحًا في هذه الفئة هو بالتأكيد Wt. يحتوي Wt على العديد من الأدوات الكلاسيكية أو المتقدمة، وSQL ORM، ونظام المصادقة، والقدرة على التعامل مع HTML وCSS، وما إلى ذلك. في Wt، البرنامج الرئيسي هو توجيه عناوين URL إلى التطبيقات المقابلة.
تتوافق تطبيقات Wt هذه مع الواجهات الرسومية التقليدية، ولكن مع بنية خادم العميل.
بالنسبة لتطبيق أكثر تعقيدًا، على سبيل المثال الصفحة التي تعرض الحيوانات، يمكننا تحديد عنصر واجهة مستخدم جديد يقوم بتنفيذ صورة مصغرة، ثم استخدام هذه الفئة لعرض جميع الحيوانات المقروءة في قاعدة البيانات.
للوهلة الأولى، قد يبدو هذا التنفيذ أطول وأكثر تعقيدًا من التطبيقات السابقة. ومع ذلك، يجب أن يبدو الكود الخاص به مألوفًا لأي مطور لواجهة المستخدم الرسومية لسطح المكتب. بالإضافة إلى ذلك، يقوم هذا التنفيذ بإدارة التطبيق بأكمله (fullstack)، وليس جزء الخادم فقط. على سبيل المثال، توصيل الإشارة _myquery->textInput()
إلى وظيفة HomeApp::filterAnimals
يتضمن تحديثات من جانب العميل في الوقت الفعلي، والتي سيكون تنفيذها أكثر صعوبة مع الأطر السابقة.
لتطوير تطبيقات الويب الخلفية، يعد C++ خيارًا ممكنًا للغاية. ومع أحدث التطورات، أصبحت اللغة بشكل عام أبسط وأكثر أمانًا في الاستخدام، دون المساس بالأداء. تتوفر العديد من مكتبات C++ لتطوير الويب: القوالب، وإنشاء HTML، واتصال SQL، وORM... كما أن أطر الويب عديدة ومتنوعة: أطر عمل MVC مثل RoR وDjango، وأطر عمل صغيرة مثل Sinatra وFlask، وأطر عمل غير متزامنة مثل Node.js والأطر المستندة إلى قوالب PHP، وحتى أطر العمل الكاملة المستندة إلى عناصر واجهة المستخدم. بالطبع، كل هذا سيثير اهتمام المطورين الذين يعرفون لغة C++ بالفعل، لأن العديد من اللغات الأخرى لديها أيضًا أدوات مثيرة جدًا لتطوير الويب.
الأطر والمكتبات والأدوات التالية المستخدمة لتنفيذ خادم HTTP وإنشاء HTML والوصول إلى قاعدة بيانات SQL.
مشروع | إطار الويب | مولد HTML | واجهة SQL |
---|---|---|---|
الحيوانات-cppcms | Cppcms (إطار عمل الويب) | cppcms (نظام القوالب) | cppcms (موصل SQL) |
الحيوانات cpprestsdk | cpprestsdk (إطار عمل الشبكات غير المتزامنة) | ctml (مولد مستندات HTML) | sqlite_orm (ORM) |
الحيوانات الغراب | http:crow (إطار ويب خفيف الوزن) | الغراب (نظام القوالب) | sqlpp11 (ORM) |
الحيوانات لطيف | كيوتليست (إطار الويب) | جرانتلي (نظام القوالب) | لطيف (موصل SQL) |
عقدة الحيوانات (Javascript/Node.js) | صريح (إطار ويب خفيف الوزن غير متزامن) | الصلصال (مولد المستندات) | أفضل sqlite3 (موصل SQL) |
حيوانات الفستق | الفستق (إطار ويب خفيف الوزن غير متزامن) | شارب كينجو (نظام القوالب) | sqlite_modern_cpp (موصل SQL) |
حيوانات سكوتي (هاسكل) | سكوتي (إطار ويب خفيف الوزن) | الوضوح والطين (مولدات المستندات) | SQLite-بسيط (موصل SQL) |
الحيوانات-السيليكون | السيليكون (إطار ويب خفيف الوزن) | لا أحد | السيليكون (موصل SQL) |
الحيوانات-tntnet | Tntnet (إطار عمل الويب القائم على القالب) | تي ان تي نت (نظام القوالب) | تي ان تي نت (موصل SQL) |
الحيوانات بالوزن | وزن (إطار عمل واجهة المستخدم الرسومية على الويب) | بالوزن (نظام القطعة + قوالب) | بالوزن (ORM) |