Panel użytkownika
Nazwa użytkownika:
Hasło:
Nie masz jeszcze konta?
Autor: Grzegorz 'baziorek' Bazior
Inne artykuły

[C++, cgicc] Tworzenie strony Internetowej w C++ jako skrypt CGI

[artykuł] Artykuł omawia w jaki sposób tworzy się strony Internetowe jako skrypty CGI w języku C++ przy użyciu biblioteki cgicc. W treści niniejszej publikacji omówiono takie zagadnienia jak:
- instalacja i konfiguracja serwera Apache na potrzeby skryptów CGI;
- instalacja i konfiguracja biblioteki CGICC;
- podstawy pisania skryptów CGI;
- obsługa formularzy, ciasteczek oraz sesji przy pomocy biblioteki CGICC.

Wprowadzenie

Internet i dynamiczne strony internetowe istnieją już od jakiegoś czasu, są pewne narzędzia (i jest ich coraz więcej), przy pomocy których można sworzyć strony internetowe. Najczęściej strony internetowe pisze się w językach programowania do tego dedykowanych, żeby łatwo i możliwie szybko dało się napisać strone, oraz w łatwy sposób można było ją poprawiać, debuggować czy nawet czytać kod. Najpopularniejszy język do stron internetowych -PHP jest, wg powszechnie panującej opinii, językiem bardzo prostym do nauki i pisania aplikacji; chociaż jest to język interpretowalny. Konkurencyjnym do PHP w dziedzinie pisania stron internetowych jest język ASP Microsoftu. Poza PHP duża część stron jest pisana w javie, czy ostatnio coraz popularniejszym C#. Jako ciekawostke można sobie zobaczyć statystyki używanych języków programowania serwerów WWW w skali świata; jak widać mimo największej popularności PHP czołowe serwisy światowe częściej używają innych języków do generowania dynamicznej zawartości stron, co widać na Wikipedii.
Używanie języka programowania dedykowanego do stron internetowych daje szybkość pisania stron, ale nie zapewnia szybkości działania strony internetowej. Języki interpretowane są o wiele wolniejsze od kompilowanych, poza tym część błędów znajduje się często dopiero w trakcie wykonywania. Gdy serwis internetowy ma być szybki używa się języków kompilowanych. W serwerach jest dostępne coś takiego jak skrypty CGI, które umożliwiają pisanie stron internetowych w bardzo wielu językach programowania, warunkiem koniecznym względem danego języka programowania jest umiejętność tego języka pisania do standardowego wyjścia i czytania ze standardowego wejścia.

Ten artykuł jest poświęcony pisaniu stron internetowych w języku C++. Wydaje się, że nie jest to pierwszy wybór jak chodzi o strony internetowe, niemniej jednak w realnym życiu wiele serwerów WWW używa tego języka, ze względu na dużą szybkość języków kompilowanych. Poza tym jak wiemy język C++ daje naprawdę wielkie możliwości ale żadnej z nich nie narzuca. Dlatego pisanie w nim stron nie jest rzeczą najprostrzą ani nie zajmuje najmniej czasu, ale w pewnych sytuacjach okazuje się być najlepszym wyborem.

Konfiguracja skryptów CGI

Prze instalowaniem biblioteki trzeba sobie ściągnąć odpowiedni serwer; przedstawię tutaj jak skonfigurować serwer Apache żeby korzystał ze skryptów CGI.

Instalacja serwera i konfiguracja Apache na systemie Windows

Apache można ściągnąć ze strony producenta, natomiast ja, wolę ściągnąć wamp serwer (Apache, PHP, MySQL w jednym), ze względu na wygodę instalacji i obsługi.

Mając odpalony Apache'a wchodzimy do odpowiedniego katalogu z plikiem httpd.conf (w moim przypadku jest to: C:\wamp\bin\apache\Apache2.4.4\conf), ROBIMY KOPIĘ ZAPASOWĄ PLIKU, w razie jakbyśmy coś zepsuli.
Poza tym po każdej zmianie w pliku konfuguracyjnym, żeby uczynić zmianę prawomocną, musimy ZRESETOWAĆ CAŁY SERWER.

Jak widać domyślnie możemy do katalogu cgi-bin (u mnie: C:\wamp\cgi-bin) wrzucać różne pliki wykonywalne i generować strony przy ich pomocy wchodząc na stronę: http://localhost/cgi-bin/, niestety w innych lokalizacjach serwera próba otwarcia takiego pliku domyślnie skutkuje pobieraniem danego pliku.

