然而,以這種方式搭建起來的HTTP 伺服器,不管何時請求目錄中的某個文件,HTTP 伺服器發送回來的不是該文件,而是以程式形式執行,並把執行產生的輸出傳回瀏覽器顯示出來。
公共網關介面(CGI),是使得應用程式(稱為 CGI 程式或 CGI 腳本)能夠與 Web 伺服器以及用戶端互動的標準協定。這些 CGI 程式可以用 Python、PERL、Shell、C 或 C++ 等來寫。
在您進行CGI 程式設計之前,請確保您的Web 伺服器支援CGI,並已設定成可以處理CGI 程式。所有由 HTTP 伺服器執行的 CGI 程序,都必須在預先配置的目錄中。目錄稱為 CGI 目錄,依慣例命名為 /var/www/cgi-bin。雖然 CGI 文件是 C++ 可執行文件,但是按照慣例它的擴展名是 .cgi。
預設情況下,Apache Web 伺服器會設定在 /var/www/cgi-bin 中執行 CGI 程式。如果您想要指定其他目錄來執行CGI 腳本,您可以在httpd.conf 檔案中修改以下部分:
變數名稱 | 描述 |
---|
#CONTENT_TYPE | ##內容的資料型態。當客戶端向伺服器發送附加內容時使用。例如,文件上傳等功能。 |
CONTENT_LENGTH | 查詢的資訊長度。只對 POST 請求可用。 |
HTTP_COOKIE | 以鍵 & 值對的形式傳回設定的 cookies。 |
HTTP_USER_AGENT | 用戶代理請求標頭字段,遞交用戶發起請求的有關信息,包含了瀏覽器的名稱、版本和其他平台性的附加信息。 |
PATH_INFO | CGI 腳本的路徑。 |
QUERY_STRING | 透過 GET 方法傳送請求時的 URL 編碼訊息,包含 URL 中問號後面的參數。 |
REMOTE_ADDR | 發出要求的遠端主機的 IP 位址。這在日誌記錄和認證時是非常有用的。 |
REMOTE_HOST | #發出要求的主機的完全限定名稱。如果此資訊不可用,則可以用 REMOTE_ADDR 來取得 IP 位址。 |
REQUEST_METHOD | 用於發出請求的方法。最常見的方法是 GET 和 POST。 |
SCRIPT_FILENAME | CGI 腳本的完整路徑。 |
SCRIPT_NAME | CGI 腳本的名稱。 |
SERVER_NAME | 伺服器的主機名稱或 IP 位址。 |
SERVER_SOFTWARE | 伺服器上執行的軟體的名稱和版本。 |
下面的 CGI 程式列出了所有的 CGI 變數。
#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";
cout << "<head>\n";
cout << "<title>CGI 环境变量</title>\n";
cout << "</head>\n";
cout << "<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 << "环境变量不存在。";
}
cout << "</td></tr>\n";
}
cout << "</table><\n";
cout << "</body>\n";
cout << "</html>\n";
return 0;
}
C++ CGI 函式庫
在真實的實例中,您需要透過 CGI 程式執行許多操作。這裡有一個專為C++ 程式所寫的CGI 函式庫,我們可以從ftp://ftp.gnu.org/gnu/cgicc/ 上下載這個CGI 函式庫,並按照下面的步驟安裝函式庫:
$tar xzf cgicc-X.X.X.tar.gz
$cd cgicc-X.X.X/
$./configure --prefix=/usr
$make
$make install
您可以點選C++ CGI Lib Documentation,查看相關的函式庫文件。
GET 和 POST 方法
您可能曾經遇到這樣的情況,當您需要從瀏覽器傳遞一些資訊到 Web 伺服器,最後再傳到 CGI 程式。通常瀏覽器會使用兩種方法把這個資訊傳到 Web 伺服器,分別是 GET 和 POST 方法。
使用 GET 方法傳遞訊息
GET 方法傳送已編碼的使用者資訊追加到頁面請求中。頁面和已編碼訊息透過? 字元分隔開,如下所示:
http://www.test.com/cgi-bin/cpp.cgi?key1=value1&key2=value2
GET 方法是預設的從瀏覽器向Web 伺服器傳遞訊息的方法,它會在瀏覽器的網址列中產生一串很長的字串。當您向伺服器傳密碼或其他一些敏感資訊時,請勿使用 GET 方法。 GET 方法有大小限制,在一個請求字串中最多可以傳送 1024 個字元。
當使用 GET 方法時,是使用 QUERY_STRING http 頭來傳遞訊息,在 CGI 程式中可使用 QUERY_STRING 環境變數來存取。
您可以透過在 URL 後面跟上簡單連接的鍵值對,也可以透過使用 HTML <FORM> 標籤的 GET 方法來傳遞訊息。
簡單的 URL 實例:Get 方法
下面是一個簡單的 URL,使用 GET 方法將兩個值傳遞給 hello_get.py 程式。
/cgi-bin/cpp_get.cgi?first_name=ZARA&last_name=ALI
以下的實例產生cpp_get.cgi CGI 程序,用於處理Web 瀏覽器給出的輸入。透過使用C++ 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";
cout << "<head>\n";
cout << "<title>使用 GET 和 POST 方法</title>\n";
cout << "</head>\n";
cout << "<body>\n";
form_iterator fi = formData.getElement("first_name");
if( !fi->isEmpty() && fi != (*formData).end()) {
cout << "名:" << **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 << "姓:" << **fi << endl;
}else{
cout << "No text entered for last name" << endl;
}
cout << "<br/>\n";
cout << "</body>\n";
cout << "</html>\n";
return 0;
}
現在,編譯上面的程序,如下所示:
$g++ -o cpp_get.cgi cpp_get.cpp -lcgicc
生成cpp_get.cgi,並把它放在CGI 目錄中,並嘗試使用下面的連結進行存取:
/cgi-bin/cpp_get.cgi?first_name=ZARA&last_name=ALI
這會產生以下結果:
名:ZARA
姓:ALI
簡單的表單實例:GET 方法
下面是一個簡單的實例,使用HTML 表單和提交按鈕傳遞兩個值。我們將使用相同的 CGI 腳本 cpp_get.cgi 來處理輸入。
<form action="/cgi-bin/cpp_get.cgi" method="get">
名:<input type="text" name="first_name"> <br />
姓:<input type="text" name="last_name" />
<input type="submit" value="提交" />
</form>
下面是上述表單的實際輸出,請輸入名稱和姓,然後點擊提交按鈕查看結果。
使用 POST 方法傳遞訊息
一個更可靠的向 CGI 程式傳遞訊息的方法是 POST 方法。這種方法打包資訊的方式與 GET 方法相同,不同的是,它不是把訊息以文字字串形式放在 URL 中的 ? 之後進行傳遞,而是把它以單獨的訊息形式進行傳遞。該訊息是以標準輸入的形式傳給 CGI 腳本的。
我們同樣使用 cpp_get.cgi 程式來處理 POST 方法。讓我們以同樣的例子,透過使用HTML 表單和提交按鈕來傳遞兩個值,只不過這次我們使用的不是GET 方法,而是POST 方法,如下所示:
<form action="/cgi-bin/cpp_get.cgi" method="post">
名:<input type="text" name="first_name"><br />
姓:<input type="text" name="last_name" />
<input type="submit" value="提交" />
</form>
向CGI 程序傳遞複選框資料
當需要選擇多個選項時,我們使用複選框。
下面的HTML 程式碼實例是一個帶有兩個複選框的表單:
<form action="/cgi-bin/cpp_checkbox.cgi"
method="POST"
target="_blank">
<input type="checkbox" name="maths" value="on" /> 数学
<input type="checkbox" name="physics" value="on" /> 物理
<input type="submit" value="选择学科" />
</form>
下面的C++ 程式會產生cpp_checkbox.cgi 腳本,用於處理Web 瀏覽器透過複選框框給出的輸入。
#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";
cout << "<head>\n";
cout << "<title>向 CGI 程序传递复选框数据</title>\n";
cout << "</head>\n";
cout << "<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";
cout << "</body>\n";
cout << "</html>\n";
return 0;
}
向 CGI 程式傳遞單選按鈕資料
當只需要選擇一個選項時,我們使用單選按鈕。
下面的HTML 程式碼實例是一個帶有兩個單選按鈕的表單:
<form action="/cgi-bin/cpp_radiobutton.cgi"
method="post"
target="_blank">
<input type="radio" name="subject" value="maths"
checked="checked"/> 数学
<input type="radio" name="subject" value="physics" /> 物理
<input type="submit" value="选择学科" />
</form>
下面的C++ 程式會產生cpp_radiobutton.cgi 腳本,用於處理Web 瀏覽器透過單選按鈕給出的輸入。
#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";
cout << "<head>\n";
cout << "<title>向 CGI 程序传递单选按钮数据</title>\n";
cout << "</head>\n";
cout << "<body>\n";
form_iterator fi = formData.getElement("subject");
if( !fi->isEmpty() && fi != (*formData).end()) {
cout << "Radio box selected: " << **fi << endl;
}
cout << "<br/>\n";
cout << "</body>\n";
cout << "</html>\n";
return 0;
}
向 CGI 程式傳遞文字區域資料
當需要向 CGI 程式傳遞多行文字時,我們使用 TEXTAREA 元素。
下面的HTML 程式碼實例是一個帶有TEXTAREA 框的表單:
<form action="/cgi-bin/cpp_textarea.cgi"
method="post"
target="_blank">
<textarea name="textcontent" cols="40" rows="4">
请在这里输入文本...
</textarea>
<input type="submit" value="提交" />
</form>
下面的C++ 程式會產生cpp_textarea.cgi 腳本,用於處理Web 瀏覽器透過文字區域給出的輸入。
#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";
cout << "<head>\n";
cout << "<title>向 CGI 程序传递文本区域数据</title>\n";
cout << "</head>\n";
cout << "<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";
cout << "</body>\n";
cout << "</html>\n";
return 0;
}
向 CGI 程式傳遞下拉框資料
當有多個選項可用,但只能選擇一個或兩個選項時,我們使用下拉框。
下面的HTML 程式碼實例是一個帶有下拉框的表單:
<form action="/cgi-bin/cpp_dropdown.cgi"
method="post" target="_blank">
<select name="dropdown">
<option value="Maths" selected>数学</option>
<option value="Physics">物理</option>
</select>
<input type="submit" value="提交"/>
</form>
下面的C++ 程式會產生cpp_dropdown.cgi 腳本,用於處理Web 瀏覽器透過下拉框給出的輸入。
#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";
cout << "<head>\n";
cout << "<title>向 CGI 程序传递下拉框数据</title>\n";
cout << "</head>\n";
cout << "<body>\n";
form_iterator fi = formData.getElement("dropdown");
if( !fi->isEmpty() && fi != (*formData).end()) {
cout << "Value Selected: " << **fi << endl;
}
cout << "<br/>\n";
cout << "</body>\n";
cout << "</html>\n";
return 0;
}
在 CGI 中使用 Cookies
HTTP 協定是一種無狀態的協定。但對於一個商業網站,它需要在不同頁間保持會話資訊。例如,一個使用者在完成多個頁面的步驟之後結束註冊。但是,如何在所有網頁中保持使用者的會話資訊。
在許多情況下,使用 cookies 是記憶和追蹤有關使用者喜好、購買、佣金以及其他為追求更好的遊客體驗或網站統計所需資訊的最有效的方法。
它是如何運作的
伺服器以 cookie 的形式向訪客的瀏覽器發送一些資料。如果瀏覽器接受了 cookie,則 cookie 會以純文字記錄的形式儲存在訪客的硬碟上。現在,當訪客造訪網站上的另一個頁面時,會檢索 cookie。一旦找到 cookie,伺服器就知道儲存了什麼。
cookie 是一種純文字的資料記錄,有5 個可變長度的欄位:
Expires : cookie 的過期日期。如果此欄位留空,cookie 會在訪客退出瀏覽器時過期。
Domain : 網站的網域。
Path : 設定 cookie 的目錄或網頁的路徑。如果您想要從任意的目錄或網頁檢索 cookie,此欄位可以留空。
Secure : 如果此欄位包含單字 "secure",那麼 cookie 只能透過安全伺服器進行檢索。如果此欄位留空,則不存在該限制。
Name=Value : cookie 以鍵值對的形式被設定和取得。
設定 Cookies
向瀏覽器發送 cookies 是非常簡單的。這些 cookies 會在 Content-type 欄位之前,與 HTTP 頭一起被傳送。假設您想要設定 UserID 和 Password 為 cookies,設定 cookies 的步驟如下所示:
#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.w3cschool.cc;\r\n";
cout << "Set-Cookie:Path=/perl;\n";
cout << "Content-type:text/html\r\n\r\n";
cout << "<html>\n";
cout << "<head>\n";
cout << "<title>CGI 中的 Cookies</title>\n";
cout << "</head>\n";
cout << "<body>\n";
cout << "设置 cookies" << endl;
cout << "<br/>\n";
cout << "</body>\n";
cout << "</html>\n";
return 0;
}
從這個實例中,我們了解如何設定 cookies。我們使用 Set-Cookie HTTP 頭來設定 cookies。
在這裡,有一些設定 cookies 的屬性是可選的,例如 Expires、Domain 和 Path。值得注意的是,cookies 是在發送行"Content-type:text/html\r\n\r\n 之前被設定的。
編譯上面的程序,產生setcookies .cgi,並嘗試使用下面的連結設定cookies。 ##檢索所有設定的cookies 是非常簡單的。 #現在,編譯上面的程序,產生getcookies.cgi,並嘗試使用下面的連結獲取您的電腦上所有可用的cookies:
/cgi-bin/getcookies.cgi
#這會產生一個列表,顯示了上一節中設定的四個cookies 以及您的電腦上所有其他的cookies:
key1=value1;key2=value2;key3=value3....
文件上傳實例
為了上傳一個文件,HTML 表單必須把enctype 屬性設定為
multipart/form-data。
##注意:
上面的實例已經故意禁用了保存上傳的檔案在我們的伺服器上。用於處理檔案上傳的腳本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;
const_cookie_iterator cci;
cout << "Content-type:text/html\r\n\r\n";
cout << "<html>\n";
cout << "<head>\n";
cout << "<title>CGI 中的 Cookies</title>\n";
cout << "</head>\n";
cout << "<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";
cout << "<br/>\n";
cout << "</body>\n";
cout << "</html>\n";
return 0;
}
上面的實例是在cout 流中寫入內容,但您可以開啟檔案流,並把上傳的檔案內容儲存在目標位置的某個檔案中。