C++ Webprogrammering met CGI: Volledige gids
Wat is CGI?
De Common Gateway Interface (CGI) is een set standaarden die definieert hoe informatie wordt uitgewisseld tussen de webserver en een aangepast script.
De CGI-specificaties worden beheerd door de NCSA. NCSA definieert CGI als een standaard voor externe gateway-programma's om te communiceren met informatieservers zoals HTTP-servers.
De huidige versie is CGI/1.1; CGI/1.2 is in ontwikkeling.
Web Browsen
Om CGI te begrijpen, kijken we wat er gebeurt wanneer we op een hyperlink klikken om een webpagina of URL op te vragen.
Uw browser contacteert de HTTP-webserver en vraagt de URL (bestandsnaam) op.
De webserver parseert de URL en zoekt het bestand. Als het gevonden wordt, stuurt de server het bestand terug; anders stuurt hij een foutmelding.
De browser toont het ontvangen bestand of de foutmelding.
Het is mogelijk de HTTP-server zo te configureren dat bestanden in een bepaalde map niet direct worden teruggestuurd, maar als programma worden uitgevoerd. De output van dat programma wordt dan naar de browser gestuurd.
CGI is een standaardprotocol voor het laten communiceren applicaties (CGI-programma's of -scripts) met webservers en clients. Deze programma's kunnen in Python, PERL, Shell, C, C++ etc. geschreven zijn.
CGI-architectuurdiagram
Het volgende eenvoudige diagram toont de CGI-architectuur:
Webserverconfiguratie
Voordat u met CGI-programmering begint, zorg ervoor dat uw webserver CGI ondersteunt en geconfigureerd is om CGI-programma's uit te voeren. Alle CGI-programma's worden in een vooraf geconfigureerde map bewaard, de CGI-directory, conventioneel /var/www/cgi-bin. CGI-bestanden hebben conventioneel de extensie .cgi, ook al zijn het C++-uitvoerbare bestanden.
Standaard is Apache geconfigureerd om CGI-programma's in /var/www/cgi-bin uit te voeren. Om een andere map op te geven, wijzigt u de httpd.conf:
<Directory "/var/www/cgi-bin"> AllowOverride None Options ExecCGI Order allow,deny Allow from all </Directory> <Directory "/var/www/cgi-bin"> Options All </Directory>
Ervan uitgaande dat uw webserver draait en u andere CGI-programma's (Perl, Shell) kunt uitvoeren.
Eerste CGI-programma
Hieronder een eenvoudig C++ CGI-programma:
#include <iostream>
using namespace std;
int main() {
cout << "Content-type:text/html\r\n\r\n";
cout << "<html>\n";
cout << "<head>\n";
cout << "<title>Hello World - First CGI Program</title>\n";
cout << "</head>\n";
cout << "<body>\n";
cout << "<h2>Hello World! This is my first CGI program</h2>\n";
cout << "</body>\n";
cout << "</html>\n";
return 0;
}
Compileer de code en noem het uitvoerbare bestand cplusplus.cgi. Plaats het in /var/www/cgi-bin en maak het uitvoerbaar met chmod 755 cplusplus.cgi.
Mijn eerste CGI-programma
Het programma schrijft naar STDOUT (scherm). De cruciale eerste regel Content-type:text/html\r\n\r\n wordt naar de browser gestuurd en specificeert het content-type. Nu kunt u complexe CGI-programma's schrijven, bijvoorbeeld met interactie met een RDBMS.
HTTP-header
De regel Content-type:text/html\r\n\r\n maakt deel uit van de HTTP-header. Alle HTTP-headers hebben de vorm:
HTTP-veldnaam: Veldinhoud Bijvoorbeeld: Content-type: text/html\r\n\r\n
Andere veelgebruikte HTTP-headers:
| Nr. | Header & Beschrijving |
|---|---|
| 1 | Content-type: Een MIME-string die het formaat van het teruggegeven bestand definieert. Voorbeeld: |
| 2 | Expires: Datum De datum waarop de informatie ongeldig wordt. Formaat: |
| 3 | Location: URL De URL die geretourneerd moet worden in plaats van de aangevraagde URL. Kan gebruikt worden voor redirect. |
| 4 | Last-modified: Datum De datum van de laatste wijziging van de resource. |
| 5 | Content-length: N De lengte in bytes van de teruggegeven data. De browser gebruikt dit voor geschatte downloadtijd. |
| 6 | Set-Cookie: String Stelt een cookie in via de string. |
CGI-omgevingsvariabelen
Alle CGI-programma's hebben toegang tot de volgende omgevingsvariabelen, die essentieel zijn bij het schrijven van CGI-programma's.
| Nr. | Variabelenaam & Beschrijving |
|---|---|
| 1 | CONTENT_TYPE Het datatype van de inhoud, gebruikt wanneer de client bijgevoegde inhoud stuurt (bijv. bestandsupload). |
| 2 | CONTENT_LENGTH De lengte van de query-informatie, alleen beschikbaar voor POST-verzoeken. |
| 3 | HTTP_COOKIE Retourneert de ingestelde cookies als sleutel-waardeparen. |
| 4 | HTTP_USER_AGENT Bevat informatie over de user agent (browser). |
| 5 | PATH_INFO Het pad voor het CGI-script. |
| 6 | QUERY_STRING De URL-gecodeerde informatie die met de GET-methode wordt verstuurd. |
| 7 | REMOTE_ADDR Het IP-adres van de remote host die het verzoek doet. Nuttig voor logging of authenticatie. |
| 8 | REMOTE_HOST De volledig gekwalificeerde hostnaam van de requester. Als niet beschikbaar, kan REMOTE_ADDR gebruikt worden. |
| 9 | REQUEST_METHOD De methode van het verzoek (GET, POST, etc.). |
| 10 | SCRIPT_FILENAME Het volledige pad naar het CGI-script. |
| 11 | SCRIPT_NAME De naam van het CGI-script. |
| 12 | SERVER_NAME De hostnaam of het IP-adres van de server. |
| 13 | SERVER_SOFTWARE De naam en versie van de serversoftware. |
Klein CGI-programma om alle CGI-variabelen te tonen:
#include <iostream>
#include <stdlib.h>
using namespace std;
const string ENV[24] = {
"COMSPEC", "DOCUMENT_ROOT", "GATEWAY_INTERFACE", "HTTP_ACCEPT",
"HTTP_ACCEPT_ENCODING", "HTTP_ACCEPT_LANGUAGE", "HTTP_CONNECTION",
"HTTP_HOST", "HTTP_USER_AGENT", "PATH", "QUERY_STRING", "REMOTE_ADDR",
"REMOTE_PORT", "REQUEST_METHOD", "REQUEST_URI", "SCRIPT_FILENAME",
"SCRIPT_NAME", "SERVER_ADDR", "SERVER_ADMIN", "SERVER_NAME",
"SERVER_PORT", "SERVER_PROTOCOL", "SERVER_SIGNATURE", "SERVER_SOFTWARE"
};
int main() {
cout << "Content-type:text/html\r\n\r\n";
cout << "<html>\n<head>\n<title>CGI Environment Variables</title>\n</head>\n<body>\n";
cout << "<table border=\"0\" cellspacing=\"2\">";
for(int i = 0; i < 24; i++) {
cout << "<tr><td>" << ENV[i] << "</td><td>";
char *value = getenv(ENV[i].c_str());
if(value != 0) cout << value;
else cout << "Environment variable does not exist.";
cout << "</td></tr>\n";
}
cout << "</table><\n";
cout << "</body>\n</html>\n";
return 0;
}
C++ CGI-bibliotheek
Voor echte toepassingen hebt u een CGI-bibliotheek nodig. Er is een C++ CGI-bibliotheek beschikbaar op ftp://ftp.gnu.org/gnu/cgicc/. Installatiestappen:
$ tar xzf cgicc-X.X.X.tar.gz $ cd cgicc-X.X.X/ $ ./configure --prefix=/usr $ make $ make install
Documentatie vindt u bij 'C++ CGI Lib Documentation'.
GET- en POST-methodes
Browsers gebruiken twee methoden om informatie door te geven aan de webserver en uiteindelijk aan uw CGI-programma: GET en POST.
Informatie doorgeven met GET
De GET-methode stuurt de gecodeerde gebruikersinformatie aangehecht aan de paginaverzoek, gescheiden door een ?:
https://www.test.com/cgi-bin/cpp.cgi?key1=value1&key2=value2
GET is de standaardmethode, produceert een lange string in de adresbalk, heeft een limiet van ~1024 tekens en is ongeschikt voor gevoelige data (wachtwoorden). De informatie komt via de QUERY_STRING HTTP-header en is beschikbaar via de omgevingsvariabele QUERY_STRING.
Eenvoudig URL-voorbeeld: GET
URL die twee waarden doorgeeft aan cpp_get.cgi:
/cgi-bin/cpp_get.cgi?first_name=ZARA&last_name=ALI
C++ CGI-programma met de cgicc-bibliotheek:
#include <iostream>
#include <vector>
#include <string>
#include <stdio.h>
#include <stdlib.h>
#include <cgicc/CgiDefs.h>
#include <cgicc/Cgicc.h>
#include <cgicc/HTTPHTMLHeader.h>
#include <cgicc/HTMLClasses.h>
using namespace std;
using namespace cgicc;
int main() {
Cgicc formData;
cout << "Content-type:text/html\r\n\r\n";
cout << "<html>\n<head>\n<title>Using GET and POST Methods</title>\n</head>\n<body>\n";
form_iterator fi = formData.getElement("first_name");
if(!fi->isEmpty() && fi != (*formData).end()) {
cout << "First name: " << **fi << endl;
} else {
cout << "No text entered for first name" << endl;
}
cout << "<br/>\n";
fi = formData.getElement("last_name");
if(!fi->isEmpty() && fi != (*formData).end()) {
cout << "Last name: " << **fi << endl;
} else {
cout << "No text entered for last name" << endl;
}
cout << "<br/>\n</body>\n</html>\n";
return 0;
}
Compileren:
$ g++ -o cpp_get.cgi cpp_get.cpp -lcgicc
Plaats cpp_get.cgi in uw CGI-directory en open:
/cgi-bin/cpp_get.cgi?first_name=ZARA&last_name=ALI
Resultaat:
First name: ZARA Last name: ALI
Eenvoudig FORM-voorbeeld: GET
<form action="/cgi-bin/cpp_get.cgi" method="get"> First Name: <input type="text" name="first_name"><br /> Last Name: <input type="text" name="last_name" /> <input type="submit" value="Submit" /> </form>
Informatie doorgeven met POST
POST is betrouwbaarder: de informatie wordt op dezelfde manier verpakt als GET, maar als een apart bericht (standaard input) verstuurd, niet in de URL. Hetzelfde CGI-script kan zowel GET als POST afhandelen.
FORM-voorbeeld: POST
<form action="/cgi-bin/cpp_get.cgi" method="post"> First Name: <input type="text" name="first_name"><br /> Last Name: <input type="text" name="last_name" /> <input type="submit" value="Submit" /> </form>
Checkbox-data doorgeven aan CGI
Checkboxes worden gebruikt wanneer meerdere opties geselecteerd mogen worden.
<form action="/cgi-bin/cpp_checkbox.cgi" method="POST" target="_blank"> <input type="checkbox" name="maths" value="on" /> Maths <input type="checkbox" name="physics" value="on" /> Physics <input type="submit" value="Select Subject" /> </form>
C++ programma voor cpp_checkbox.cgi:
#include <iostream>
#include <vector>
#include <string>
#include <stdio.h>
#include <stdlib.h>
#include <cgicc/CgiDefs.h>
#include <cgicc/Cgicc.h>
#include <cgicc/HTTPHTMLHeader.h>
#include <cgicc/HTMLClasses.h>
using namespace std;
using namespace cgicc;
int main() {
Cgicc formData;
bool maths_flag, physics_flag;
cout << "Content-type:text/html\r\n\r\n";
cout << "<html>\n<head>\n<title>Checkbox Data to CGI</title>\n</head>\n<body>\n";
maths_flag = formData.queryCheckbox("maths");
if(maths_flag) cout << "Maths Flag: ON " << endl;
else cout << "Maths Flag: OFF " << endl;
cout << "<br/>\n";
physics_flag = formData.queryCheckbox("physics");
if(physics_flag) cout << "Physics Flag: ON " << endl;
else cout << "Physics Flag: OFF " << endl;
cout << "<br/>\n</body>\n</html>\n";
return 0;
}
Radio-button data doorgeven aan CGI
Radio buttons voor enkelvoudige selectie.
<form action="/cgi-bin/cpp_radiobutton.cgi" method="post" target="_blank"> <input type="radio" name="subject" value="maths" checked="checked"/> Maths <input type="radio" name="subject" value="physics" /> Physics <input type="submit" value="Select Subject" /> </form>
C++ programma voor cpp_radiobutton.cgi:
#include <iostream>
#include <vector>
#include <string>
#include <stdio.h>
#include <stdlib.h>
#include <cgicc/CgiDefs.h>
#include <cgicc/Cgicc.h>
#include <cgicc/HTTPHTMLHeader.h>
#include <cgicc/HTMLClasses.h>
using namespace std;
using namespace cgicc;
int main() {
Cgicc formData;
cout << "Content-type:text/html\r\n\r\n";
cout << "<html>\n<head>\n<title>Radio Button Data to CGI</title>\n</head>\n<body>\n";
form_iterator fi = formData.getElement("subject");
if(!fi->isEmpty() && fi != (*formData).end()) {
cout << "Radio box selected: " << **fi << endl;
}
cout << "<br/>\n</body>\n</html>\n";
return 0;
}
Tekstarea-data doorgeven aan CGI
TEXTAREA voor meervoudige tekst.
<form action="/cgi-bin/cpp_textarea.cgi" method="post" target="_blank"> <textarea name="textcontent" cols="40" rows="4"> Type your text here... </textarea> <input type="submit" value="Submit" /> </form>
C++ programma voor cpp_textarea.cgi:
#include <iostream>
#include <vector>
#include <string>
#include <stdio.h>
#include <stdlib.h>
#include <cgicc/CgiDefs.h>
#include <cgicc/Cgicc.h>
#include <cgicc/HTTPHTMLHeader.h>
#include <cgicc/HTMLClasses.h>
using namespace std;
using namespace cgicc;
int main() {
Cgicc formData;
cout << "Content-type:text/html\r\n\r\n";
cout << "<html>\n<head>\n<title>Text Area Data to CGI</title>\n</head>\n<body>\n";
form_iterator fi = formData.getElement("textcontent");
if(!fi->isEmpty() && fi != (*formData).end()) {
cout << "Text Content: " << **fi << endl;
} else {
cout << "No text entered" << endl;
}
cout << "<br/>\n</body>\n</html>\n";
return 0;
}
Dropdown-box data doorgeven aan CGI
Dropdown voor veel opties, één selectie.
<form action="/cgi-bin/cpp_dropdown.cgi" method="post" target="_blank"> <select name="dropdown"> <option value="Maths" selected>Maths</option> <option value="Physics">Physics</option> </select> <input type="submit" value="Submit"/> </form>
C++ programma voor cpp_dropdown.cgi:
#include <iostream>
#include <vector>
#include <string>
#include <stdio.h>
#include <stdlib.h>
#include <cgicc/CgiDefs.h>
#include <cgicc/Cgicc.h>
#include <cgicc/HTTPHTMLHeader.h>
#include <cgicc/HTMLClasses.h>
using namespace std;
using namespace cgicc;
int main() {
Cgicc formData;
cout << "Content-type:text/html\r\n\r\n";
cout << "<html>\n<head>\n<title>Drop Down Box Data to CGI</title>\n</head>\n<body>\n";
form_iterator fi = formData.getElement("dropdown");
if(!fi->isEmpty() && fi != (*formData).end()) {
cout << "Value Selected: " << **fi << endl;
}
cout << "<br/>\n</body>\n</html>\n";
return 0;
}
Cookies gebruiken in CGI
HTTP is staatloos. Voor commerciële sites is sessie-informatie nodig over pagina's heen. Cookies zijn efficiënt voor het onthouden van voorkeuren, aankopen, etc.
Hoe het werkt
De server stuurt data naar de browser als een cookie. De browser kan deze accepteren en opslaan als platte tekst. Bij een volgend bezoek is de cookie beschikbaar voor ophaling.
Een cookie bestaat uit 5 velden:
- Expires – vervaldatum; leeg betekent bij sluiten browser.
- Domain – domeinnaam van uw site.
- Path – pad naar de map/pagina die de cookie zette; kan leeg zijn.
- Secure – als "secure", alleen ophaalbaar via beveiligde server.
- Name = Value – sleutel-waardeparen.
Cookies instellen
Cookies worden verstuurd met de HTTP-header Set-Cookie vóór de Content-type-regel.
#include <iostream>
using namespace std;
int main() {
cout << "Set-Cookie:UserID = XYZ;\r\n";
cout << "Set-Cookie:Password = XYZ123;\r\n";
cout << "Set-Cookie:Domain = www.tutorialspoint.com;\r\n";
cout << "Set-Cookie:Path = /perl;\n";
cout << "Content-type:text/html\r\n\r\n";
cout << "<html>\n<head>\n<title>Cookies in CGI</title>\n</head>\n<body>\n";
cout << "Setting cookies" << endl;
cout << "<br/>\n</body>\n</html>\n";
return 0;
}
Compileer naar setcookies.cgi en roep aan via /cgi-bin/setcookies.cgi.
Cookies ophalen
Cookies staan in de omgevingsvariabele HTTP_COOKIE als key1=value1; key2=value2; ....
#include <iostream>
#include <vector>
#include <string>
#include <stdio.h>
#include <stdlib.h>
#include <cgicc/CgiDefs.h>
#include <cgicc/Cgicc.h>
#include <cgicc/HTTPHTMLHeader.h>
#include <cgicc/HTMLClasses.h>
using namespace std;
using namespace cgicc;
int main() {
Cgicc cgi;
const_cookie_iterator cci;
cout << "Content-type:text/html\r\n\r\n";
cout << "<html>\n<head>\n<title>Cookies in CGI</title>\n</head>\n<body>\n";
cout << "<table border=\"0\" cellspacing=\"2\">";
const CgiEnvironment& env = cgi.getEnvironment();
for(cci = env.getCookieList().begin(); cci != env.getCookieList().end(); ++cci) {
cout << "<tr><td>" << cci->getName() << "</td><td>";
cout << cci->getValue();
cout << "</td></tr>\n";
}
cout << "</table><\n<br/>\n</body>\n</html>\n";
return 0;
}
Compileer naar getcookies.cgi en roep aan via /cgi-bin/getcookies.cgi. Resultaat toont alle ingestelde cookies.
Bestandsupload voorbeeld
HTML-formulier met enctype="multipart/form-data" en <input type="file">.
<html> <body> <form enctype="multipart/form-data" action="/cgi-bin/cpp_uploadfile.cgi" method="post"> <p>File: <input type="file" name="userfile" /></p> <p><input type="submit" value="Upload" /></p> </form> </body> </html>
Let op: Voorbeeld uitgeschakeld om uploads op onze server te voorkomen. U kunt het op uw eigen server testen.
Script cpp_uploadfile.cpp:
#include <iostream>
#include <vector>
#include <string>
#include <stdio.h>
#include <stdlib.h>
#include <cgicc/CgiDefs.h>
#include <cgicc/Cgicc.h>
#include <cgicc/HTTPHTMLHeader.h>
#include <cgicc/HTMLClasses.h>
using namespace std;
using namespace cgicc;
int main() {
Cgicc cgi;
cout << "Content-type:text/html\r\n\r\n";
cout << "<html>\n<head>\n<title>File Upload in CGI</title>\n</head>\n<body>\n";
const_file_iterator file = cgi.getFile("userfile");
if(file != cgi.getFiles().end()) {
cout << HTTPContentHeader(file->getDataType());
file->writeToStream(cout);
}
cout << "<File uploaded successfully>\n";
cout << "</body>\n</html>\n";
return 0;
}
De upload kan ook naar een bestand op de server worden weggeschreven.
Hopelijk heeft u deze tutorial genoten. Stuur ons gerust feedback!
C Taal
- Namespaces in C#: organisatie, scope en best practices
- C++ operators: complete gids met praktische voorbeelden
- C++ comments: best practices voor leesbare, onderhoudbare code
- Arrays doorgeven aan functies in C++: een praktische gids
- C-programmeren leren: Uitgebreide tutorial voor beginners
- C++ Programmeertaal: Overzicht & Belangrijkste Kenmerken
- C++ Constanten & Literals Uitgelegd: Types, Syntax en Best Practices
- Uitgebreide Gids voor C++ Operators: Types, Gebruik en Best Practices
- Numerieke Typen in C++ Beheersen: Een Praktische Gids
- C++ Referenties Begrijpen: Sleutelconcepten & Verschillen met Pointers
- C++ Templates Beheersen: Gids voor Generisch Programmeren