Wykonywanie plików wykonywalnych z dowolnego miejsca

Najpierw warto skonfigurować serwer, aby pliki wykonywalne mogły być wykonywane z dowolnego miejsca, nie tylko ze ścieżki cgi-bin. W tym celu w pliku konfiguracyjnym trzeba mieć linię:
AddHandler cgi-script .cgi .exe
dzięki temu pliki wykonywalne o rozszerzeniach .cgi, oraz .exe nie będą pobierane, tylko wykonywane, a na podstawie ich outputu będzie wygenerowana strona WWW.
Następnie znajdujemy kod:
DocumentRoot "c:/wamp/www"
<Directory "c:/wamp/www">
Options Indexes FollowSymLinks
i musimy w nim dopisać do linijki: "Options: ..."
ExecCGI
, więc całość może wyglądać tak:

Options Indexes FollowSymLinks ExecCGI

Domyślnie wykonywany skrypt index.cgi lub index.exe

Wykonywanie poszczególnych skryptów po nazwie plików może być dla nas niewygodne, może woleli byśmy zamiast localhost/sciezka/index.exe napisac localhost/sciezka/. Jeżeli tak to musimy znaleść w pliku konfiguracyjnym linie:
DirectoryIndex
, a następnie dopisać na w odpowiednim miejscu index.exe, mając na uwadze że im bardziej z lewej, tym wcześniej serwer będzie próbował odpalić dany plik.
U mnie to wygląda tak:
DirectoryIndex index.exe index.cgi index.php index.html index.htm

Dodatkowy katalog traktowany jako pliki wykonywalne

Domyślnie pliki wykonywalne mamy w katalogu:
/cgi-bin/ "c:/wamp/cgi-bin/"
, ale z jakiegoś powodu możemy chcieć mieć dodatkowy katalog (np. katalog do którego domyślnie kompiluje kompilator), w tym celu dopisujemy np. taką linię:
ScriptAlias /bbin/ "C:/Users/Grzegorz/Desktop/stronaCpp/Debug/"
, dzięki temu po każdym skompilowaniu przez np. Visual Studio będziemy mogli do naszego pliku dostać się przez adres:
http://localhost/bbin/stronaCpp.exe
, bez ręcznej konieczności przenoszenia binarki do odpowiedniego katalogu.
Ważne: w katalogach wyznaczonych jako ScriptAlias nie działają pliki niewykonywalne takie jak np *.html, tak samo nie działa listowanie zawartości danego katalogu. W sytuacji gdy chcemy mieć dodatkowy katalog, z możliwością listowania zawartości, wyświetlania plików niewykonywalnych i wyświetlania wydruku z plików wykonywalnych zapraszam do kolejnego punktu.

Dodatkowy katalog zawierający pliki wykonywalne, oraz inne pliki

W poprzednim podpunkcie pokazałem jak utworzyć ścieżkę z wyłącznie plikami wykonywalnymi (tzn. tylko pliki wykonywalne działają, reszta plików niestety nie), bez możliwości listowania plików i bez możliwości wyświetlania plików takich jak m.in. *.html, teraz pokażę jak zrobić aby w danym katalogu można było wyświetlać pliki m.in. *.xml, *.html, wyświetlać zawartość katalogu, oraz wykonywać pliki wykonywalne.
W tym celu należy dodać dla przykładowej ścieżki: "C:/Users/Grzegorz/Desktop/strona/" następujący kod:

<Directory "C:/Users/Grzegorz/Desktop/strona/">
    Options Indexes Includes FollowSymLinks SymLinksifOwnerMatch ExecCGI
</Directory>
, jako że podałem ścieżkę poza domyślnym katalogiem serwera
c:/wamp/www
, aby mieć do niej dostęp muszę utworzyć alias:

Alias /strona "C:/Users/Grzegorz/Desktop/strona/"
. Teraz po zresetowaniu serwera mam pod adresem http://localhost/strona/ listę plików katalogu C:/Users/Grzegorz/Desktop/strona/.

Pierwsze skrypty CGI w C++

