Сценарии JavaScript в активных страницах Web© Александр Фролов, Григорий ФроловТом 34, М.: Диалог-МИФИ, 1998, 288 стр. Несколько примеров использования cookieВ этом разделе на примере конкретных сценариев JavaScript мы покажем, как можно использовать cookies для решения различных практических задач. Фиксация повторных посещений страницыВ нашем первом примере документ HTML содержит форму с двумя кнопками (рис. 7.1). Рис. 7.1. Кнопки для перехода к динамически создаваемому документу HTML и для удаления cookie Если нажать на кнопку Go to page, сценарий JavaScript создаст новый документ HTML. Внешний вид этого документа зависит от того, сколько раз пользователь нажимал на эту кнопку. Кнопка Remove All Cookies предназначена для удаления cookie, созданного в нашем документе HTML. Когда вы нажимаете на кнопку Go to page в первый раз, cookie еще не создано. При этом создается документ HTML, внешний вид которого показан на рис. 7.2. Рис. 7.2. Внешний вид созданного динамически документа HTML при первом посещении Во второй и последующий разы внешний вид документа изменяется (рис. 7.3). Рис. 7.3. Внешний вид созданного динамически документа HTML при третьем посещении Теперь здесь виден новый заголовок, а также содержимое параметров cookie с именами Visit и Count. При каждом новом посещении значение параметра Count будет увеличиваться на единицу. Если же в документе, показанном на рис. 7.1, нажать кнопку Remove All Cookies, подсчет посещений начнется заново. Исходный текст документа HTML представлен в листинге 7.1. Листинг 7.1. Файл chapter7/Again/Again.html <HTML> <HEAD> <TITLE>Cookies demo</TITLE> <SCRIPT LANGUAGE="JavaScript"> <!-- function addCookie(szName,szValue,dtDaysExpires) { var dtExpires = new Date(); var dtExpiryDate = ""; dtExpires.setTime(dtExpires.getTime() + dtDaysExpires * 24 * 60 * 60 * 1000); dtExpiryDate = dtExpires.toGMTString(); document.cookie = szName + "=" + szValue + "; expires=" + dtExpiryDate; } function findCookie(szName) { var i = 0; var nStartPosition = 0; var nEndPosition = 0; var szCookieString = document.cookie; while (i <= szCookieString.length) { nStartPosition = i; nEndPosition = nStartPosition + szName.length; if(szCookieString.substring( nStartPosition,nEndPosition) == szName) { nStartPosition = nEndPosition + 1; nEndPosition = document.cookie.indexOf(";",nStartPosition); if(nEndPosition < nStartPosition) nEndPosition = document.cookie.length; return document.cookie.substring( nStartPosition,nEndPosition); break; } i++; } return ""; } function removeCookie(szName) { var dtExpires = new Date(); dtExpires.setTime(dtExpires.getTime() - 1); var szValue = findCookie(szName); document.cookie = szName + "=" + szValue + "; expires=" + dtExpires.toGMTString(); } function btnClick() { if(findCookie("Visit") == "") { addCookie("Visit","Alexandr_Frolov",10); addCookie("Count","0",10); document.write("<H2>You are welcome!</H2>"); } else { var szCnt = findCookie("Count"); var i=0; if(szCnt != "") { i = szCnt; i++; szCnt = i.toString(); addCookie("Count",szCnt,10); } document.write("<H2>You are welcome AGAIN!</H2>"); document.write(document.cookie); } } // --> </SCRIPT> </HEAD> <BODY BGCOLOR=white> <H1>Visit our page!</H1> <FORM NAME="TestForm"> <P><INPUT TYPE="button" VALUE="Go to page" onClick="btnClick();"> <P><INPUT TYPE="button" VALUE="Remove All Cookies" onClick="removeCookie('Count');removeCookie('Visit')"> </FORM> </BODY> </HTML> Функции addCookie, findCookie и removeCookie, определенные в этом документе, вам уже знакомы. Они предназначены, соответственно, для создания cookie, извлечения значения заданного параметра cookie и удаления cookie. Функция btnClick вызывается, когда пользователь нажимает в форме кнопку с надписью Go to page. Прежде всего эта функция ищет параметр cookie с именем Visit. Если такой параметр не найден, считается, что страница посещается в первый раз. В этом случае функция btnClick создает параметры cookie с именами Visit и Count, а затем формирует текст документа HTML с приглашением: addCookie("Visit","Alexandr_Frolov",10); addCookie("Count","0",10); document.write("<H2>You are welcome!</H2>"); В том случае, когда пользователь посещает страницу повторно, параметр cookie с именем Visit уже существует. В этом случае функция btnClick пытается найти параметр с именем Count и получить его значение: var szCnt = findCookie("Count"); Это значение затем увеличивается на единицу и записывается обратно в параметр cookie с именем Count: i = szCnt; i++; szCnt = i.toString(); addCookie("Count",szCnt,10); Завершая свою работу, функция btnClick записывает приглашение для повторно посетивших страницу пользователей и отображает содержимое свойства document.cookie: document.write("<H2>You are welcome AGAIN!</H2>"); document.write(document.cookie); Обработчик события onClick кнопки с надписью Remove All Cookies вызывает функцию removeCookie для параметров cookie с именами Count и Visit, удаляя их: <INPUT TYPE="button" VALUE="Remove All Cookies" onClick="removeCookie('Count');removeCookie('Visit')"> Записная книжка Cookies NotepadВ следующем примере мы применили cookie для хранения произвольного текста, набранного пользователем в многострочном окне редактирования (рис. 7.4). Рис. 7.4. Документ с записной книжкой Cookies Notepad При первой загрузке документа HTML с записной книжкой окно редактирования остается пустым. Вы можете набрать здесь любой текст и записать его в cookie, нажав кнопку Store text. Если теперь закрыть документ HTML и открыть его вновь, набранный вами ранее текст появится в окне редактирования. Для того чтобы удалить текст и cookie, достаточно нажать кнопку Clear text. Исходный текст документа HTML с записной книжкой Cookies Notepad представлен в листинге 7.2. Листинг 7.2. Файл chapter7/Notebook/Notebook.html <HTML> <HEAD> <TITLE>Cookies demo</TITLE> <SCRIPT LANGUAGE="JavaScript"> <!-- function addCookie(szName,szValue,dtDaysExpires) { var dtExpires = new Date(); var dtExpiryDate = ""; dtExpires.setTime(dtExpires.getTime() + dtDaysExpires * 24 * 60 * 60 * 1000); dtExpiryDate = dtExpires.toGMTString(); document.cookie = szName + "=" + escape(szValue) + "; expires=" + dtExpiryDate; } function findCookie(szName) { var i = 0; var nStartPosition = 0; var nEndPosition = 0; var szCookieString = document.cookie; var szTemp = ""; while (i <= szCookieString.length) { nStartPosition = i; nEndPosition = nStartPosition + szName.length; if(szCookieString.substring(nStartPosition,nEndPosition) == szName) { nStartPosition = nEndPosition + 1; nEndPosition = document.cookie.indexOf(";",nStartPosition); if(nEndPosition < nStartPosition) nEndPosition = document.cookie.length; szTemp = document.cookie.substring(nStartPosition,nEndPosition); return unescape(szTemp); break; } i++; } return ""; } function removeCookie(szName) { var dtExpires = new Date(); dtExpires.setTime(dtExpires.getTime() - 1); var szValue = findCookie(szName); document.cookie = szName + "=" + szValue + "; expires=" + dtExpires.toGMTString(); } function btnClick() { addCookie("MyText",TestForm.Comment.value,10); } // --> </SCRIPT> </HEAD> <BODY BGCOLOR=white> <H1>Cookies Notepad</H1> <FORM NAME="TestForm"> <P><TEXTAREA NAME="Comment" ROWS="5" COLS="25" WRAP="physical"> </TEXTAREA> <P><INPUT TYPE="button" VALUE="Store text" onClick="btnClick();"> <INPUT TYPE="button" VALUE="Clear text" onClick="removeCookie('MyText');TestForm.Comment.value=''"> </FORM> <SCRIPT LANGUAGE="JavaScript"> <!-- var szMyText=""; szMyText = findCookie("MyText"); if(szMyText != "") { TestForm.Comment.value = szMyText; } // --> </SCRIPT> </BODY> </HTML> Функция addCookie, использованная нами в этом документе, имеет одну особенность: перед записью текстовой строки в параметр cookie она выполняет ее кодировку в формате URL, вызывая для этого функцию escape: document.cookie = szName + "=" + escape(szValue) + "; expires=" + dtExpiryDate; Это необходимо по той причине, что введенный в окне редактирования текст может содержать пробелы и любые другие символы. Аналогичные изменения мы внесли и в функцию findCookie. Эта функция возвращает значение, перекодированное в обычный текст функцией unescape, выполняющей действия, обратные по отношению к функции escape: szTemp = document.cookie.substring( nStartPosition,nEndPosition); return unescape(szTemp); Когда пользователь нажимает кнопку Store text, вызывается функция btnClick: function btnClick() { addCookie("MyText",TestForm.Comment.value,10); } Эта функция просто записывает в параметр cookie с именем MyText текстовую строку, извлеченную из многострочного поля редактирования TestForm.Comment.value. При удалении текста кнопкой Clear text вызывается функция removeCookie, удаляющая параметр cookie с именем 'MyText, а также записывается пустая строка в окно многострочного редактирования: <INPUT TYPE="button" VALUE="Clear text" onClick = "removeCookie('MyText'); TestForm.Comment.value=''"> В самом конце тела документа HTML находится небольшой фрагмент сценария JavaScript, запускающийся сразу после загрузки этого документа: var szMyText=""; szMyText = findCookie("MyText"); if(szMyText != "") { TestForm.Comment.value = szMyText; } Этот фрагмент пытается получить значение параметра cookie с именем MyText. Если это ему удается и функция findCookie возвращает непустую строку, полученная строка записывается в окно многострочного поля редактирования TestForm.Comment.value. В результате сразу после загрузки документа это окно оказывается заполненным текстом, сохраненным в cookie. Вы можете посмотреть системный файл, хранящий данные cookie. Для этого откройте каталог Temporary Internet Files, расположенный в системном каталоге Microsoft Windows 95 или Microsoft Windows NT. Там должен быть файл и именем Notebook\. Вы можете скопировать этот файл, например, на поверхность рабочего стола и открыть для просмотра любым текстовым редактором. Вы увидите примерно это: MyText This%20is%20sample%20text.%0D%0A%u042D%u0442%u043E%20%u0442%u0435%u043A%u0441%u0442%2C%20%u043A%u043E%u0442%u043E%u0440 В самом начале файла видно имя MyText параметра cookie. На следующих строках до строки ~~local~~ расположено значение параметра MyText, соответствующее тексту, показанному на рис. 7.4. Вслед за строкой ~~local~~ идет локальный адрес URL документа и другие параметры cookies, такие как дата, по достижению которой браузер удалит cookie. Если удалить файл Notebook\ и затем открыть документ HTML, многострочное окно редактирования будет пустым. Удалив этот файл, мы удалим и расположенный в нем cookie. Настройка параметров документа HTMLТретий пример демонстрирует, как можно использовать cookie для настройки пользователем параметров документа HTML. На рис. 7.5 показан документ HTML с двумя кнопками и переключателем, имеющим зависимую фиксацию. Рис. 7.5. Главный документ HTML, при помощи которого можно выполнить настройку Если нажать верхнюю кнопку, то в окне браузера появится документ HTML, созданный динамически сценарием JavaScript. В первый раз этот документ будет таким, как показано на рис. 7.6. Рис. 7.6. Внешний вид документа при первом посещении При помощи переключателей вы можете выбрать один из четырех цветов фона документа. Выбранное значение запоминается в cookie. Для того чтобы вернуться к цвету, принятому по умолчанию, в документе HTML, показанному на рис. 7.5, нужно нажать нижнюю кнопку. При последующих посещениях внешний вид документа изменится (рис. 7.7). Рис. 7.7. Внешний вид документа при третьем посещении Его фон будет иметь такой цвет, какой был выбран при помощи переключателей. Исходный текст документа HTML приведен в листинге 7.3. Листинг 7.3. Файл chapter7/CustomPage/CustomPage.html <HTML> <HEAD> <META HTTP-EQUIV="Content-Type" CONTENT="text/html; CHARSET=windows-1251"> <TITLE>Customize your page</TITLE> <SCRIPT LANGUAGE="JavaScript"> <!-- var szColor="White"; function addCookie(szName,szValue,dtDaysExpires) { var dtExpires = new Date(); var dtExpiryDate = ""; dtExpires.setTime(dtExpires.getTime() + dtDaysExpires * 24 * 60 * 60 * 1000); dtExpiryDate = dtExpires.toGMTString(); document.cookie = szName + "=" + szValue + "; expires=" + dtExpiryDate; } function findCookie(szName) { var i = 0; var nStartPosition = 0; var nEndPosition = 0; var szCookieString = document.cookie; while (i <= szCookieString.length) { nStartPosition = i; nEndPosition = nStartPosition + szName.length; if(szCookieString.substring(nStartPosition, nEndPosition) == szName) { nStartPosition = nEndPosition + 1; nEndPosition = document.cookie.indexOf(";",nStartPosition); if(nEndPosition < nStartPosition) nEndPosition = document.cookie.length; return document.cookie.substring(nStartPosition, nEndPosition); break; } i++; } return ""; } function removeCookie(szName) { var dtExpires = new Date(); dtExpires.setTime(dtExpires.getTime() - 1); var szValue = findCookie(szName); document.cookie = szName + "=" + szValue + "; expires=" + dtExpires.toGMTString(); } function btnGo() { if(findCookie("Count") == "") { addCookie("Count","0",10); addCookie("bgColor",szColor,10); document.write("<H2>Добро пожаловать!</H2>"); document.write("<P>Вы можете настроить цвет фона этой"); document.write(" страницы при помощи переключателей,"); document.write(" расположенных на главной странице."); document.write("<P>Настройки будут использованы, когда вы"); document.write(" посетите эту страницу в следующий раз."); } else { var szCnt = findCookie("Count"); var i=0; if(szCnt != "") { i = szCnt; i++; szCnt = i.toString(); addCookie("Count",szCnt,10); } document.write("<H2>Рады видеть вас снова!</H2>"); document.write("Вы посетили эту страницу в " + szCnt.bold() + " раз."); document.bgColor=findCookie("bgColor"); } } function chkRadio(form,value) { szColor = value; addCookie("bgColor",szColor,10); } function setDefault(form) { removeCookie('Count'); szColor="White"; } // --> </SCRIPT> </HEAD> <BODY BGCOLOR=white> <H1>Посетите вашу персональную страницу</H1> <FORM NAME="TestForm"> <P> <INPUT TYPE="button" VALUE="Переход на страницу" onClick="btnGo();"> <P><HR> <P>Настройка параметров персональной страницы <P><B>Цвет фона:</B> <P><INPUT TYPE="radio" NAME="Color" CHECKED VALUE="White" onClick="if(this.checked) {chkRadio(this.form,this.value);}"> Белый <BR><INPUT TYPE="radio" NAME="Color" VALUE="Yellow" onClick="if(this.checked) {chkRadio(this.form,this.value);}"> Желтый <BR><INPUT TYPE="radio" NAME="Color" VALUE="Lime" onClick="if(this.checked) {chkRadio(this.form,this.value);}"> Зеленый <BR><INPUT TYPE="radio" NAME="Color" VALUE="Fuchsia" onClick="if(this.checked) {chkRadio(this.form,this.value);}"> Малиновый <P> <INPUT TYPE="reset" VALUE="Параметры по умолчанию" onClick="setDefault(this.form);"> </FORM> </BODY> </HTML> Помимо функций addCookie, findCookie и removeCookie, предназначенных для работы с cookie, в сценарии определена переменная szColor, предназначенная для хранения выбранного пользователем цвета фона, а также функции btnGo, chkRadio и setDefault. Функция btnGo прежде всего проверяет наличие параметра cookie с именем Count. Если такого параметра нет, сценарий считает, что пользователь просматривает этот документ в первый раз. В этом случае функция btnGo добавляет два параметра cookie с именами Count и bgColor: addCookie("Count","0",10); addCookie("bgColor",szColor,10); Первый из них предназначен для хранения счетчика посещений, а второй - для хранения цвета фона. Далее функция btnGo формирует документ HTML с приглашением для пользователя, просматривающего документ в первый раз, и завершает свою работу. В том случае, когда сразу после вызова функция btnGo нашла параметр cookie с именем Count и получила его значение, это значение увеличивается на единицу и записывается обратно. Кроме того, оно отображается в тексте документа: document.write("<H2>Рады видеть вас снова!</H2>"); document.write("Вы посетили эту страницу в " + szCnt.bold()+" раз."); Затем функция btnGo устанавливает цвет фона сформированного документа HTML в соответствии со значением, извлеченным из параметра cookie с именем bgColor: document.bgColor=findCookie("bgColor"); Функция chkRadio вызывается, когда пользователь включает один из переключателей выбора цвета: function chkRadio(form,value) { szColor = value; addCookie("bgColor",szColor,10); } Эта функция записывает значение выбранного цвета в переменную szColor, а также в параметр cookie с именем bgColor. И, наконец, функция setDefault удаляет параметр cookie с именем Count и устанавливает в переменной szColor белый цвет фона, принятый по умолчанию: function setDefault(form) { removeCookie('Count'); szColor="White"; } Эта функция вызывается, когда пользователь нажимает кнопку с надписью "Параметры по умолчанию": <INPUT TYPE="reset" VALUE="Параметры по умолчанию" onClick="setDefault(this.form);"> Обратите внимание, что данная кнопка имеет тип reset. Когда пользователь нажимает на нее, в форме включается переключатель, задающий белый цвет фона. Это происходит потому, что указанный переключатель определен с параметром CHECKED, а кнопка типа reset устанавливает все органы управления формы в исходное состояние. Получение cookie расширением сервера WebВ этом разделе мы приведем пример документа HTML, создающего cookie, а также исходные тексты расширения сервера ISAPI, отображающее на динамически создаваемой странице заголовки HTTP этого документа. Когда браузер создает cookie, расширение сервера получает заголовок HTTP_COOKIE и отображает его. Наш документ аналогичен описанному ранее в разделе “Записная книжка Cookies Notepad” (рис. 7.8). Рис. 7.8. Документ HTML, работающий с cookie и расширением сервера ISAPI Дополнительно мы разместили в документе кнопку Send. С помощью этой кнопки содержимое окна редактирования и заголовок HTTP_COOKIES передается расширению ISAPI сервера Web. Расширение ISAPI отображает содержимое cookie, как это показано на рис. 7.9. Рис. 7.9. Результат работы расширения ISAPI Исходный текст документа HTML вы найдете в листинге 7.4. Листинг 7.4. Файл chapter7/NotebookISAPI/NotebookISAPI.html <HTML> <HEAD> <TITLE>Cookies demo</TITLE> <SCRIPT LANGUAGE="JavaScript"> <!-- function addCookie(szName,szValue,dtDaysExpires) { var dtExpires = new Date(); var dtExpiryDate = ""; dtExpires.setTime(dtExpires.getTime() + dtDaysExpires * 24 * 60 * 60 * 1000); dtExpiryDate = dtExpires.toGMTString(); document.cookie = szName + "=" + escape(szValue) + "; expires=" + dtExpiryDate; } function findCookie(szName) { var i = 0; var nStartPosition = 0; var nEndPosition = 0; var szCookieString = document.cookie; var szTemp = ""; while (i <= szCookieString.length) { nStartPosition = i; nEndPosition = nStartPosition + szName.length; if(szCookieString.substring( nStartPosition,nEndPosition) == szName) { nStartPosition = nEndPosition + 1; nEndPosition = document.cookie.indexOf(";",nStartPosition); if(nEndPosition < nStartPosition) nEndPosition = document.cookie.length; szTemp = document.cookie.substring( nStartPosition,nEndPosition); return unescape(szTemp); break; } i++; } return ""; } function removeCookie(szName) { var dtExpires = new Date(); dtExpires.setTime(dtExpires.getTime() - 1); var szValue = findCookie(szName); document.cookie = szName + "=" + szValue + "; expires=" + dtExpires.toGMTString(); } function btnClick() { addCookie("MyText",TestForm.Comment.value,10); } // --> </SCRIPT> </HEAD> <BODY BGCOLOR=white> <H1>Cookies Notepad</H1> <FORM NAME="TestForm" METHOD=POST ACTION="http://frolov/scripts/ishello.dll?"> <P><TEXTAREA NAME="Comment" ROWS="5" COLS="25" WRAP="physical"> </TEXTAREA> <P><INPUT TYPE="button" VALUE="Store text" onClick="btnClick();"> <INPUT TYPE="button" VALUE="Clear text" onClick = "removeCookie('MyText');TestForm.Comment.value=''"> <P><INPUT TYPE=submit VALUE="Send"> </FORM> <SCRIPT LANGUAGE="JavaScript"> <!-- var szMyText=""; szMyText = findCookie("MyText"); if(szMyText != "") { TestForm.Comment.value = szMyText; } // --> </SCRIPT> </BODY> </HTML> В нем, по сравнению с документом из раздела “Записная книжка Cookies Notepad”, мы добавили параметр ACTION в оператор <FORM>, а также кнопку типа submit с надписью Send. С помощью этой кнопки данные из формы отправляются расширению ISAPI: . . . <FORM NAME="TestForm" METHOD=POST ACTION="http://frolov/scripts/ishello.dll?"> . . . <INPUT TYPE=submit VALUE="Send"> . . . Исходный текст расширения ISAPI представлен в листинге 7.5. Он сделан на базе примера, взятого из 29 тома нашей “Библиотеки системного программиста” (раздел “Приложение ISHELLO” восьмой главы). Листинг 7.5. Файл chapter7/NotebookISAPI/ishello.c // =============================================== // Расширение ISAPI ishello.c // Пример расширения ISAPI, отображающего // содержимое cookie // // (C) Фролов А.В., 1997, 1998 // E-mail: frolov@glas.apc.org // WWW: http://www.glasnet.ru/~frolov // или // http://www.dials.ccas.ru/frolov // =============================================== #include <windows.h> #include <httpext.h> // ========================================================= // Функция GetExtensionVersion // Запись версии интерфейса ISAPI и // строки описания расширения // ========================================================= BOOL WINAPI GetExtensionVersion(HSE_VERSION_INFO *pVer) { // Записываем версию интерфейса ISAPI pVer->dwExtensionVersion = MAKELONG(HSE_VERSION_MINOR,HSE_VERSION_MAJOR ); // Записываем строку описания расширения lstrcpyn(pVer->lpszExtensionDesc, "Cookie show ISAPI DLL", HSE_MAX_EXT_DLL_NAME_LEN); return TRUE; } // ========================================================= // Функция HttpExtensionProc // ========================================================= DWORD WINAPI HttpExtensionProc(EXTENSION_CONTROL_BLOCK *lpECB) { CHAR szBuff[4096]; CHAR szTempBuf[4096]; DWORD dwSize; // Нулевой код состояния - признак успешного выполнения lpECB->dwHttpStatusCode = 0; // Записываем в буфер заголовок HTTP и начальный // фрагмент формируемого динамически документа HTML wsprintf(szBuff, "Content-Type: text/html\r\n\r\n" "<HTML><HEAD><TITLE>Simple ISAPI Extension</TITLE></HEAD>\n" "<BODY BGCOLOR=#FFFFFF><H2>Hello from ISAPI Extension!</H2>\n"); // Добавляем разделительную линию strcat(szBuff, "<HR>"); // Добавляем версию интерфейса ISAPI wsprintf(szTempBuf, "<P>Extension Version: %d.%d", HIWORD(lpECB->dwVersion), LOWORD(lpECB->dwVersion)); strcat(szBuff, szTempBuf); // Название метода передачи данных wsprintf(szTempBuf, "<BR>Method: %s", lpECB->lpszMethod); strcat(szBuff, szTempBuf); // Строка параметров запуска расширения ISAPI wsprintf(szTempBuf, "<BR>QueryString: %s", lpECB->lpszQueryString); strcat(szBuff, szTempBuf); // Физический путь к программному файлу расширения ISAPI wsprintf(szTempBuf, "<BR>PathTranslated: %s", lpECB->lpszPathTranslated); strcat(szBuff, szTempBuf); // Полный размер данных, которые нужно получить wsprintf(szTempBuf, "<BR>TotalBytes: %d", lpECB->cbTotalBytes); strcat(szBuff, szTempBuf); // Тип данных wsprintf(szTempBuf, "<BR>ContentType: %s", lpECB->lpszContentType); strcat(szBuff, szTempBuf); // Отображаем содержимое COOKIE strcat(szBuff, "<HR><P><B>Cookie:</B><BR>"); dwSize = 4096; lpECB->GetServerVariable(lpECB->ConnID, (LPSTR)"HTTP_COOKIE", (LPVOID)szTempBuf, &dwSize); strcat(szBuff, szTempBuf); // Конечный фрагмент документа HTML strcat(szBuff, "</BODY></HTML>"); // Посылаем содержимое буфера удаленному пользователю if(!lpECB->ServerSupportFunction(lpECB->ConnID, HSE_REQ_SEND_RESPONSE_HEADER, NULL, NULL, (LPDWORD)szBuff)) { // Если послать данные не удалось, // завершаем работу нашего расширения ISAPI // с кодом ошибки return HSE_STATUS_ERROR; } // Записываем код успешного завершения lpECB->dwHttpStatusCode = 200; // Возвращаем принак успешного завершения return HSE_STATUS_SUCCESS; } Файл определения модуля для библиотеки DLL расширения приведен в листинге 7.6. Листинг 7.6. Файл chapter7/NotebookISAPI/ishello.def LIBRARY ishello DESCRIPTION 'Simple ISAPI DLL' EXPORTS GetExtensionVersion HttpExtensionProc Для извлечения значения cookie, предаваемого расширению через заголовки HTTP, мы использовали функцию GetServerVariable, указав ей в качестве второго параметра имя интересующей нас переменной HTTP_COOKIE: lpECB->GetServerVariable(lpECB->ConnID, (LPSTR)"HTTP_COOKIE", (LPVOID)szTempBuf, &dwSize); Полученное таким образом значение дописывается в буфер динамически создаваемого документа HTML. Этот буфер впоследствии будет отправлен клиенту при помощи функции ServerSupportFunction. Счетчик посещений на базе cookie и программы CGIПоследний пример, приведенный в этой главе, показывает основные приемы работы с cookie в программах CGI и сценарии JavaScript, вставляемом в тело динамически формируемого документа HTML. Внешний вид исходного документа HTML, вызывающего программы CGI, показан на рис. 7.10. Рис. 7.10. Документ HTML, вызывающий программу CGI Как видно из этого рисунка, в документе определена форма с двумя кнопками. Обе эти кнопки предназначены для вызова одной и той же программы CGI, но с разными параметрами. Если нажать на кнопку с надписью Go to page, программа GCI создаст документ HTML, предварительно проанализировав заголовок пришедшего к ней запроса на предмет наличия в нем информации о cookie. Кнопка Remove All Cookies предназначена для вызова программы CGI с целью удаления cookie. В том случае, когда этой информации нет, программа CGI динамически сформирует документ HTML, добавив к его заголовку HTTP заголовок Set-Cookie (рис. 7.11). Рис. 7.11. Документ HTML, создаваемый программой CGI при первом посещении При последующих посещениях cookie уже определен, и наша программа CGI получает его значение, интерпретируя это значение как счетчик посещений. Затем она увеличивает значение счетчика на единицу, и записывает заголовок Set-Cookie в заголовок HTTP создаваемого документа HTML с новым значением cookie. Значение счетчика посещений отображается в теле документа (рис. 7.12). Рис. 7.12. Документ HTML, создаваемый программой CGI при третьем посещении Далее программа CGI вставляет в текст этого документа сценарий JavaScript, расположенный в отдельном файле. Данный сценарий определяет значение cookie своими средствами и отображает его вместе со значением счетчика посещений в многострочном окне редактирования. Исходный документ HTML представлен в листинге 7.7. Листинг 7.7. Файл chapter7/AgainCGI/AgainCGI.html <HTML> <HEAD> <TITLE>Cookies demo</TITLE> </HEAD> <BODY BGCOLOR=white> <H1>Visit our page!</H1> <FORM METHOD=POST ACTION="http://frolov/scripts/again.exe?go"> <P><INPUT TYPE="submit" VALUE="Go to page"> </FORM> <FORM METHOD=POST ACTION="http://frolov/scripts/again.exe?clear"> <P><INPUT TYPE="submit" VALUE="Remove All Cookies"> </FORM> </BODY> </HTML> В этом документе определены две формы. Первая форма предназначена для вызова программы CGI с параметром go, а вторая - с параметром clear. Исходный текст программы CGI, использованной в нашем примере, вы найдете в листинге 7.8. Листинг 7.8. Файл chapter7/AgainCGI/Again.c // =============================================== // Расширение CGI, предназначенное для // работы с cookie // // (C) Фролов А.В., 1998 // E-mail: frolov@glas.apc.org // WWW: http://www.glasnet.ru/~frolov // или // http://www.dials.ccas.ru/frolov // =============================================== #include <windows.h> #include <stdio.h> #include <stdlib.h> #include <string.h> // Прототипы функций, определенных в нашей программе char *findCookie(char * szName); void insertHTML(char * pszFileName, char * pszBuf); void main(int argc, char *argv[]) { char * pszQueryString; char * pszCookie; char * pszMyCookie; int nCount; char szBuf[4096]; char szBuf1[20]; // Заголовок для добавления cookie char szCookieHeader[] = "Set-Cookie: AgainCount=0;\r\nContent-type: text/html\r\n\r\n"; // Заголовок для удаления cookie char szCookieRemoveHeader[] = "Set-Cookie: AgainCount=0;expires=Mon 03-May-1993 12:00:00 GMT;" "\r\nContent-type: text/html;\r\n\r\n"; // Получаем параметр запуска CGI pszQueryString = getenv("QUERY_STRING"); // Получаем строку Cookie из заголовка HTTP pszCookie = getenv("HTTP_COOKIE"); // Посещение страницы if(!strcmp(pszQueryString, "go")) { // Если cookie не обнаружен, создаем его if(pszCookie == NULL) { // Выводим заголовок для создания cookie printf("Content-type: text/html\r\n"); printf(szCookieHeader); // Выводим приглашение для первого посещения printf("<HTML><HEAD><TITLE>Cookie demo</TITLE></HEAD><BODY>"); printf("<H2>Welcome to our page!</H2>"); } else { // Получаем значение параметра cookie // с именем AgainCount pszMyCookie = findCookie("AgainCount"); if(pszMyCookie != NULL) { // Преобразуем это значение в число и увеличиваем // на единицу при каждом посещении nCount = atoi(pszMyCookie); nCount++; sprintf(szBuf1, "%d", nCount); // Выводим заголовок для обновления cookie printf("Content-type: text/html\r\n"); strcpy(szBuf, "Set-Cookie: AgainCount="); strcat(szBuf, szBuf1); strcat(szBuf, ";\r\nContent-type: text/html\r\n\r\n"); printf(szBuf); // Выводим приглашение для повторных посещений printf("<H2>Welcome to our page AGAIN!</H2>"); // Выводим счетчик посещений printf("<P>Access count: %s",szBuf1); // Вставляем документ HTML с текстом сценария // JavaScript, который тоже работает с cookie insertHTML("script.ht", szBuf); printf(szBuf); } } } // Удаление cookie else if(!strcmp(pszQueryString, "clear")) { // Выводим заголовок для удаления cookie printf("Content-type: text/html\r\n"); printf(szCookieRemoveHeader); // Выводим сообщение об успешном удалении cookie printf("<HTML><HEAD><TITLE>Cookie demo</TITLE></HEAD><BODY>"); printf("<P>Cookie Removed"); } printf("</BODY></HTML>"); } // ----------------------------------------------- // findCookie // Получение значение параметра cookie по его // имени // ----------------------------------------------- char *findCookie(char * szName) { char * pszCookie; char * pszBegin; char * pszEnd; char szBuf[4096]; // Получаем текстовую строку cookie pszCookie = getenv("HTTP_COOKIE"); if(pszCookie != NULL) { // Копируем ее в рабочий буфер strcpy(szBuf, pszCookie); // Ищем в строке имя параметра pszBegin = strstr(szBuf, szName); if(pszBegin == NULL) return NULL; else { // Пропускаем символ равенства pszBegin += strlen(szName) + 1; // Ищем символ ; и заменяем его на // двоичный нуль pszEnd = strstr(pszBegin, ";"); if(pszEnd != NULL) *pszEnd = 0; // Возвращаем значение параметра return pszBegin; } } } // ----------------------------------------------- // insertHTML // Вставка в буфер содержимого текстового файла // ----------------------------------------------- void insertHTML(char * pszFileName, char * pszBuf) { HFILE hSrcFile; DWORD dwFileSize; // Открываем файл hSrcFile = _lopen(pszFileName, OF_READ); // Определяем его длину dwFileSize = _llseek(hSrcFile, 0, 2); // Устанавливаем указатель на начало файла _llseek(hSrcFile, 0, 0); // Читаем файл в буфер _hread(hSrcFile, pszBuf, dwFileSize); // Закрываем буфер двоичным нулем pszBuf[dwFileSize] = '\0'; // Закрываем файл _lclose(hSrcFile); } В переменной szCookieHeader мы подготовили заголовок Set-Cookie, предназначенный для создания параметра cookie с именем AgainCount: char szCookieHeader[] = "Set-Cookie: AgainCount=0;\r\nContent-type: text/html\r\n\r\n"; Начальное значение этого параметра равно нулю. Заголовок, хранящийся в переменной szCookieRemoveHeader, предназначен для удаления cookie: char szCookieRemoveHeader[] = "Set-Cookie: AgainCount=0;expires=Mon 03-May-1993 12:00:00 GMT;\r\nContent-type: text/html;\r\n\r\n"; Эффект удаления достигается благодаря тому, что в параметре expires мы указали уже наступившую дату. Сразу после запуска программа CGI получает значение переменных среды QUERY_STRING и HTTP_COOKIE: pszQueryString = getenv("QUERY_STRING"); pszCookie = getenv("HTTP_COOKIE"); В первой из них хранится параметр запуска программы CGI, а во второй - строка cookie (если она определена). Далее наша программа анализирует параметр запуска. Если программа вызвана с параметром go, она проверяет переменную pszCookie. В эту переменную функция getenv записывает строку cookie или значение NULL, если cookie не определено. При первом посещении cookie еще нет, поэтому наша программа добавляет к заголовку HTTP формируемого документа заголовок Set-Cookie: printf("Content-type: text/html\r\n"); printf(szCookieHeader); Затем программа выводит приглашение для первого посещения и завершает свою работу. В том случае, если в принятом запросе уже имеется информация о cookie, программа CGI извлекает значение параметра cookie с именем AgainCount, вызывая для этого функцию findCookie: pszMyCookie = findCookie("AgainCount"); Эта функция определена в нашей программе и будет описана чуть позже. Полученная строка преобразуется в численное значение при помощи функции atoi, после чего это значение увеличивается на единицу, преобразуется обратно в тестовую строку и записывается в буфер szBuf1: nCount = atoi(pszMyCookie); nCount++; sprintf(szBuf1, "%d", nCount); На следующем этапе программа формирует заголовок Set-Cookie с новым значением параметра AgainCount: printf("Content-type: text/html\r\n"); strcpy(szBuf, "Set-Cookie: AgainCount="); strcat(szBuf, szBuf1); strcat(szBuf, ";\r\nContent-type: text/html\r\n\r\n"); printf(szBuf); Этот заголовок вместе с заголовком Content-type записывается в создаваемый документ HTML. Далее после вывода приглашения для повторного посещения страницы программа CGI записывает в документ новое значение счетчика посещений: printf("<P>Access count: %s",szBuf1); И, наконец, перед завершением своей работы программа вставляет в текст документа HTML файл со сценарием JavaScript, вызывая для этого функцию insertHTML: insertHTML("script.ht", szBuf); Эта функция определена в нашей программе, как и функция findCookie. Когда программа CGI вызывается для удаления cookie с параметром clear, она выводит специально предназначенный для этого заголовок с просроченной датой: printf("Content-type: text/html\r\n"); printf(szCookieRemoveHeader); Теперь мы кратко расскажем о работе функции findCookie. Получив текстовую строку cookie при помощи функции getenv, эта функция копирует строку в рабочий буфер, который можно редактировать (напомним, что содержимое буфера, полученного от функции getenv, изменять нельзя). Далее, вызывая функцию strstr, мы ищем в рабочем буфере имя нужного нам параметра cookie. Если это имя найдено, то мы пропускаем символ равенства, ищем символ разделителя ‘;’ и заменяем его на двоичный нуль. После выполнения всех этих действий наша функция возвращает адрес искомой строки со значением нужного нам параметра cookie. Функция insertHTML просто открывает файл, имя которого передается ей в качестве параметра, читает его содержимое в оперативную память, и затем в буфер, адрес которого передается через второй параметр. Текст сценария, вставляемого функцией insertHTML в динамически формируемый документ HTML, представлен в листинге 7.9. Листинг 7.9. Файл chapter7/AgainCGI/script.ht <HR> <P>Cookie information from JavaScript: <FORM NAME="TestForm"> <P><TEXTAREA NAME="Comment" ROWS="3" COLS="25"> </TEXTAREA> </FORM> <SCRIPT LANGUAGE="JavaScript"> <!-- function findCookie(szName) { var i = 0; var nStartPosition = 0; var nEndPosition = 0; var szCookieString = document.cookie; var szTemp = ""; while (i <= szCookieString.length) { nStartPosition = i; nEndPosition = nStartPosition + szName.length; if(szCookieString.substring(nStartPosition,nEndPosition) == szName) { nStartPosition = nEndPosition + 1; nEndPosition = document.cookie.indexOf(";",nStartPosition); if(nEndPosition < nStartPosition) nEndPosition = document.cookie.length; szTemp = document.cookie.substring(nStartPosition,nEndPosition); return unescape(szTemp); break; } i++; } return ""; } var szMyText=""; szMyText = findCookie("AgainCount"); if(szMyText != "") { TestForm.Comment.value = "Cookie: " + document.cookie + "\nAccess count: " + szMyText; } // --> </SCRIPT> С функцией findCookie вы уже знакомы. Она предназначена для получения значения параметра cookie по его имени. После завершения загрузки документа HTML наш сценарий при помощи этой функции получает текущее значение параметра cookie с именем AgainCount, установленное программой CGI: var szMyText=""; szMyText = findCookie("AgainCount"); Далее это значение добавляется к полной строке cookie и отображается в многострочном поле редактирования: TestForm.Comment.value = "Cookie: " + document.cookie + "\nAccess count: " + szMyText; Форма, содержащая поле редактирования, определена в начале вставляемого файла сценария JavaScript. |