Parto do princípio que, o leitor desta página, já possui conhecimentos de HTML e até mesmo em como funciona um Servidor WEB, seja ele em UNIX ou Microsoft Windows NT.
É altamente recomendável que desenvolvamos nossos aplicativos WEB em servidores locais, e quando estiverem totalmente livres de erros, passarmos os mesmos para os servidores de produção.
Apesar dos exemplos aquí estarem todos feitos em ISAPI, sua conversão para o padrão CGI é bastante simples. Tudo que temos a fazer é criar um novo aplicativo do tipo CGI, excluir as Units que vêem por default, e então adicionar as nossas. Poderemos ter dois projetos, um CGI e outro ISAPI, compartilhando das mesmas Units.
Boa Sorte.
O CGI possibilitou ao servidor WEB produzir um conteúdo que pode ser entendido por um Browser a partir de um conteúdo não acessível, como por exemplo um Banco de Dados. Dessa maneira, o CGI age como uma ponte entre dois produtos, permitindo ao usuário ter acesso à informações que antes só poderiam ser lidas por um sistema desenvolvido especificamente para este propósito, transformando o Browser WEB em um perfeito "Cliente Universal".
Um aplicativo CGI é um programa que é executado pelo servidor WEB, em resposta a uma solicitação do navegador, e que escreve uma página HTML como resultado. Esta página é então enviada de volta ao navegador.
A seguir os passos detalhados de um processo de CGI:
01 - O navegador pede ao servidor WEB para executar
determinado aplicativo CGI, ou seja, gera uma requisição.
02 - O servidor WEB inicia um novo processo para
rodar o aplicativo CGI
03 - O CGI é executado e recebe parâmetros,
que podem estar localizados em propriedades diferentes, dependendo do tipo
de requisição ( GET ou POST ). Neste ponto
poderemos acessar banco de dados, fazendo consultas ou mesmo alterações.
04 - Após efetuar suas tarefas, o CGI deve
escrever em stdout, que será enviado ao navegador.
Através destas API's o servidor WEB pôde, então, tirar proveito do mecanismo de DLL's do Windows para carregar o ISAPI ou o NSAPI apenas uma vez, no seu próprio espaço de endereçamento. Dessa maneira cada requisição passou a gerar apenas uma nova "thread" ao invés de um processo inteiro.
As vantagens desta abordagem sobre o CGI é
que os aplicativos se tornam mais rápidos, principalmente no seu
tempo de carga, porém passam a depender de um único tipo
de servidor WEB - Isso antes do Delphi 3, pois agora o mesmo aplicativo
pode, facilmente, se transformar de CGI para ISAPI/NSAPI e vice-versa.
Existe
um problema com os aplicativos ISAPI ou NSAPI. Como eles rodam no mesmo
espaço de endereçamento do servidor WEB, os mesmos só
poderão ser substituídos ou apagados quando o serviço
www, ou até mesmo o servidor, estiver fora do ar. Desligar e ligar
um servidor WEB não é nada difícil, más se
for de produção pode ocasionar problemas.
O Microsoft Personal Web Server 4 não é
recomendável para desenvolvimento, pois uma vez no ar, só
poderá ser retirado após a inicialização da
máquina. Se possuir o Windows 95 poderá instalar o Personal
Web Server 1.0a, que é perfeito para desenvolvimento. O Windows
95 OSR2 já o possui, e em português.
Para aplicativos ISAPI ou NSAPI, que são DLL's, teremos que efetuar alterações no fonte do projeto gerado pelo Delphi. Teremos que incluir, nas primeiras linhas após o begin o seguinte:
IsMultiThread := True;
Application.CacheConnections := False;
Nosso projeto então deverá ter, aproximadamente, esta codificação:
begin // Duas linhas incluídas IsMultiThread := True; Application.CacheConnections := False; // Application.Initialize; Application.CreateForm(TWebModule1, WebModule1); Application.Run; end.
Tabela de Tipos de Objetos Aplication, Request
e Response por Tipo de Aplicativo:
Tipo
de Aplicativo WEB
|
Objeto
Application
|
Objeto
Request
|
Objeto
Response
|
Microsoft
Server DLL ( ISAPI )
|
TISAPIApplication
|
TISAPIRequest
|
TISAPIResponse
|
Netscape
Server DLL ( NSAPI )
|
TISAPIApplication
|
TISAPIRequest
|
TISAPIResponse
|
Console
CGI Application
|
TCGIApplication
|
TCGIRequest
|
TCGIResponse
|
Windows
CGI Application ( Win-CGI )
|
TISAPIApplication
|
TWinCGIRequest
|
TWinCGIResponse
|
Todos os tipo de aplicativos WEB são criados da mesma maneira, selecionando na IDE do Delphi:
A partir deste ponto escolhemos o tipo, de aplicativo WEB, que melhor se ajusta ao Servidor WEB no qual será implementado. O Delphi criará um novo projeto com um TWEBModule vazio. Este TWEBModule é um descendente direto de um TDataModule comum, e deve ser usado da mesma maneira, ou seja, podemos colocar nele componentes, principalmente os de Banco de Dados, sendo que a principal diferença é que ele age como um "Dispatcher", tratando as requisições dos clientes através de Action Items, que são os verdadeiros responsáveis pelo preenchimento e envio das respostas.
Como é comum a conversão de um sistema já em funcionamento em um aplicativo WEB, o Delphi 3 nos oferece um outro componente chamado TWEBDispatcher. Com ele nós podemos adicionar toda a funcionalidade de um TWEBModule à um TDataModule existente. Isso é feito da seguinte maneira: Após gerarmos um novo aplicativo WEB, retiramos do projeto o TWEBModule gerado e adicionamos nosso TDataModule, depois solta-se sobre ele um TWEBDispatcher e pronto, metade do trabalho foi poupado.
A partir daí podemos adicionar quantos Action
Items quisermos, dependendo claro, de quantos tipos de requisições
desejamos tratar. Isto funciona da seguinte maneira: Quando o servidor
WEB recebe uma requisição do tipo:
http://www.server.com/scripts/myappl.dll/counter | ISAPI ou NSAPI DLL |
http://www.server.com/scripts/myappl.exe/counter | Executável CGI |
Tabela de Ocorrências dos Eventos de TWEBModule:
Tipo de Aplicativo WEB | OnCreate | BeforeDispatch | AfterDispatch | OnDestroy |
CGI | Toda Requisição | Toda Requisição | Toda Requisição | Toda Requisição |
ISAPI / NSAPI | Só na Primeira | Toda Requisição | Toda Requisição | Desliga serviço WEB |
O PathInfo no nosso exemplo de requisição anterior é o "/counter", ou seja, é a parte logo em seguida ao nome do aplicativo. Quanto ao MethodType, é a forma como o seu FORM HTML envia os dados, sendo que os mais comuns são GET e POST.
Obs.: Seria altamente aconselhável um breve conhecimento de HTML, sobretudo a parte de FORMs
Quando é encontrado um Action Item que atenda às condições, é disparado um evento deste Action Item chamado OnAction. Dentro deste evento é que ocorre todo o trabalho de um aplicativo WEB, agora é que vamos entrar no modo de programação mesmo.
Anatomia do Evento OnAction:Um Action Item pode tratar sozinho de uma requisição ou apenas fazer uma parte do trabalho, deixando que outros a completem. Se o parâmetro Handled for setado como False o TWEBModule irá procurar por um outro Action Item que atenda às especificações e, se nenhum for encontrado, será acionado o Action Item Default. Se nenhum for executado nada será retornado ao Browser. Um Action Item é posto como Default colocando True em sua propriedade Default. Apenas um pode ser o Action Item Default, sendo que o valor de seu parâmetro Handled deve sempre ser setado como True ou então ignorado ( trata como True ), más nunca False.Após os Action Items terem sido executados e a resposta enviada ao servidor WEB, será executado o AfterDispatch do TWEBModule. Uma observação muito importante sobre ISAPI e NSAPI apenas: Como o TWEBModule permanece na memória e é único a todas as threads geradas ( Action Items ), devemos tomar muito cuidado com variáveis e propriedades criadas no TWEBModule, pois as mesmas serão também únicas, ou seja, se tivermos uma propriedade no nosso TWEBModule e ela tiver seu valor alterado por uma requisição, as próximas irão exergá-la com este novo valor. Quando precisarmos gravar valores nestas propriedades e/ou variáveis de dentro de um Action Item, devemos fazer uso de uma Critical Section para garantir que uma outra thread espere até a atual ter terminado o trabalho. Devemos usar este recurso também quando quisermos acessar arquivos texto ( File-IO ) em nosso disco, como veremos no exemplo de contador de páginas.procedure TWebModule1.WebModule1WebActionItem1Action(Sender: TObject; Request: TWebRequest; Response: TWebResponse; var Handled: Boolean); begin end;Parâmetros:
Sender: Representa o TWEBModule. Request: Informações sobre a requisição HTTP cliente. Response: Deve ser preenchida com a resposta a ser enviada de volta ao Browser do usuário. Handled: Se o evento encerrou a resposta deve ser True, se outros Action Items forem completar a resposta deve ser False. Se nada for informado age como True.
Action do FORM HTML | Method do FORM HTML | Action Item que será disparado |
/scripts/myappl.dll | GET ou POST | O Default |
/scripts/myappl.dll/consulta | GET | Um que possua o PathInfo como /consulta e o MethodType como mtGet ou mtAny, ou então o Default |
/scripts/myappl.dll/incluir | POST | Um que possua o PathInfo como /incluir e o MethodType como mtPost ou mtAny, ou então o Default |
/scripts/myappl.dll/teste | POST | Um que possua o PathInfo como /teste e o MethodType como mtPost ou mtAny, ou então o Default |
Propriedades | Significado |
Authorization | Contém a informação de autenticação do cliente, se ele foi autenticado. |
ContentFields | TStrings contendo os nomes e valores dos campos do FORM HTML passados via POST. |
CookieFields | TStrings contendo os nomes e valores de cookies vindos do cliente. |
From | Contém o e-mail do cliente ( Comigo não funciona ). |
MethodType | Contém o Method do FORM HTML. |
PathInfo | Contém o PathInfo da requisição. |
QueryFields | TStrings contendo os nomes e valores dos campos do FORM HTML passados via GET. |
RemoteAddr | Contém o IP do cliente. |
UserAgent | Contém informações sobre o Browser cliente. |
Propriedades | Significado |
Content | O próprio conteúdo da resposta, podendo ser comandos HTML, arquivo HTML ou texto. |
ContentStream | Quando a resposta precisa ser escrita diretamente de um Stram |
ContentType | Usado para indicar o tipo de conteúdo ( MIME-Type ). O padrão é 'text/html' |
WWWAuthenticate | Usado para autenticação de usuários. |
Métodos | Significado |
SendRedirect | Usado para redirecionar o cliente para uma outra página. |
SendStream | Envia conteúdo de um Stream. |
SetCookieField | Usado para adicionar Cookies à resposta. |
procedure TWebModule1.WebModule1WebActionItem1Action(Sender: TObject; Request: TWebRequest; Response: TWebResponse; var Handled: Boolean); Var slValores: TStringList; begin slValores := TStringList.Create; Try slValores.Add(''); slValores.Add(''); slValores.Add('Valores da sua Requisição:
No OnAction nós criamos um TStringList e incluímos nele os comandos HTML, depois atribuímos a propriedade Text dele à propriedade Content do TWEBResponse. Pronto, a página foi enviada de volta ao cliente.
Para executá-lo basta digitar seu nome completo no campo de endereço do navegador.
Nós vimos neste exemplo o uso da propriedade Content, do TWEBResponse, para o retorno de uma página HTML, más podemos usar também o SendRedirect para redirecionar uma requisição para uma outra página, ou SendStream para enviar o conteúdo de um Stream, estes, aliás, serão usados nos próximos 2 exemplos.Download dos fontes de vl_request
procedure TWMCounter.WMCounterAICounterAction(Sender: TObject; Request: TWebRequest; Response: TWebResponse; var Handled: Boolean); Var sCount: String; imCount: TJPEGImage; stCount: TMemoryStream; sFile: String; begin // Request.PathTranslated retorna o path físico do ROOT // no meu caso: c:\webshare\wwwroot sFile := Request.PathTranslated + '\fbn\scripts\fcount.cnt'; // Entra na Sessão Crítica - Acesso a recursos por várias threads // Isso garante que só uma thread executará esta função por vez EnterCriticalSection( csFileIO ); Try // Incrementa contador e retorna novo número sCount := IncCounter( sFile ); Finally LeaveCriticalSection( csFileIO ); End; // Gera a Imagem do Contador imCount := BuildImage( sCount ); Try // Uso de um MemoryStream stCount := TMemoryStream.Create; Try imCount.SavetoStream( stCount ); stCount.Position := 0; Response.ContentType := 'image/jpeg'; Response.ContentLength := stCount.Size; Response.SendResponse; Response.SendStream( stCount ); Finally stCount.Free; End; Finally imCount.Free; End; end;Criamos 2 funções, uma para incrementar o contador, que está em um arquivo texto, e outra para transformar o texto com o contador em uma imagem JPEG. Criamos então um TMemoryStream para carregar a imagem nele e o enviamos ao browser do cliente.
Este exemplo, funcionando, pode ser visto no final desta página e para executá-lo basta criar um tag de imagem, no HTML, apontando para a DLL.Download dos fontes de WebCounter
<p>Esta página
foi acessada <img
src="/scripts/webcounter.dll">
vezes.</p>
Para transformarmos este exemplo em CGI, no caso de termos de usar um outro servidor WEB não compatível com os da Microsoft, devemos iniciar um novo projeto de aplicativo WEB, escolher o tipo CGI, retirar as Units criadas, e adicionar a Unit deste exemplo. Retirar também todas as linhas de CriticalSection. Esta conversão já foi feita e pode ser obtida aquí.
Da maneira que o WebCounter se encontra, só poderemos usá-lo em uma única página. Como poderemos então colocar nosso contador em várias páginas de nosso site ? - Simples, a única mudança necessária seria em passarmos o nome do arquivo como parâmetro para o aplicativo. Veja como ficaria nosso HTML.Download dos fontes de WebCounter ( Versão CGI )
<p>Esta página
foi acessada img
src="/scripts/webcounter.dll?fn=cont1.cnt">
vezes.</p>
Note que após o nome do aplicativo nós colocamos o ?fn=cont1.cnt, que seria a passagem de um parâmetro chamado fn com o valor de cont1.cnt no método GET. Este parâmetro estaria disponível ao aplicativo WEB na propriedade QueryFields do TWEBRequest, Veja o trecho de código modificado:
... begin sFile := Request.PathTranslated + '\fbn\scripts\' + ; Request.QueryFields.Values[ 'fn' ]; // Passa Nome do Arquivo ... imCount := BuildImage( sCount ); ...Não esquecer de alterar o código em '\fbn\scripts\' para apontar para seu diretório de scripts no servidor WEB.
procedure TWebModule1.WebModule1WebActionItem1Action(Sender: TObject; Request: TWebRequest; Response: TWebResponse; var Handled: Boolean); begin IncCounter( Request.PathTranslated ); Response.SendRedirect( 'http://www.borland.com' ); end;Isto é mais simples do que se pode imaginar. Só precisamos criar um procedure para incrementar o contador de acessos e, através do SendRedirect, enviamos o usuário para o site da Borland
Para executá-lo basta colocar um Link em sua página dessa maneira:Download dos fontes de HitCounter
<p align="center"><a href="/scripts/hitcounter.dll">Borland WebSite</a></p>
Nexte
exemplo não coloquei uma Critical Section, se desejar
usar este aplicativo, deve providenciar isto. Veja exemplo anterior
Os 3 geradores de conteúdo disponíveis são: TPageProducer, TDataSetTableProducer e TQueryTableProducer. O primeiro gera HTML através de alterações em Documentos HTML que serverm como Templates. Os outros 2 produzem comandos HTML baseados em informações de um Banco de Dados.
Isto pode ser feito graças a Tags que são colocadas nesta página HTML padrão, e que serão substituídas por informações reais em nosso aplicativo WEB. Um Tag possui o seguinte formato:
<#TagName Param1=Value1 Param2=Value2 ...>
Os sinais de > e < tornam os Tags transparentes ao navegadores, no caso da Tag não ser tratada pelo TPageProducer. O sinal # informa ao TPageProducer que esta construção é uma Tag, e que deve ser substituída por outro valor. Uma Tag pode opcionalmente possuir parâmetros, sendo que eles devem ser no formato Nome=Valor, com nenhum espaço entre o nome, o sinal de = e o valor. Cada parâmetro deve ser separado dos demais por um espaço em branco.
O Delphi 3 nos oferece um número de Tags
predefinidas, que estão associadas com valores do tipo TTag.
São elas:
Nome da Tag | Valor de TTag | No que ela deve ser convertida |
Um Nome qualquer | tgCustom | A seu critério |
Link | tgLink | Em um Link A../A |
Image | tgImage | Em uma Tag HTML de Imagem IMG SRC=... |
Table | tgTable | Em uma tabela HTML TABLE.../TABLE" |
ImageMap | tgImageMap | Em um mapa de imagens MAP.../MAP" |
Object | tgObject | Em um Controle ActiveX OBJECT.../OBJECT |
Embed | tgEmbed | Em um NetScape Add-In DLL EMBED.../EMBED |
O TPageProducer possui duas propriedades, chamadas HTMLFile e HTMLDoc, pelas quais podemos especificar qual será nossa página HTML padrão. HTMLFile é usada para armazenar o nome de um arquivo em disco, enquanto que HTMLDoc armazena um TStrings representando o Template, ou seja, se nossa página HTML padrão for uma página que está no disco, devemos colocar seu nome na propriedade HTMLFile, se não, podemos criá-la em memória e armazená-la na propriedade HTMLDoc, que é um TStrings. Nós podemos também armazenar nossas páginas padrões em campos MEMO de um banco de dados e usar o método ContentFromStream para obter este HTML diretamente do campo.
Quando especificamos que a propriedade Content do objeto TWEBResponse é igual ao Content, ou um correspondente, de TPageProducer, a página HTML padrão é avaliada e, para cada Tag HTML encontrado será disparado o evento OnHTMLTag de TPageProducer.
Anatomia do Evento OnHTMLTag:procedure TWebModule1.PageProducer1HTMLTag(Sender: TObject; Tag: TTag; const TagString: String; TagParams: TStrings; var ReplaceText: String); begin end;Parâmetros:
Sender: Representa o TPageProducer Tag: O Tipo da Tag ( tgCustom, tgLink, ... ). TagString: O Nome da Tag. TagParams: Descendente de TStrings com cada um de seus itens representando um parâmetro da Tag HTML. ReplaceText: Deve ser preenchida com o Valor que substituirá o Tag HTML.