Gdy mamy już skonfigurowanego Apache'a trzeba by przetestować generowanie dynamicznych stron z poziomu języka C++.
Moje przykłady będę kompilował przy użyciu Visual Studio 2008 Professional.
Ważne! Wszystkie skrypty Cgi na serwerze apache muszą generować output zaczynający się od nagłówka MINE-type:
Content-type: text/html\n\n
, bez tego zobaczymy stronę zaczynającą się od: "Internal Server Error".

Witaj Swiecie

Zacznę od przykładu wyświetlania czegokolwiek.
Tworzę nowy projekt (ja wybrałem Empty Projekt), dodaję nowy plik źródłowy i kompiluję następujący kod:
C/C++
#include <iostream>
using std::cout;
using std::endl;

int main()
{
    cout << "Content-type: text/html\n" << endl;
    cout << "Witaj zly swiecie" << endl;
}
Po przeniesieniu do katalogu serwera, w którym pliki wykonywalne są odpalane, lub po utworzeniu odpowiedniego aliasu do położenia skompilowanego pliku i wejścia w odpowiedni adres zobaczymy białą stronę z napisem:
Witaj zly swiecie"
.

Zmienne środowiskowe serwera

Zanim przejdę do biblioteki CGICC chciałbym zademonstrować jeszcze jeden przykładowy program -program który wyświetla zmienne środowiskowe systemu. Przed wyświetleniem outputu danego programu z poziomu przeglądarki, zostanie do niego wysłany następujący formularz:

Formularz wysylajacy metodą GET:<br/>
<form action="zmienne_srodowiskowe.exe" method="get">
Napis1: <input type="text" name="napis1">  <br />
Napis2: <input type="text" name="napis2">  <br />
<input type="hidden" name="sposob_wyslania" value="get">
<input type="submit" value="Submit" />
</form>

Formularz wysylajacy metodą POST:<br/>
<form action="zmienne_srodowiskowe.exe" method="post">
Napis1: <input type="text" name="napis1">  <br />
Napis2: <input type="text" name="napis2">  <br />
<input type="hidden" name="sposob_wyslania" value="post">
<input type="submit" value="Submit" />
</form>
A poniżej znajduje się kod wyświetlający zmienne środowiskowe:
C/C++
#include <iostream>
#include <cstdlib>
using namespace std;

int main( int argc, char ** argv, char ** envp )
{
    cout << "Content-type:text/html\n\n";
   
    cout << "<html>\n";
    cout << "<head>\n";
    cout << "<title>Zmienne srodowiskowe</title>\n";
    cout << "</head>\n<body>\n";
   
    cout << "<table>" << endl;
   
    for( char ** env = envp; * env != 0; ++env )
    {
        cout << "<tr><td>" << * env << "</td><td>";
        char * value = getenv( * env );
        if( value )
             cout << value;
       
        cout << "</td></tr>\n";
    }
   
    cout << "</table>\n";
    cout << "</body>\n</html>" << endl;
   
    return 0;
}
Zapawne, Drogi Czytelniku, zastanawiasz się dlaczego demonstruje taki przykład, dlatego że serwer Apache zapisuje do zmiennych środowiskowych pewne, ciekawe dla nas informacje; fragmenty wydruku strony:

HTTP_HOST=localhost
HTTP_USER_AGENT=Mozilla/5.0 (Windows NT 6.1; WOW64; rv:24.0) Gecko/20100101 Firefox/24.0
SERVER_SOFTWARE=Apache/2.4.4 (Win32) PHP/5.4.16
SERVER_NAME=localhost
SERVER_ADDR=127.0.0.1
SERVER_PORT=80
CONTEXT_PREFIX=/strona
SCRIPT_NAME=/strona/zmienne_srodowiskowe.exe
QUERY_STRING=napis1=napisalem+1&napis2=napisalem+2+i+jest+super&sposob_wyslania=get
REQUEST_URI=/strona/zmienne_srodowiskowe.exe?napis1=napisalem+1&napis2=napisalem+2+i+jest+super&sposob_wyslania=get
Jak widać w powyższych zmiennych środowiskowych są bardzo interesujące informacje, dzięki nim można mi.in.:
  • dowiedzieć się jaką przeglądarkę ma klient
  • pozyskać adres serwera
  • pozyskać adres żądania
  • przejżeć całą treść przesłana przez formularze

Instalacja i konfiguracja CGICC na systemie Windows

