- Introdução
- CGI
- ISAPI/NSAPI
- Delphi 3 C/S
- Leituras Complementares
Esta página pretende ensinar aos desenvolvedores do Borland Delphi 3 a construir aplicativos de servidor WEB. Se este material lhe serviu em algum propósito, se gostou ou até mesmo se possui críticas, por favor assine meu Livro de Visitas, deixe lá seu comentário. Com a sua ajuda poderemos melhorar cada vez mais.
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. Se desejar, poderá obter maiores esclarecimentos nestes assuntos seguindo os Links em Leituras Complementares.
É 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.
CGI - Commom Gateway Interface, nada mais é do que uma definição de como o servidor WEB e o Browser se comunicam, para permitir aos autores de páginas HTML fornecerem conteúdo dinâmico e personalizado de acordo com informações enviadas por seus usuários. Uma página dinâmica é uma página que é gerada na hora a partir de modêlos existentes sem, contudo, existir fisicamente no disco do servidor.
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.
Com a chegada do servidores WEB ao mundo Windows, alguns fabricantes, como Netscape e Microsoft, resolveram criar API's proprietárias de acesso ao serviço WEB, se utilizando de recursos existentes no Windows para contornarem o principal problema do CGI, que é justamente a natureza de todo programa executável. Um EXE tem que ser carregado na memória, executado em seu próprio espaço de endereçamento e, finalmente encerrado e retirado da memória, isto tudo para cada requisição cliente.
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.
Com o Delphi 3 Client/Server podemos desenvolver quaisquer destes aplicativos para WEB, sem termos que nos preocupar com os detalhes inerentes a cada uma destas API's. Seu esquema é bem simples e poderoso, graças à sua "Orientação à Objetos". Um aplicativo WEB é na realidade um objeto descendente da classe TWEBApplication, que para cada chamada irá criar dois novos objetos descendentes de TWEBRequest e TWEBResponse, para tratar respectivamente das requisições e das respostas. Dessa forma ficamos completamente afastados de toda a complexidade que é a programação a nível de API de um servidor WEB, como tinha que ser feito com o Delphi 2.
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.
Nós podemos criar quatro tipos de aplicativos para WEB, chamados de "WEB Server Extensions", cada tipo usa um descendente específico de TWEBApplication, TWEBRequest e TWEBResponse.
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:
File -> New -> Web Server Application
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.
Existe uma propriedade no TWEBModule chamada Actions, que uma vez acionada invoca o Action Editor, conforme podemos ver na figura abaixo:
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 |
ele executa o programa myappl, seja uma DLL ou um EXE. É neste ponto onde se dá a verdadeira diferença entre o ISAPI e o CGI, ou seja, no modo como o servidor WEB "executa" cada um deles. No caso da DLL, ela será carregada apenas na primeira chamada, permanecendo na memória como uma extensão do servidor até ocorrer um "shutdown" do serviço WEB. Nosso TWEBModule é um só, enquanto que os Action Items são criados a cada requisição, como uma nova thread. Por este motivo o evento do TWEBModule chamado OnCreate não será executado para toda requisição, más no EXE sim, este seria então o local ideal para uma conexão com um Banco de Dados Cliente/Servidor. Já o evento BeforeDispatch irá ocorrer para todas as requisições, independente do tipo de aplicativo WEB, sendo o local ideal para a abertura das tabelas ou uma configuração de alguma característica que deva ser reinicializada a cada execução.
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 |
A partir deste momento o TWEBModule procura na sua lista de Action Items por um que atenda às especificações da requisição. Isto é feito através da comparação das propriedades PathInfo e MethodType de cada Action Item com as propriedades de mesmo nome no objeto TWEBRequest ( representa as informações da requisição ).
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:
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.
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.
Um aplicativo WEB é basicamente um WEBModule com um Action Item para cada tipo de requisição que tratamos, dessa maneira se tivermos 4 requisições diferentes usaremos 4 Action Items. Veja a tabela abaixo:
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 |
Cada Action Item possui um evento OnAction, que será executado quando chegar a requisição, recebendo dois objetos representando os dados do pedido e da resposta: TWEBRequest e TWEBResponse.
Quando uma requisição HTTP chega ao TWEBModule, os parâmetros referentes a esta requisição são convertidos em propriedades de um descendente de TWEBRequest ( dependendo do tipo de aplicativo ). Aquí estão algumas de suas propriedades mais importantes.
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. |
Se quiser obter mais detalhes ou até mesmo ver as outras propriedades, dê uma olhada no Help do Delphi 3.
Da mesma forma que uma requisição gera um objeto TWEBRequest, é criado também um objeto TWEBResponse, o qual deve ser preenchido e retornado ao Browser cliente. Vamos ver algumas propriedades e métodos mais importantes.
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. |
Todas estas propriedades e métodos serão usados nos exemplos que vamos ver a partir de agora.
Um ISAPI simples, com apenas um Action Item, o Default, retornando as propriedades do TWEBRequest.
procedure TWebModule1.WebModule1WebActionItem1Action(Sender: TObject; Request: TWebRequest; Response: TWebResponse; var Handled: Boolean); Var slValores: TStringList; begin slValores := TStringList.Create; Try slValores.Add(''); slValores.Add('Conteúdo de TWEBRequest '); slValores.Add('Valores da sua Requisição:
'); slValores.Add('Authorization = '+ Request.Authorization +'
' ); ... ... slValores.Add('Date = '+ FormatDateTime('dd/mm/yyyy - hh:nn', Request.Date ) +'
' ); slValores.Add(''); // Response.Content := slValores.Text; // Ok - o WebModule vai enviar a resposta Finally slValores.Free; End; end;
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.Assim:
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.
Neste exemplo veremos como construir um contador de página HTML, usaremos o SendStream para retornar uma imagem no formato JPEG. Este é bem simples, más pode ser alterado para se obter uma versão mais configuirável. Veja abaixo o OnAction de nosso único Action Item.
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.
<p>Esta
página foi acessada <img
src="/scripts/webcounter.dll">
vezes.</p>
Para transformarmos este exemplo em CGI, no caso de termos que 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.
<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.
O SendRedirect pode ser usado da seguinte maneira:
Quando possuímos um link em nossa página para uma outra de um
dos nossos patrocinadores, e desejamos saber quantas pessoas
chegaram até lá via nosso link.. Por exemplo, suponha, que
tenhamos um link para a página da Borland e que necessitamos
saber quantos acessos foram feitos à Borland através de nossa
página.
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:
<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
Ficar gerando página HTML através de uma TStringList não é nada agradável, más não se preocupe, o Delphi 3 possui alguns objetos que nos ajudam na tarefa de produzir uma resposta em forma de página HTML. Estes objetos derivam de TCustomContentProducer e, como eles, nós podemos escrever nossos próprios geradores de conteúdo derivados.
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.
Este componente é muito utilizado na geração de páginas HTML baseadas em uma outra página HTML padrão, este template pode existir no disco, como um arquivo, ou na memória apenas. Por exemplo, podemos possuir uma página de resposta padrão a uma transação efetuada pelo usuário, más gostaríamos de personalizá-la colocando o nome do mesmo.
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 |
Vale
ressaltar que o Delphi não oferece nenhum processamento especial
para estas Tags predefinidas, elas servem apenas para
nos ajudar no processo de conversão. Dessa maneira cabe a nós
transformar um Tag do tipo tgLink em uma sequência do tipo
'href="www.borland.com">Borland WebSite</A>'.
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.
Neste exemplo nós vamos fazer uso de um FORM HTML, onde iremos receber a entrada de dados do Usuário e submetê-las ao nosso aplicativo WEB para que ele possa gerar uma página de agradecimento personalizada. Vale ressaltar que nossa linha de definição do FORM HTML é a seguinte: