แอปพลิเคชันและบทช่วยสอนนี้อิงตามตัวอย่างในบทความ Quelques cadriciels Web C++ ที่เผยแพร่บน LinuxFr.org
ปัจจุบันมีภาษาและเฟรมเวิร์กที่น่าสนใจมากมายสำหรับการพัฒนาเว็บฝั่งเซิร์ฟเวอร์ ในด้านนี้ C++ ไม่ใช่ภาษาที่ทันสมัยที่สุด แต่ก็มีเนื้อหาที่น่าสนใจอยู่บ้าง อย่างแท้จริง:
วัตถุประสงค์ของบทช่วยสอนนี้คือเพื่อให้:
ซอร์สโค้ดสำหรับ Cutelyst รวมอยู่ที่นี่ ซอร์สโค้ดสำหรับเฟรมเวิร์กเว็บ C++ อื่นๆ มีอยู่ในที่เก็บ Git นี้ กรอบงานต่างๆ ที่ใช้สรุปไว้ในภาคผนวก สุดท้ายนี้ รายการไลบรารี C++ ก็มีอยู่ใน Awesome C++
เราต้องการใช้แอปพลิเคชันที่แสดงภาพสัตว์ที่เก็บไว้บนเซิร์ฟเวอร์ แบบฟอร์มใช้ระบุจุดเริ่มต้นของชื่อสัตว์ที่จะแสดง คุณสามารถแสดงภาพขนาดเต็มได้โดยคลิกที่ภาพขนาดย่อ และคุณสามารถแสดงหน้าข้อมูลผ่านลิงก์ที่ด้านล่างของหน้าได้ ข้อมูลสัตว์ (ชื่อและเส้นทางไฟล์) จะถูกจัดเก็บไว้ในฐานข้อมูล SQLite บนเซิร์ฟเวอร์
ในที่นี้ การสร้างเพจ HTML จะดำเนินการบนเซิร์ฟเวอร์ แม้ว่าแนวโน้มปัจจุบันจะค่อนข้างเป็นการจัดเตรียม API ฝั่งเซิร์ฟเวอร์และสร้าง 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 มีระบบรูปแบบขั้นสูง แต่ก็มีเครื่องมืออิสระเช่นกัน เช่น หนวด หนวดเป็นรูปแบบที่เป็นทางการที่มีการนำไปใช้ในหลายภาษา รวมถึงหลายภาษาในภาษา C++ ตัวอย่างเช่น animal-pistache/src/View.cpp ใช้การใช้งาน kainjow mustache และโค้ดต่อไปนี้ (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 (ดูที่animals-cppcms/src/Animal.cpp), tntnet (ดูที่animals-tntnet/src/Animal.cc) และsilicon (ดูที่animals-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;
}
Object-Relational Mapping (ORM) ใช้เพื่อแปลงข้อมูลจากตารางในฐานข้อมูลเป็นคลาส C++ และในทางกลับกัน ทำให้สามารถใช้ฐานข้อมูลได้อย่างปลอดภัยยิ่งขึ้น เนื่องจากข้อมูลได้รับการตรวจสอบโดยระบบการพิมพ์และตรวจสอบในเวลาคอมไพล์เนื่องจากการร้องขอถูกสร้างขึ้นโดยฟังก์ชัน C++ อย่างไรก็ตาม ORM จะกำหนดเลเยอร์นามธรรมของตัวเองที่เทียบเท่ากับ SQL แต่ก็เป็นที่รู้จักน้อยกว่า
มี ORM ของ C++ ที่แตกต่างกัน เช่น wt (ดูanimals-wt/src/main.cpp), sqlpp11 (ดูanimals-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 จะสร้างไฟล์ symbols.hh ซึ่งประกาศสัญลักษณ์ที่กำหนดโดยโปรแกรมเมอร์ รวมถึงเส้นทาง ( _about, _home, _mystatic...) ซึ่งทำให้สามารถตรวจสอบแบบคงที่ได้ว่ามีการใช้เส้นทางอย่างถูกต้องในโค้ด ภาษาอื่นใช้วิปัสสนาเพื่อตรวจสอบประเภทนี้ แต่ C ++ ไม่มีคุณสมบัตินี้
เฟรมเวิร์กแบบอะซิงโครนัส เช่น Node.js/Express ใน JavaScript มีฟังก์ชันการทำงานเหมือนกับไมโครเฟรมเวิร์กทั่วไป แต่ผ่านฟังก์ชันที่ไม่มีการบล็อก ดังนั้น หากคำขอต้องการทรัพยากร แอปพลิเคชันสามารถสลับไปยังคำขออื่นในขณะที่รอให้ทรัพยากรพร้อมใช้งาน สิ่งนี้ช่วยปรับปรุงประสิทธิภาพโดยรวมของแอปพลิเคชัน แต่ต้องใช้รูปแบบการเขียนโปรแกรมเฉพาะ โดยขึ้นอยู่กับสัญญาที่เชื่อมต่อกับฟังก์ชันการโทรกลับเพื่อสร้างห่วงโซ่ของการประมวลผลแบบอะซิงโครนัส
มีเฟรมเวิร์กแบบอะซิงโครนัสที่แตกต่างกันใน C ++ เช่น cpprestsdk (ดูที่สัตว์ - cpprestsdk) และพิสตาชิโอ (ดูที่สัตว์ - พิสตาชิโอ)
ที่นี่เราพบการจัดการเส้นทางแบบคลาสสิก (พร้อมชื่อเส้นทางและฟังก์ชันการประมวลผล) อย่างไรก็ตาม ขณะนี้เรามีการดำเนินการแบบอะซิงโครนัสผ่านฟังก์ชันที่ไม่มีการบล็อก ตัวอย่างเช่น สำหรับเส้นทาง "คงที่" ฟังก์ชัน serverFile ส่งคืนสัญญาที่เชื่อมต่อกับฟังก์ชันโทรกลับ ซึ่งจะแสดงข้อความบันทึกเมื่อสัญญาได้รับการแก้ไขแล้ว
เว็บเฟรมเวิร์ก 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 เหล่านี้สอดคล้องกับอินเทอร์เฟซแบบกราฟิกทั่วไป แต่มีสถาปัตยกรรมไคลเอ็นต์-เซิร์ฟเวอร์
สำหรับแอปพลิเคชันที่ซับซ้อนมากขึ้น เช่น หน้าที่แสดงสัตว์ เราสามารถกำหนดวิดเจ็ตใหม่ที่ใช้ภาพขนาดย่อ จากนั้นใช้คลาสนี้เพื่อแสดงสัตว์ทั้งหมดที่อ่านในฐานข้อมูล
เมื่อมองแวบแรก การใช้งานนี้อาจดูยาวนานและซับซ้อนกว่าการใช้งานครั้งก่อน อย่างไรก็ตาม โค้ดของมันควรจะดูคุ้นเคยสำหรับนักพัฒนา GUI บนเดสก์ท็อป นอกจากนี้ การใช้งานนี้จะจัดการแอปพลิเคชันทั้งหมด (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: อีกา (เฟรมเวิร์กเว็บน้ำหนักเบา) | อีกา (ระบบเทมเพลต) | sqlpp11 (ORM) |
สัตว์น่ารักที่สุด | cutelyst (กรอบเว็บ) | ผู้ให้สิทธิ์ (ระบบเทมเพลต) | น่ารักที่สุด (ตัวเชื่อมต่อ SQL) |
สัตว์ nodejs (Javascript/Node.js) | ด่วน (เฟรมเวิร์กเว็บน้ำหนักเบาแบบอะซิงโครนัส) | ปั๊ก (โปรแกรมสร้างเอกสาร) | ดีกว่า-sqlite3 (ตัวเชื่อมต่อ SQL) |
สัตว์พิสตาเช่ | pistache (เฟรมเวิร์กเว็บน้ำหนักเบาแบบอะซิงโครนัส) | หนวดกะเหรี่ยง (ระบบแม่แบบ) | sqlite_modern_cpp (ตัวเชื่อมต่อ SQL) |
สัตว์สกอตติช (Haskell) | scotty (เฟรมเวิร์กเว็บแบบไลท์เวท) | สุวิมลและดินเหนียว (เครื่องกำเนิดเอกสาร) | sqlite แบบง่าย (ตัวเชื่อมต่อ SQL) |
สัตว์-ซิลิคอน | ซิลิคอน (กรอบเว็บน้ำหนักเบา) | ไม่มี | ซิลิคอน (ตัวเชื่อมต่อ SQL) |
สัตว์-tntnet | tntnet (เฟรมเวิร์กเว็บที่ใช้เทมเพลต) | tntnet (ระบบเทมเพลต) | tntnet (ตัวเชื่อมต่อ SQL) |
สัตว์น้ำหนัก | wt (กรอบงานเว็บ GUI) | wt (ระบบวิดเจ็ต + เทมเพลต) | น้ำหนัก (ORM) |