Mamy już podstawy i konfiguracje serwera, aby zacząć pisać programy generujące dynamiczną zawartość stron internetowych, możemy pisać dalej używając zmiennych środowiskowych do pobierania danych z formularzy i pisać ręcznie każdy, nawet najdrobniejszy, szczegół w htmlu; ale możemy też użyć biblioteki cgicc, która masę rzeczy zrobi za nas.

Na początku musimy pobrać cgicc ze strony domowej, na dzień pisania artykułu pobrałem plik: cgicc-3.2.10.tar.gz. Po pobraniu wypakowywujemy.
Następnym krokiem jest skompilowanie biblioteki (które to jest bardzo proste jak na systemy windows), w tym celu wchodzimy do podkatalogu: win, odpalamy plik cgicc.dsw w Visual Studio, przy otwieraniu może się spytać czy konwertować projekt na nowszą wersję, zaryzykowałem wybór "Yes To All". Pojawi się nam lista projektów, klikamy prawym przyciskiem na "Solution 'cgicc'", następnie wybieramy opcję Build Solution. W tym momencie może się nam pojawić pewien błąd w rodzaju: "Cannot open include file: 'cgicc/CgiDefs.h': No such file or directory", rozwiązać go możemy w następujący sposób: wchodzimy z bieżącej ścieżki do katalogu: ../cgicc/ i tam widzimy plik "CgiDefs.h.in", wystarczy zmienić jego nazwę na "CgiDefs.h" i budowa się powiedzie. Oczywiście pojawi się masa warningów, ale w pliku README.WIN, dołączonym do archiwum, jest informacja jak należy się przejąć tymi ostrzeżeniami: "These warnings may be safely ignored.".
Po chwili będziemy mieli skompilowane cgicc.lib i cgicc.dll.

Konfiguracja CGICC w Visual Studio

Tworzymy nowy projekt, najlepiej jako empty project.

Konfiguracja CGICC w konkretnym projekcie Visual Studio

Gdy chcemy skorzystać z biblioteki w konkretnym projekcie VS zaczynamy od dodania ścieżki nagłówków, robimy to klikając pracym przyciskiem na ustawieniach projektu > Properties > Configuration Properties > C/C++ > General > Additional Include Directory, następnie wpisujemy w to pole adres, gdzie mamy rozpakowaną naszą bibliotekę (ścieżka musi zawierać katalog: "cgicc" zawierający pliki *.h).
Następnym krokiem jest użycie skompilowanego wcześniej "liba", zapewne będzie to plik: cgicc-3.2.10\win\Debug\cgicc.lib; aby dodać go do projektu ponownie klikamy prawym przyciskiem na projekcie, następnie wybieramy Add > Existing Item, potem wybieramy naszego liba. Po zrobieniu powyższych czynności kompilacja się powiedzie, ale w okolice skompilowanego pliku musimy przenieść plik "cgicc.dll", powinien on być wygenerowany w tym katalogu: cgicc-3.2.10\win\Debug\cgicc.dll.

Konfiguracja CGICC dla wielu projektów Visual Studio

Jeżeli nie chcemy za każdym razem dodawać dodatkowej ścieżki do nagłówków, chcemy w łatwiejszy sposób korzystać z cgicc.lib (i mamy dostęp do odpowiednicj katalogów) możemy przekopiować katalog z nagłówkami "cgicc" do wspólnego katalogu includów Visual Studio, u mnie jest to: C:\Program Files\Microsoft SDKs\Windows\v6.0A\Include, oraz przekopiować plik z cgicc-3.2.10\win\Debug\cgicc.lib do C:\Program Files\Microsoft SDKs\Windows\v6.0A\Lib. Po tej czynności dla każdego projektu trzeba będzie tylko kliknąć prawym przyciskiem na projekt, wybrać Properties > Configuration Properties > Linker > Input > Additional Dependencies, w tym miejscu wpisać/dopisać: "cgicc.lib" i gotowe.
Dla jeszcze większego ułatwienia możemy nasz plik "cgicc.dll" przenieść do: C:\Windows\System32, dzięki temu nie będziemy musieli za każdym razem kopiować tego pliku w sąsiedztwo naszych binarek.

CGICC kompilowane jako statyczna biblioteka w Visual Studio

