Разработка ASP-приложений при помощи Delphi
Разработка ASP-приложений при помощи Delphi 5
Сервер ASP работает под управлением Windows NT (98, 2000) с Internet Information Server (IIS) или Microsoft Personal Web Server (PWS). Такая зависимость несколько сужает круг использования ASP. По существу, ASP-технология -- это внутренняя разработка Microsoft, которая не была согласована с такими законодателями де-факто технологий интернет, как Sun и Netscape. Возможно, что не стоит особо рассчитывать на то, что эта технология “приживется” на других платформах, поскольку в ASP заложены такие эксклюзивные разработки Microsoft, как COM-технология и запись данных в системный реестр. Однако уже сегодня многие крупные ресурсы интернет построены на основе ASP.Принцип работы ASP
Клиентское приложение, работающее с ASP-сервером,-- это обычный документ HTML, который можно прочитать в любом браузере. И размещаются эти документы на обычном вебсервере. Вебсервер, получив требование о предоставлении такого документа, считывает его из локального хранилища и передает клиенту, но при этом часть информации вносится в этот документ ASP-сервером динамически. Принято, чтобы документы, обращающиеся к ASP-серверу, имели расширение ASP. Примеры таких документов есть в каталогах Microsoft Internet Informational Server -- WINNT\SYSTEM32\INETSRV\IISADMIN и INETPUB\IISAMPLES\EXAIR. Типичное обращение к ASP-серверу из документа HTML выглядит так:
Это сценарий, написанный на Visual Basic (VB). Поскольку сценарии из ASP-документов выполняются на сервере, то клиенту передается уже готовая вебстраница. Таким образом, несмотря на наличие VB-сценария, страница ASP может быть доступна клиентам, работающим на других платформах, таких как UNIX.
По существу ASP-сервер представляет собой сервер OLE-автоматизации, где предопределено несколько интерфейсов, в частности, IRequest (содержит методы, вызов которых позволяет установить параметры, заполненные на клиенте) и IResponse (вызов методов которого приводит к формированию документа HTML и передаче последнего клиенту). В этом ASP-сервер напоминает приложения CGI и ISAPI/NSAPI DLL (далее -- вебсерверные приложения). Принципы выполнения методов на ASP-сервере и в вебсерверных приложениях также аналогичны: анализируется (при необходимости) запрос клиента и динамически формируется отклик. Отличие заключается в том, что вебсерверные приложения формируют документ HTML целиком, а отклик ASP-сервера вставляется в код исходной вебстраницы. Например, если документ ASP представлен в виде
и результатом выполнения метода MyContent является строка “Первое обращение к ASP-серверу”, то на экране клиентского браузера появится документ, содержащий текст “Первое обращение к ASP-серверу”, отформатированный в соответствии с тэгами этого документа.
Таким образом, в документ HTML вставляется отклик ASP-сервера. В одном документе могут содержаться обращения к разным ASP-серверам. Результат будет также объединен в один документ. Получить аналогичный результат при использовании веб-приложений невозможно. Однако и здесь есть свое ограничение: все ASP-серверы, к которым обращается документ, должны быть зарегистрированы на одном сервере IIS. Обратиться по разным адресам для формирования одного документа HTML нельзя.
Простой пример работы ASP
ASP-сервер может быть реализован как в виде исполняемого приложения, так и в виде динамической подключаемой библиотеки (DLL). Это разрешается при создании сервера OLE-автоматизации. ASP-сервер в виде EXE-файла запускается один раз в ответ на запрос клиента. При использовании внутренних (in-process) ASP-серверов один экземпляр DLL, загруженный в оперативную память, способен обслуживать одновременно нескольких клиентов. Для каждого из них может создаваться отдельный экземпляр COM-объекта. Кроме того, один экземпляр COM-объекта также может обслуживать нескольких клиентов. Это зависит от выбранной модели потоков обработки данных (Threading Model) в диалоговом окне, открываемом по команде File р New р ActiveX р Active Server Object.
Рассмотрим следующий пример. Создадим при помощи среды разработки Delphi 5 внутренний (in-process) ASP-сервер и рассмотрим принципы его работы. Пусть сервер выполняет один запрос. Создадим с помощью команды File р New р ActiveX р ActiveX Library новый проект и сохраним его под именем MyASP. Теперь воспользуемся командой File р New р ActiveX р Active Server Object. В появившемся диалоговом окне определим имя класса, например, TestASP (рис. 1). Поскольку мы создаем внутренний (in-process) сервер, то в данном случае параметр Instancing не играет роли, зато имеет значение параметр Threading Model. Выбор режима Single приводит к неэффективной работе сервера, т. к. при одновременном обращении к нему нескольких клиентов сервер будет выполнять запросы последовательно. Если выполнение запроса одного из клиентов потребует много времени, то остальные клиенты будут вынуждены ожидать его окончания, даже если их запросы можно выполнить гораздо быстрее. Это создает впечатление “зависания” браузера и часто приводит к попыткам пользователей, так или иначе, прервать выполнение задачи. В режиме Apartment запросы клиентов разделяются по потокам, причем для каждого клиента создается отдельный экземпляр COM-объекта (в данном примере класса TTestASP). В этом случае при написании методов класса не требуется защиты переменных класса по потокам. Клиент может свободно модифицировать их, что упрощает разработку кода. Недостаток такой модели состоит в ресурсоемкости проекта. Кроме того, при каждом обращении к ASP-серверу переменные класса инициализируются заново, что увеличивает время отклика. Этих недостатков лишена модель Free, в которой один экземпляр COM-объекта обслуживает нескольких клиентов. Однако, если клиенты могут изменять данные, то потребуется защита общих переменных по потокам. Это усложнит код приложения и станет потенциальным источником трудноуловимых ошибок. Как правило, модель Free используют в серверах, которые только предоставляют данные, но не позволяют клиенту их модифицировать. В нашем случае выберем модель Apartment.
В этом же диалоговом окне мы имеем возможность выбрать назначение ASP-сервера. Если планируется инсталлировать сервер на компьютере, которым управляет IIS версии 3 или 4, то следует выбрать Page Level Events Methods. С IIS 5 этот режим также работает, но в этом случае эффективнее будет работать опция Object Context. Ее же следует выбирать, если работой ASP управляет Microsoft Transaction Server (MTS). В режиме Generate a template test script for this object будет создан документ HTML, который в дальнейшем можно будет использовать для тестирования ASP-сервера.
После нажатия кнопки OK будет создан файл с описанием интерфейсов Unit1.pas. Переименуем его в TestASPUnit.pas. Кроме того, будет создана библиотека типов, откроется ее редактор и файл с описанием -- в данном примере MyASP_TLB.pas. Если был выбран режим Page-level event methods, то библиотека типов будет содержать два предопределенных метода -- OnStartPage и OnEndPage. Также будет создан файл TestASP.asp, который содержит документ HTML с заготовками VB-сценариев для тестирования сервера.
Теперь создадим метод, который будет заполнять документ HTML. Для этого в редакторе библиотеки типов выберем интерфейс ITestASP и вберем команду New Method. Назовем вновь созданный метод MyContent. Этот метод не должен иметь параметров. Затем обновим данные в модуле, описывающем библиотеку, нажатием кнопки.
В модуле реализации (TestASPUnit.pas) появится заготовка, где следует описать эту реализацию. Пусть метод выглядит следующим образом:
procedure TTestASP.MyContent; begin if Assigned(Response) then Response.Write ('First call to ASP server'); end;
Здесь происходит обращение к методу Write интерфейса IResponse. Проверка Assigned (Response) гарантирует наличие ссылки на интерфейс в момент записи сообщений.
Теперь необходимо изменить документ HTML, созданный Delphi для тестирования сервера. Он хранится в файле TestASP.asp. В этом документе имеется следующий VB-сценарий:
В таком виде он работать не будет. Необходимо заменить фигурную скобку {Insert Method name here} на имя метода ASP-сервера, который генерирует отклик. В данном примере это MyContent:
Скомпилируем проект, выбрав пункт меню Run р Register ActiveX Server. Теперь можно приступать к тестированию. Для этого создадим на IIS виртуальный каталог и назначим ему права доступа Read (так как отсюда будут читаться данные) и Execute (так как отсюда будет загружаться и запускаться MyASP.dll). Можно поступить и иначе -- разместить эти файлы в разных каталогах, из которых один имел бы полномочия на чтение, а второй -- на выполнение. В любом случае этот каталог (каталоги) должен экспонироваться через HTTP-протокол. Поэтому, в первую очередь, необходимо обратиться к WWW-сервису IIS, посмотреть список доступных каталогов и при необходимости создать новые с соответствующими правами доступа. В моем случае проект тестировался локально. Был создан виртуальный каталог /TestASP, куда был скопирован файл TestASP.asp.
Затем введем в Microsoft Internet Explorer строку
https://localhost/TestASP/TestAsp.asp
Как можно увидеть, сценарий (текст между ) замещен тем, что получилось в результате выполнения на ASP-сервере метода MyContent.
Технология выполнения сценария
Рассмотрим подробнее, каким образом выполняется сценарий на странице TestASP.asp. Internet Information Server, получив запрос на TestASP.asp, получает содержимое данной страницы, определяет, что в ней находится сценарий и выполняет его. При этом в фоновом режиме запускается интерпретатор Visual Basic, и вызывается команда CreateObject. Если файл MyASP.dll еще не был загружен, то происходит его загрузка. Для данного запроса создается COM-объект - экземпляр класса TTestASP (он описан в модуле реализации, для нашего случая - TestASPUnit.pas). Ссылка на интерфейс Idispatch (он поддерживается классом TTestASP) сохраняется в переменной DelphiASPObj. При дальнейшем написании кода после имени переменной, хранящей ссылку на IDispatch, можно набирать любой текст. Компилятор передает ASP-серверу текст, который в сценарии следует после имени переменной (в данном примере: переменная - DelphiASPObj, метод - MyContent). Если ASP-сервер найдет метод с данным именем, то он его выполнит, если же нет, будет сгенерировано исключение. Поэтому при написании сценариев для ASP-сервера следует быть внимательным, а при возникновении исключений в первую очередь проверить корректность имен методов. При вызове метода ASP-серверу становятся доступны интерфейсы IRequest и IResponse.
Пример анализа запроса
Рассмотрим более сложный вариант ASP-сервера, где запрос клиента анализируется при помощи методов интерфейса IRequest. В качестве задачи сервера будет выступать выполнение запроса к СУБД Oracle 8.0.5. В рамках данной статьи я не буду затрагивать вопросы создания объектов баз данных. Скажу лишь, что в нашем случае имеет место таблица Names с единственным полем Name. Таблица создана в схеме Demo. Далее приведен SQL-запрос, создающий эту таблицу.
CREATE TABLE Names
(Name VARCHAR2(50))
Вначале создадим документ HTML, содержащий форму, с помощью которой будут вызываться методы ASP-сервера. Например, такой:
https://localhost/TestASP/TestASP.asp" method="POST" name="Query">
Имя
Сохраним этот файл под именем name.HTML и поместим его в каталог TestASP.
Но прежде чем создавать модуль данных и обращаться к серверу базы данных необходимо выяснить, каким образом анализируется запрос клиента на ASP-сервере. Это можно делать при помощи методов интерфейса IRequest, ссылка на который есть в свойстве Request класса TASPObject -- предка класса, соответствующего ASP-серверу. Интерфейс IRequest предоставляет три свойства -- QueryString, Form и Body, в которых находятся ссылки на интерфейс IRequestDictionary. QueryString содержит параметры запроса, Form -- список элементов управления, предоставляемых клиенту, а Body -- данные, которые клиент ввел при помощи этих элементов. Именно они нам и потребуются, поэтому проанализируем свойство Body. Однако все сказанное далее о методах IRequestDisctionary применимо и к двум другим свойствам типа ICustomDictionary -- QueryString и Form.
Метод IRequestDictionary определен в модуле ASPTlb.pas следующим образом:
IRequestDictionary = interface(IDispatch) ['{D97A6DA0-A85F-11DF-83AE-00A0C90C2BD8}'] function Get_Item (Var_: OleVariant): OleVariant; safecall; function Get__NewEnum: IUnknown; safecall; function Get_Count: SYSINT; safecall; function Get_Key (VarKey: OleVariant): OleVariant; safecall; property Item [Var_: OleVariant]: OleVariant read Get_Item; default; property _NewEnum: IUnknown read Get__NewEnum; property Count: SYSINT read Get_Count; property Key [VarKey: OleVariant]: OleVariant read Get_Key; end;
Свойство Count содержит число элементов управления формы (для формы в name.HTML оно равно 2), свойство Key - имена элементов управления (в нашем случае это field и button), свойство Item - значения, введенные пользователем.
Следует помнить, что в коллекции Key[] индексы начинаются с единицы, а не с нуля. Обращаясь к коллекции Key с соответствующим индексом - в данном случае 1 или 2 - можно получить имена элементов управления в виде строковых переменных.
Внесем в проект небольшое дополнение. Воспользовавшись редактором библиотек типов, создадим новый метод RequestProp. Введем для этого метода следующий код.
procedure TTestASP.RequestProp; var S:string; V:OLeVariant; I,J,N:integer; begin S:=''; if Assigned(Request) then if Request.Body.Count>0 then begin for I:=1 to Request.Body.Count do begin S:=S+'Key'+IntToStr(I)+'='+Request.Body.Key[I]+' '; V:=Request.Body.Item[I]; if not VarIsEmpty(V) then if varType(V)=varDispatch then begin N:=V.Count; S:=S+'ItemCount'+IntToStr(I)+'='+IntToStr(N)+' '; if N>0 then for J:=1 to N do S:=S+V.Item[J]+' '; end; end; end; if Assigned(Response) then Response.Write(S); end;
Скомпилируем проект и в созданном ранее файле TestASP.asp изменим VB-скрипты следующим образом: заменим строку DelphiASPObj.MyContent на DelphiASPObj.RequestProp. Теперь откроем name.HTML в Internet Explorer, введя следующий адрес: http://localhost/TestASP/name.HTML
Откроется страница с формой. Введем в строке любое значение, щелкнем на кнопке Вперед! и получим результат выполнения приведенного ранее метода RequestProp:
Key1=field
ItemCount1=1
some
Key2=button
ItemCount2=1
Вперед!
Таким образом, для определения параметров, введенных клиентом в какой-либо элемент управления, необходимо просмотреть все ключи, найти индекс интересуемого нас элемента управления (в данном примере это 1, что соответствует ключу field) и извлечь значение, введенное клиентом, при помощи команды Request.Body.Item[Index].Item[1].
ASP и диалоговые окна
Создадим в нашем ASP-сервере новый метод для доступа к базам данных. Поместим в нем невизуальные компоненты доступа к данным. (Использовать визуальные компоненты в ASP-сервере нельзя.) Вообще-то в приложениях типа ASP, ISAPI/NSAPI, CGI - показ модальных форм с элементами управления (таких как диалоговые окна) ни к чему хорошему не приводит. При попытке это сделать будут созданы элементы управления диалогового окна, на них будет помещен текст и\или графика, после чего приложение будет ожидать закрытия диалогового окна (по нажатию кнопки OK или Cancel), чтобы продолжить свою работу. Но дело в том, что окно при этом остается невидимым. Поэтому ни нажатием кнопок (они не получают сообщения OnClick), ни акселератором (сигналы с клавиатуры невидимым элементам управления не посылаются) закрыть его нельзя. Визуально программист наблюдает следующее: приложение “висит”, отклика с ASP-сервера клиент не получает, а для повторной компиляции проекта требуется перезагрузка системы. Но, несмотря на то, что команды показа диалоговых окон в ASP-сервере отсутствуют, эти окна можно использовать в работе приложения, например, сообщения об ошибке BDE. Это нужно помнить при написании кода, и тщательно проверять данные перед их использованием, чтобы внешние приложения не сообщали об ошибках.
Традиционно доступ к данным осуществляется с помощью BDE. При этом использовались компоненты TSession, TDatabase и TQuery. Однако лучше при разработке ASP-сервера использовать новый способ доступа к данным, появившийся в Delphi 5 - ADO (Active Data Objects). Для работы с ADO нам прежде всего понадобится компонент TADOConnection. Поставим его на форму. В инспекторе объектов выберем свойство ConnectionString и вызовем диалоговое окно для создания строки. Выберем там Microsoft OLE DB Provider for Oracle и щелкнем на кнопке Next.
На следующей вкладке диалогового окна необходимо указать имя сервера (в данном примере beq-local) и параметры аутентификации -- имя пользователя (Demo) и пароль (тот, который вы указали при создании пользователя Demo). Обязательно должна быть установлена метка в элементе управления Allow Saving Password, иначе ASP-сервер попытается вывести на экран диалоговое окно для входа в систему. Протестировать соединение можно нажатием кнопки Test Connection. Если все в порядке, то появится сообщение об успешном соединении с сервером.
Затем присвойте в инспекторе объектов свойству LoginPromp значение False. Проверить правильность настройки можно, изменив значение Connected на True. При этом не должно появиться диалоговое окно входа в систему или информация о генерации исключения.
Поставим на модуль данных компонент TADOQuery и в свойстве Connection сошлемся на определенный ранее компонент ADOConnection1.
При вызове эксперта для создания ASP-сервера модуль данных не создается автоматически. Его необходимо создавать отдельно. Добавляем модуль данных к проекту с помощью команды File р New р Data Module. Назовем созданный модуль данных ASPDataModule, а сам файл сохраним под именем ASPDataModuleUnit.pas. При запуске приложения или при обращении клиента этот модуль не будет создаваться автоматически, поэтому необходимо переписать конструктор и деструктор класса TTestASP, реализация которого находится в файле TestASPUnit.pas. Сошлемся в модуле TestASPUnit.pas на модуль ASPDataModuleUnit.pas. В объявлении класса TTestASP в разделе private определим переменную FData типа TASPDataModule. В разделе public объявим процедуры AfterConstruction и BeforeDesctruction c обязательной директивой override:
TTestASP = class(TASPObject, ITest) private FData:TDataModule1; protected public procedure AfterConstruction; override; procedure BeforeDestruction; override; end;
реализуем процедуры AfterConstruction и BeforeDestruction в секции реализации:
procedure TTest.AfterConstruction; begin inherited; FData:=TDataModule1.Create(nil); end; procedure TTest.BeforeDestruction; begin if Assigned(FData) then begin FData.Query1.Active:=False; FData.ADOConnection1.Connected:=False; FData.Free; end; inherited; end;
Теперь создадим в библиотеке типов новый метод и назовем его QueryResponse. Реализуем его следующим образом:
procedure TTest.QueryResponse; var S:string; I,J:integer; begin S:=Request.Body.Item[1].Item[1]; if FData.ADOQuery1.Active then FData.ADOQuery1.Close; FData.ADOQuery1.SQL.Clear; FData.ADOQuery1.SQL.Add('Select * from EMP'); FData.ADOQuery1.SQL.Add('where name like '+CHR(39)+S+'%'+CHR(39)); FData.ADOQuery1.Active:=True; if FData.ADOQuery1.RecordCount>0 then begin FData.ADOQuery1.First; for J:=0 to FData.ADOQuery1.Fields.Count-1 do Response.Write(FData.ADOQuery1.Fields[J].FieldName+' '); Response.Write(' '); for I:=1 to FData.ADOQuery1.RecordCount do begin for J:=0 to FData.ADOQuery1.Fields.Count-1 do Response.Write(FData.ADOQuery1.Fields[J].AsString+' '); Response.Write(' '); if I end; end; end;
В этом методе динамически создается SQL-запрос на основе параметров, введенных клиентом в форму. Затем осуществляется обращение к SQL-серверу, и возвращаемые данные помещаются в HTML-документ. Изменим в созданном ранее файле TestASP.asp VB-сценарии следующим образом: вместо строки DelphiASPObj.MyContent напишем строку DelphiASPObj.QueryResponse. После этого в Microsoft Internet Explorer обратимся к странице name.HTML.
Настройка ASP-сервера
При использовании ASP-сервера можно поместить параметры, необходимые для его работы, в HTML-документ. Эти параметры можно редактировать и, таким образом, настраивать документ под определенный сайт. Это удобно при распространении ASP-сервера: покупатель может изменить начальные установки так, чтобы они отвечали его требованиям. Для этого достаточно всего лишь отредактировать HTML-документ. В качестве примера определим в заголовке класса TTestASP (TestASPUnit.pas) две переменные, FCompanyName:string и FCopyrightYear:string, а в библиотеке типов - два новых свойства, CompanyName:string и CopyrightYear:integer. Для методов *Read и *Wirte для этих свойств определим чтение и возврат данных из описанных ранее переменных. Добавим в библиотеку типов новый метод ShowCopyright, который реализуем следующим образом:
procedure TTestASP.ShowCopyright; var S:OLEVariant; begin S:=Format('Copyright (C) %d by %s',[FCopyrightYear,FCompanyName]); if Assigned(Response) then Response.Write(S); end;
В созданном ранее файле TestASP.asp изменим VB-сценарии:
Если в файле TestASP.asp изменить имя компании, а это можно сделать при помощи любого текстового редактора, то все изменения будут отражены в HTML-документе.
Естественно, в рамках одной статьи невозможно рассказать обо всех сторонах использования Active Server Pages. Надеюсь, что мне удалось показать вам перспективы технологий, несущих новую жизнь интернет-ресурсам. Привлекательный и функциональный дизайн необходим, но, чтобы выжить, ваши страницы должны быть интерактивными.
2004.04.08 Автор: Сергей Кривошеев