Jest jeszcze inny sposób na wyzbycie się zabawy z cgicc.dll. Otwieramy jeszcze raz "cgicc.dsw", następnie klikamy prawym przyciskiem na projekcie "cgicc", wybieramy Properties > Configuration Properties >  General > Configuration Type i tam ustawiamy opcję "Static Libraly (.lib)". Następnie kompilujemy i zastępujemy dotychczasowego cgicc.lib na nowy, teraz nie będziemy potrzebowali pliku cgicc.dll.

Instalacja i konfiguracja CGICC na systemie Linux

Informacje na ten temat znajdują się m.in. na Yolinux.com, dlatego pozwolę sobie nie opisać tego procesu.

Użycie biblioteki CGICC

Biblioteka mimo pozorów jest bardzo obszerna, dlatego nie opiszę jej w całości, skupię się na opisaniu podstawowej funkcjonalności, umożliwiającej następnie tworzenie własnych stron internetowych przy użyciu C++.

Witaj Świecie przy użyciu CGICC

Pamiętamy wcześniejszy przykład, bez użycia biblioteki, porównajmy z nowym przykładem:
C/C++
#include <iostream>

#include <cgicc/Cgicc.h>
#include <cgicc/HTTPHTMLHeader.h>
#include <cgicc/HTMLClasses.h>

using namespace std;
using namespace cgicc;

int main( int argc, char ** argv, char ** envp )
{
    try
    {
        Cgicc formData;
        cout << HTTPHTMLHeader() << endl; // Content-type: text/html
       
        cout << html() << head() << title( "Witaj Swiecie z CGICC" ) << head() << endl;
        cout << body() << endl;
       
        cout << h1( "Kluczowy tekst" ) << endl;
        cout << "Witaj okrotny swiecie" << endl;
       
        cout << body() << html() << endl;
    }
    catch( exception & e )
    {
        cout << "Blad!!: " << e.what() << endl;
    }
   
    return 0;
}
Wygenerowany html (wyświetlanie może być inne niż oryginalnie zapisane w pliku, gdyż używałem POKAŻ ŹRÓDŁO STRONY z poziomu Firefoxa, poza tym nie został wyświetlony nagłówek "Content-type: text/html"):

<html><head><title>Witaj Swiecie z CGICC</title></head>
<body>
<h1>Kluczowy tekst</h1>
Witaj okrotny swiecie
</body></html>

Teraz czas na opis ciekawych rzeczy w powyższym programie:
Konieczny nagłówek dla skryptów CGI jest za nas wygenerowany przy użyciu metody: HTTPHTMLHeader().
Są zdefiniowane funkcje dla dostępnych tagów htmla, dzięki nim możemy napisać np. h1() a zostanie utworzony <h1>, przy kolejnym wywołaniu zostanie utworzony tag zamykający: </h1>. Alternatywnie funkcje te posiadają wersje przyjmujące podklasy cgicc::HTMLElement np.: head(title("Title")), która to wygeneruje <head><title>Title</title></head>.

Klasy do manipulacji danymi serwera, formularzy, rządań danych


cgicc::Cgicc -główna klasa biblioteki. Używana do pobrania danych z wysłanych formularzy, załadowanych plików, zmiennych środowiskowych.

cgicc::CgiEnvironment -przechowywuje dane otrzymane z serwera HTTP do aplikacji; zawiera wszystkie zmienne środowiskowe serwera.

cgicc::FormEntry -klasa reprezentująca pojedyńcze wpisy w formularzu HTML, takie jak m.in. pole tekstowe. Przetrzymywane są informacje o nazwie i wartości danego pola. Klasa ta dostarcza metody do odnoszenia się do wartości formularzy jako string, int lub double, oraz sprawdzenie czy do pola zostało coś wpisane.

cgicc::FormFile -klasa reprezentująca załadowane pliki.

Żeby nie być gołosłownym przedstawię przykład użycia powyższych klas:
C/C++
#include <iostream>
#include <string>

#include <cgicc/Cgicc.h>
#include <cgicc/HTTPHTMLHeader.h>
#include <cgicc/HTMLClasses.h>

using namespace std;
using namespace cgicc;

int main( int argc, char ** argv, char ** envp )
{
    try
    {
        Cgicc formData;
        cout << HTTPHTMLHeader() << endl; // Content-type: text/html
       
        cout << table() << endl;
        cout << tr().add( td( "getCompileDate()" ) ).add( td( formData.getCompileDate() ) ) << endl;
        cout << tr().add( td( "getVersion()" ) ).add( td( formData.getVersion() ) ) << endl;
        cout << tr().add( td( "getHost()" ) ).add( td( formData.getHost() ) ) << endl;
       
        const CgiEnvironment & env = formData.getEnvironment();
       
        cout << tr().add( td( "getServerName()" ) ).add( td( env.getServerName() ) ) << endl;
        cout << tr().add( td( "getScriptName()" ) ).add( td( env.getScriptName() ) ) << endl;
        cout << table() << endl;
    }
    catch( exception & e )
    {
        cout << "Blad!!: " << e.what() << endl;
    }
   
    return 0;
}
A wygenerowany html będzie wyglądał mniej więcej tak:

<table>
<tr><td>getCompileDate()</td>  <td>Oct 16 2013</td></tr>
<tr><td>getVersion()</td>      <td>@VERSION@</td></tr>
<tr><td>getHost()</td>         <td>Win32</td></tr>
<tr><td>getServerName()</td>   <td>localhost</td></tr>
<tr><td>getScriptName()</td>   <td>/strona/TestStron.exe</td></tr>
</table>
W powyższym przykładzie na uwagę zasługuje metoda add(), która to do danego taga html (w naszym przypadku tr()) dodaje podelement, w naszym przypadku td.

Przykład obsługi formularza HTML przez CGICC

Do lepszego zilustrowania przykładu użycia powyższych metod przedstawię teraz formularz html (nie chodziło mi tutaj o super formularz, więc jest on trochę kulawy):

Formularz:<br/>
<form action="obsluga_formularza.exe" method="post">
Imie: <input type="text" name="imie">  <br />
Haslo: <input type="password" name="haslo">  <br />
<input type="radio" name="sex" value="male">Male <br />
<input type="radio" name="sex" value="female">Female <br />
<input type="checkbox" name="czy_jestem" value="Studentem">Jestem studentem <br />
<input type="checkbox" name="czy_jestem" value="Pracujacym">Pracujacym <br />
<input type="checkbox" name="czy_jestem" value="Emerytowanym">Emerytowanym <br />

<input type="checkbox" name="czy_place_podatki" value="zapewne tak">Place podatki <br />

Zwierze domowe:
<select>
  <option name="zwierze_domowe" value="pies">pies</option>
  <option name="zwierze_domowe" value="kot">kot</option>
  <option name="zwierze_domowe" value="chomik">chomik</option>
</select> <br />
Para slow o sobie:  <br />
<textarea name="o_sobie" rows="4" cols="50"></textarea> <br />
Ulubiona liczba: <input type="number" name="ulubiona liczba"> <br />

<input type="submit" name="przycisk_wysylania" value="Wyslij" />
</form>
Poniżej jest kod C++, ilustrujący obsługę tego formularza:
C/C++
#include <iostream>
#include <string>

#include <cgicc/Cgicc.h>
#include <cgicc/HTTPHTMLHeader.h>
#include <cgicc/HTMLClasses.h>

using namespace std;
using namespace cgicc;

int main( int argc, char ** argv, char ** envp )
{
    try
    {
        Cgicc formData;
        cout << HTTPHTMLHeader() << endl; // Content-type: text/html
       
        form_iterator name = formData.getElement( "imie" );
        if( name !=( * formData ).end() && !name->isEmpty() )
             cout << "Witaj: " << name->getStrippedValue() << "<br>";
       
        cout << h2( "Otrzymane dane:" );
        cout << table() << tr().add( td( "Nazwa" ) ).add( td( "Wartosc" ) ) << endl;
       
        const std::vector < FormEntry > & elements = formData.getElements();
        for( const_form_iterator iter = elements.begin(); iter != elements.end(); ++iter )
             cout << tr().add( td( iter->getName() ) ).add( td( iter->getValue() ) ) << endl;
       
        cout << table() << br() << endl;
       
        cout << "Wyslano metoda: " << "post <br>";
        if( formData.queryCheckbox( "czy_jestem" ) )
             cout << "Jestem studentem" << br();
       
        cout << "Wybrana plec: " << formData( "sex" ) << "<br>";
        cout << "Ulubiona liczba: " <<( * formData.getElement( "ulubiona liczba" ) ).getIntegerValue() << "<br>";
       
        form_iterator iter = formData.getElementByValue( "lubie szarlotke" );
        if( iter !=( * formData ).end() )
             cout << "ktos jednak lubi szarlotke w polu: " << iter->getName() << br();
       
        vector < FormEntry > checkboxes;
        if( formData.getElement( "czy_jestem", checkboxes ) )
        for( vector < FormEntry >::iterator it = checkboxes.begin(); it != checkboxes.end(); ++it )
             cout << it->getName() << " = " << it->getValue() << br();
       
        cout << h2( "Informacje o wyslanych danych:" );
        CgiEnvironment env = formData.getEnvironment();
        cout << "Wyslano metoda: " << env.getRequestMethod() << br();
        cout << "query string (dla metody GET): " << env.getQueryString() << br();
        cout << "podane dane (dla metody POST): " << env.getPostData() << br();
        cout << "dlugosc kontekstu (dla POST): " << env.getContentLength() << br();
       
    }
    catch( exception & e )
    {
        cout << "Blad!!: " << e.what() << endl;
    }
   
    return 0;
}
Powyższy kod wygeneruje następujący output na stronie:

Witaj: Grzegorz
Otrzymane dane:
Nazwa Wartosc
imie Grzegorz
haslo brak
sex male
czy_jestem Studentem
czy_jestem Pracujacym
o_sobie lubie szarlotke
ulubiona liczba 33
przycisk_wysylania Wyslij

Wyslano metoda: post
Wybrana plec: male
Ulubiona liczba: 33
ktos jednak lubi szarlotke w polu: o_sobie
czy_jestem = Studentem
czy_jestem = Pracujacym
Informacje o wyslanych danych:
Wyslano metoda: POST
query string (dla metody GET):
podane dane (dla metody POST): imie=Grzegorz&haslo=brak&sex=male&czy_jestem=Studentem&czy_jestem=Pracujacym&o_sobie=lubie+szarlotke&ulubiona+liczba=33&przycisk_wysylania=Wyslij
dlugosc kontekstu (dla POST): 145

Obsługa plików, polskie znaki

Teraz zademonstruje przykładowy formularz do ładowania plików (zarówno tekstowych, jak i graficznych):

Zaladuj pliki:<br/>
<form action="TestStronGLobalny.exe" method="post" enctype="multipart/form-data">
    Zdjecie: <input type="file" name="plik_zdjecie"><br>
    Plik z opisem zdjecia: <input type="file" name="plik_tekstowy"><br>
    <input type="submit" name="przycisk_wysylania" value="Wyslij" />
</form>
Poniżej znajduje się kod, który zajmie się obsłużeniem powyższego formularza, oraz wyświetleniem w tabeli obrazka i zawartości pliku z opisem obrazka. W tym przykładzie jest też przedstawiony sposób na polskie znaki na serwerze:
C/C++
#include <iostream>
#include <fstream>
#include <string>

#include <cgicc/Cgicc.h>
#include <cgicc/HTTPHTMLHeader.h>
#include <cgicc/HTMLClasses.h>

using namespace std;
using namespace cgicc;

int main( int argc, char ** argv, char ** envp )
{
    try
    {
        Cgicc formData;
        cout << HTTPHTMLHeader() << endl; // Content-type: text/html
       
        cout << html();
        {
            cout << head() << endl;
            cout << comment() << "Polskie znaki:" << comment();
            cout << meta().set( "HTTP-EQUIV", "Content-type" ).set( "CONTENT", "text/html; charset=utf-8" );
            cout << head() << endl;
           
            cout << body() << endl << h1( "Zaladowano:" ) << table();
           
            const std::vector < FormFile > & files = formData.getFiles();
           
            const_file_iterator file = formData.getFile( "plik_zdjecie" );
            if( file != files.end() )
            {
                string file_name = file->getFilename();
               
                ofstream file_stream( file_name.c_str(), ios::binary );
               
                file->writeToStream( file_stream );
               
                cout << tr( td( img().set( "src", file_name ).set( "width", "50" ) ) ) << endl;
                cout << file_name;
            }
           
            file = formData.getFile( "plik_tekstowy" );
            if( file != files.end() )
                 cout << tr( td( file->getData() ) ) << endl;
           
            cout << table() << endl << "koniec" << body() << endl;
        }
        cout << html() << endl;
    }
    catch( exception & e )
    {
        cout << "Blad!!: " << e.what() << endl;
    }
   
    return 0;
}

Obsługa ciasteczek

Kolejnym, ważnym zagadnieniem w pisaniu dynamicznie generowanych stron, są ciasteczka. Dlatego też zademonstruję przykład ich użycia.
C/C++
#include <string>
#include <vector>
#include <stdexcept>
#include <iostream>
#include <sstream>
#include <cstdlib>
#include <ctime>

#include <cgicc/Cgicc.h>
#include <cgicc/HTTPHTMLHeader.h>
#include <cgicc/HTMLClasses.h>
#include <cgicc/HTTPCookie.h>

using namespace std;
using namespace cgicc;


string getRandNumberInString()
{
    ostringstream os;
    os << rand();
    return os.str();
}


int main()
{
    srand( time( 0 ) );
   
    try
    {
        Cgicc cgi;
       
        HTTPCookie cookie( "losowa_liczba", getRandNumberInString() );
        cookie.setMaxAge( 60 ); // ciasteczko na 1 min
       
        cout << HTTPHTMLHeader().setCookie( cookie ); /** Zapisuje ciasteczko **/
       
        cout << html().set( "lang", "pl" ).set( "dir", "ltr" ) << endl;
       
        string styles( "background-color: yellow;" );
       
        cout << head() << '\n'
        << style() << comment() << '\n' << styles << comment() << style() << '\n'
        << title() << "GNU cgicc obsluga ciasteczek" << title() << '\n'
        << head() << endl;
       
        cout << body() << endl;
       
        const CgiEnvironment & env = cgi.getEnvironment();
       
        cout << p() << "Ustawiono ciasteczko: " << cookie.getName() << " = " << cookie.getValue() << br();
        cout << "Aby zobaczyc je zobaczyc " << a( "odswierz" ).set( "href", env.getScriptName() ) << " strone: " << p();
       
        cout << h2( "Ciag zawierajacy wszystkie ciasteczka" ) << endl << env.getCookies() << endl;
       
        cout << h2( "Zapisane ciasteczka" ) << endl << cgicc::div().set( "align", "center" ) << endl;
       
        cout << table().set( "border", "1" ) << endl;
        {
            cout << tr() << td( "Nazwa ciasteczka" ) << td( "Wartosc ciasteczka" ) << tr() << endl;
           
            const std::vector < HTTPCookie >& cookies = env.getCookieList(); /**Pobranie wszystkich ciasteczek**/
            if( cookies.size() == 0 )
                 cout << tr( td( "brak ciasteczek" ) ) << endl;
           
            for( const_cookie_iterator iter = cookies.begin(); iter != cookies.end(); ++iter )
                 cout << tr() << td( iter->getName() ) << td( iter->getValue() ) << tr() << endl;
           
        }
        cout << table() << endl;
       
        cout << body() << html() << endl;
       
        return 0;
    }
    catch( const std::exception & e )
    {
        cout << HTTPHTMLHeader() << HTMLDoctype( HTMLDoctype::eStrict ) << endl;
       
        cout << head( title( "GNU cgicc exception" ) ) << endl
        << h1() << " Polecial wyjatek: " << e.what() << h1() << endl;
       
        return 0;
    }
}

Powyższy kod najpierw tworzy ciasteczko: "losowa_liczba", oraz czyta wszystkie, zapisane ciasteczka. Dodam że dopiero co zapisanego ciasteczka nie będzie wśród wszystkich ciasteczek podczas tego wyświetlania strony, dopiero podczas kolejnego żądaniem strony.

Sesja

Ważnym aspektem pisania dynamicznie generowanych stron jest utrzymanie sesji, czyli współdzielonych, między żądaniami, danych, trzymanych na serwerze. Niestety CGICC nie dostarcza jeszcze takiej funkcjonalności. Niemniej jednak mamy wiele sposobów na jej zaimplementowanie.
Najprostrzym rozwiązaniem jest symulacja sesji przy użyciu ciasteczek, niemniej jednak dzięki temu podszywanie się pod kogoś innego stanie się bardzo proste. Dlatego lepszym rozwiązaniem jest wygenerowanie pewnego, unikalnego ID dla klienta, oraz trzymanie dla niego danych na serwerze np. w bazie danych, czy też w plikach, chociaż przy drugim rozwiązaniu trzeba by się zabezpieczyć przed wielodostępem do plików.

Bibliografia

Źródło inspiracji