Criptografia em AdvPL – Parte 08

Introdução

No post anterior, vimos alguns pacotes de rede e requisições feitas com HTTP e com HTTPS, e um FAQ com algumas perguntas intrigantes e respectivas respostas. Agora, vamos aproveitar que já temos um certificado digital de testes e habilitar a conexão segura entre o TOTVS SmartClient e o TOTVS Application Server ?!

Posts anteriores

Conexão SmartClient com SSL

A seção de configuração [sslconfigure] do TOTVS Application Server foi criada para definir as configurações default para uso de criptografia na infraestrutura do servidor de aplicação, e auxiliar na portabilidade de funções nativas da linguagem para suporte a protocolos seguros. Ela possui diversas chaves de configuração, o que vimos até agora no post 06 foi apenas configurar o uso de um certificado digital e uma chave criptográfica em modo “servidor”, utilizada pela engine de servidor WEB nativa da plataforma para publicar um site ou serviço acessado por HTTPS.

Porém, também podemos habilitar uma conexão segura e criptografada entre o SmartClient e o Application Server, e quando fazemos isso, o certificado configurado como “ServerCertificate” e a sua chave correspondente serão usados para permitir essa implementação.

Configuração DRIVERS

No arquivo de configuração do TOTVS Application Server (appserver.ini), existe uma seção chamada [drivers], responsável por indicar a seção TCP para especificar a porta de conexão a ser usada pelo TOTVS SmartClient, Monitor, IDE/TDS e afins. Para habilitar a conexão segura para o SmartClient, acrescentamos as chaves em negrito:

[drivers]
active=tcp
secure=ssl

[tcp]
type=tcpip
port=12010

[ssl]
type=tcpip
port=13010

Pronto, com isso o Application Server vai usar a porta 13010 para atender a conexões seguras de SmartClient. Lembre-se que o certificado digital e a chave deve estar configurados na seção SSLConfigure, senão o Application Server não inicia…

[sslconfigure]
CertificateServer=C:\Protheus12LG\certificates\note-juliow-ssd.cer
KeyServer=C:\Protheus12LG\certificates\note-juliow-ssd.key

Para as conexões do SmartClient, pode ser usado o mesmo certificado que foi gerado para o HTTPS, inclusive pode até ser um certificado mais “simples”, sem as extensões obrigatórias que um navegador Web exige.

E uma vez feito isso e reiniciado o Application Server, você não precisa fazer nada nas configurações do SmartClient. O Application Server continua aceitando as conexões do SmartClient na porta da seção TCP, porém ao receber uma conexão de SmartClient na porta não segura, o próprio Application Server sabe que existe uma porta segura de conexão, então ele devolve para o SmartClient uma mensagem de reconexão segura, informando a porta configurada na seção SSL 😀

Quando o Application Server com estas configurações for iniciado, repare que no log de console devem aparecer algumas novas mensagens:

Secure Application Server.

[ERROR][STORE] Certificates loaded from [C:\Protheus12LG\certificates\note-juliow-ssd.cer].
[INFO ][SERVER] [Thread 11048] Secure Server started on port 13010.

The callback state information is active.
The PassPhrase is supplied.

*** Existe uma mensagem informando “ERROR” no log acima, que provavelmente está equivocada, deveria ser “INFO” — pois o certificado foi carregado com sucesso.

Configurando o SmartClient para conectar direto na porta segura

Embora apenas a configuração do Application Server já resolve a questão de conexão segura, existe uma forma de você configurar o SmartClient para conectar direto na porta segura, assim o SmartClient não precisa conectar na porta não segura para receber o número da porta segura e reconectar. Basta editar o arquivo de configuração do SmartClient (smartclient.ini), e criar uma nova seção de configuração de conexão, informando na seção a porta segura, e acrescentando a configuração SecureConnection=1, por exemplo:

[drivers]
active=tcp

[tcp]
server=note-juliow-ssd
port=12010

[ssltest]
server=note-juliow-ssd
port=13010
SecureConnection=1

Ao executar o SmartClient, basta trocar a conexão tcp para ssltest, e ele conectará direto na porta de conexão segura especificada, veja imagem abaixo:

Smartclient_ssl

Informações adicionais de depuração de SSL

Caso exista algum problema com uso de certificado ou criptografia no TOTVS Application Server, em qualquer funcionalidade que use chaves criptográficas e certificados e afins, é possível colocar na seção [sslconfigure] a chave verbose=1 . Com isso qualquer processo que envolver a camada de segurança e criptografia do Application Server vai gerar mensagens adicionais de diagnóstico no log de console do servidor. Vale lembrar que isso deve ser ligado apenas para fins de depuração, pois ela pode gerar muito conteúdo adicional e os detalhes gerados são apenas úteis para levantar mais informações sobre um problema relacionado a este mecanismo.

A seção [sslconfigure] possui muito mais chaves, se você está curioso em saber todas elas, veja os links de referência no final do post para a documentação oficial do TDN. Cada post explorando um novo recurso ou funcionalidade vai abordar sob demanda as chaves necessárias e explicando mais detalhes do seu funcionamento 😀

Conclusão

Com o que vimos até agora, já foi possível testar conexões seguras de HTTPS e SmartClient. No próximo post, vamos ver um pouco mais sobre os certificados digitais, e onde as funções do AdvPL se encaixam no uso desses certificados para diversos fins.

Agradeço novamente pela audiência, e desejo a todos TERABYTES DE SUCESSO !!! 

Referências

 

Criptografia em AdvPL – Parte 06

Introdução

No post anterior, vimos o procedimento passo a passo para instalar a biblioteca de ferramentas OpenSSL 1.1.1c no Windows. Agora, vamos abordar o primeiro caso de uso: Como gerar um certificado digital para testes do TOTVS Application Server como um servidor HTTP com conexão segura (HTTPS) 😀

Posts anteriores

O que é HTTP e HTTPS ?

Bem, HTTP é o protocolo de comunicação criado sobre uma conexão TCP/IP, usado entre os navegadores de internet (ou Web Browsers) e sites que você acessa. O protocolo HTTP permite a transferência de vários tipos de conteúdos, desde páginas HTML estáticas, imagens, vídeos , arquivos, páginas dinâmicas, etc… Os dados podem ser trafegados em diversas codificações, inclusive compactados. Mas o HTTP usa uma conexão TCP “simples”, não segura. Desse modo, existem utilitários que permitem “farejar” conexões de rede (Sniffers) que podem interceptar e ver os conteúdos dos pacotes de dados trocados entre o navegador de Internet que você usa e o site que você está acessando.

Quando você acessa  um site que pode prover uma conexão segura, criptografada, o protocolo HTTPS é utilizado — o sufixo “S” significa “Secured”. A conexão segura é feita sobre SSL/TLS, onde no início da conexão existe uma etapa chamada de “Handshake” (aperto de mãos) entre o navegador e o servidor do site, onde eles literalmente “negociam” uma conexão criptografada — vamos ver mais pra frente os detalhes dessa negociação.

Uma vez estabelecida a conexão segura, os dados transferidos entre o navegador e o site são apenas encapsulados em pacotes codificados — criptografados –, mas ao serem decodificados, as mensagens trocadas estão no padrão HTTP. Apenas o meio de comunicação faz a ponte segura entre ambos.

TOTVS Application Server como servidor HTTP

Nos posts abaixo, eu explico como configurar o TOTVS Application Server como um servidor HTTP, recomendo essa leitura antes de prosseguir:

Refrescou a memória ? Legal, vamos partir da configuração inicial do TOTVS Application Server como um servidor HTTP de páginas estáticas, e criar a configuração para ele também aceitar conexões seguras. O Arquivo de configuração appserver.ini deve ficar assim:

[http]
enable=1
port=80
path=c:\Protheus12LG\Http\note-juliow-ssd

[https]
enable=1
port=443
path=c:\Protheus12LG\Http\note-juliow-ssd

Dessa forma eu configuro o Application Server para ser, ao mesmo tempo, servidor para conexões HTTP (não seguras) e HTTPS (seguras). As portas default usada para conexões HTTP e HTTPS são 80 e 443, respectivamente.

Dentro da pasta c:\Protheus12LG\Http\note-juliow-ssd, eu vou colocar apenas um arquivo, chamado index.html. Trata-se de um arquivo texto simples, que pode ser criado com o Bloco de Notas do Windows, apenas com o seguinte conteúdo para teste:

<html>
<body>
<h1><center>Olá Mundo HTTP do Protheus
</h1></center>
</body>
</html>

Agora, quando você tenta subir o TOTVS Application Server em modo console ou serviço … ele não sobe 😀 Aparece a tela de console, e desaparece rapidinho … quando abrimos o log de console ( console.log ) do servidor, encontramos as seguintes mensagens:

[INFO ][SERVER] [Thread 17340] HTTP Server started on port 80.

Http server is ready.
Root path is c:\protheus12lg\http\note-juliow-ssd\
Listening port 80 (default)

[INFO ][SSL] [tSSLSocketAPI][Initialize] Initializing SSL/TLS.
[ERROR][SSL] [tSSLSocketAPI][Initialize] There is no configuration of SSL certificates for the server.
[FATAL][SERVER] [14/08/2019 19:54:29][Thread 17340] *** HTTPS SERVER FAILED TO START ***

[INFO ][SERVER] [Thread 17340][14/08/2019 19:54:29] Application SHUTDOWN in progress...

Bem, como eu configurei o TOTVS Application Server para ser um servidor de conexões seguras para HTTPS, eu preciso ter um certificado digital configurado para isso. Bem, como o HTTPS ainda não sobe, altere para 0 o valor da configuração Enable da seção [https], e tente iniciar novamente o TOTVS Application Server.

[INFO ][SERVER] [Thread 1092] HTTP Server started on port 80.

Http server is ready.
Root path is c:\protheus12lg\http\note-juliow-ssd\
Listening port 80 (default)

Ótimo, agora parou “de pé” 😀 Vamos testar o acesso para ver a página Index.html no Browser. Você pode usar o nome do seu computador na URL do browse. Se você não lembra ou não sabe o nome do seu computador na rede, utilize o prompt de comando (cmd.exe) e digite echo %COMPUTERNAME%:

c:\>echo %COMPUTERNAME%
NOTE-JULIOW-SSD

No caso foi mostrado o nome do meu notebook. Agora, abrimos qualquer navegador (Chrome, Firefox, Internet Explorer, Opera ,…) e montamos uma URL com “http://&#8221; + o nome do computador+’/index.html’. No meu caso, http://note-juliow-ssd/index.html , e vejamos abaixo o que deve aparecer no Browse:

ola mundo http

Eu abri a URL usando o Google Chrome … reparem no texto circulado em vermelho: “Não seguro”. O navegador avisa que a conexão estabelecida não é “segura” — isso indica que a conexão atual não usa criptografia.

Como implementar e fazer funcionar o HTTPS ?!

Então, voltando pra conexão segura, temos basicamente duas etapas para conseguir habilitar o HTTPS no TOTVS Application Server:

  • Arrumar um certificado digital
  • Configurar o certificado para o Protheus usar.

Inicialmente, um certificado digital “oficial” para um servidor HTTP deve ser gerado por uma Autoridade Certificadora (ou CA – Certification Authority ). Entre as autoridades certificadoras brasileiras estão a Certisign, Serasa Experian e outras — para a lista completa veia o link https://www.iti.gov.br/icp-brasil/estrutura

Basicamente um certificado oficial deve possuir uma cadeia de autenticação — são certificados intermediários usados pelos navegadores para confirmar a autenticidade do certificado digital em uso pelo servidor HTTPS. Como não vamos publicar um site oficial, mas sim um teste de conexão segura, vamos a nossa primeira missão usando a OPENSSL: Criar um certificado digital auto-assinado 😀

Este tipo de certificado pode ser gerado por você mesmo, e da forma que ele é gerado, ele mesmo “se autentica” — na prática, é como eu criar um certificado digital com o os dados e propriedades que eu mesmo preencho, e eu mesmo digo que “esse certificado é de verdade” 😀

Criando um certificado auto-assinado para testes usando OPENSSL

Primeiramente, apenas em caráter informativo … Eu apanhei que nem “gato no saco” até chegar a uma receita de bolo que fizesse o navegador aceitar o certificado. As informações mais importantes são as seguintes:

  • O certificado requer parâmetros adicionais — chamados de “extensões” — para que o navegador entendesse que o certificado era para um site com o nome da minha máquina — note-juliow-ssd
  • O navegador de internet, por questões de segurança, verifica se o certificado possui uma Autoridade Certificadora válida, que não seja “eu mesmo”. Logo, a única forma de eu fazer o navegador ‘confiar” no meu certificado foi importar ele pelo gerenciador de certificados do próprio navegador — no meu caso o Chrome — como um “Trusted Root Certificate Authority”.

Agora, vamos para a receita de bolo — lembre-se que é necessário ter a ferramenta OPENSSL instalada para gerar a chave e o certificado. E, para o certificado que você gerar funcionar no seu computador, lembre-se de trocar note-automan-ssd para o nome do host do seu equipamento, e seguir os passos abaixo:

Passo 1:

Crie uma pasta no seu computador para guardar a chave e o certificado a serem gerado e utilizados, por exemplo c:\Protheus12LG\certificates

Passo 2:

Na instalação da OPENSSL, foi criada uma variável de ambiente apontando para o arquivo de configuração da openssl — no meu caso, “C:\Program Files\OpenSSL-Win64\bin\openssl.cfg”. Confirme o aquivo de configuração usado na sua instalação, execute o seu editor de textos ( notepad ou notepad++ por exemplo) em Modo “Administrador” — senão você não consegue mexer nesse arquivo — e abra o arquivo de configuração no editor, vá no final do arquivo, e acrescente o conteúdo abaixo:

[ v3_self_signed ]

authorityKeyIdentifier=keyid,issuer
basicConstraints=CA:FALSE
keyUsage=digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment
subjectAltName=DNS:note-juliow-ssd

Com isso, eu criei uma seção de configuração nova para colocar no meu certificado os dados necessários para o Browse entender que eu vou usar esse certificado para conexão segura em um site de testes na minha máquina ( note-juliow-ssd ). Salve o arquivo e feche o editor.

Esse passo aqui é que deu trabalho descobrir — sem ele, mesmo que eu importasse o certificado como “confiável”, o Chrome retornava o erro “ERR_CERT_INVALID_COMMON_NAME”, como se o certificado não tivesse as informações corretas para ser usado em um site usando o nome da minha maquina … 

Passo 3:

Abra um prompt de comando (cmd.exe) no Windows, entre na pasta onde você vai gerar o certificado digital e a chave privada do certificado, e execute o comando abaixo:

openssl req -x509 -nodes -newkey rsa:4096 -keyout note-juliow-ssd.key -out note-juliow-ssd.cer -days 365 -subj “/C=BR/ST=SP/L=Sao Paulo/O=Tudo em AdvPL/CN=note-juliow-ssd” -extensions v3_self_signed -reqexts v3_self_signed

Com esse pequeno monstrinho, em apenas uma etapa eu consigo fazer a OpenSSL criar para mim uma chave privada de 4096 bits, usando criptografia RSA, gravar a chave SEM SENHA no arquivo note-juliow-ssd.key, criar um certificado no padrão x509 usando esta chave, com validade de um ano, colocando alguns dos meus dados no certificado, e acrescentando as extensões necessárias que colocamos no passo 2 na configuração da OpenSSL, salvando o certificado criado no arquivo note-juliow-ssd.cer

Agora vamos ver o resultado:

C:\>cd Protheus12LG\certificates

C:\Protheus12LG\certificates>openssl req -x509 -nodes -newkey rsa:4096 -keyout note-juliow-ssd.key -out note-juliow-ssd.cer -days 365 -subj "/C=BR/ST=SP/L=Sao Paulo/O=Tudo em AdvPL/CN=note-juliow-ssd" -extensions v3_self_signed -reqexts v3_self_signed
Generating a RSA private key
.......................................................................................................++++
..........................++++
writing new private key to 'note-juliow-ssd.key'
-----

C:\Protheus12LG\certificates>

Para conferir o conteúdo do certificado que foi gerado, executamos o seguinte comando:

openssl x509 -text -in note-juliow-ssd.cer -noout

E, abaixo vamos ver o que foi retornado:

Certificate:
Data:
Version: 3 (0x2)
Serial Number:
5e:a9:76:38:db:c0:3c:5e:cf:f6:8b:e0:cf:7c:b8:5c:ad:87:59:d9
Signature Algorithm: sha256WithRSAEncryption
Issuer: C = BR, ST = SP, L = Sao Paulo, O = Tudo em AdvPL, CN = note-juliow-ssd
Validity
Not Before: Aug 16 00:06:19 2019 GMT
Not After : Aug 15 00:06:19 2020 GMT
Subject: C = BR, ST = SP, L = Sao Paulo, O = Tudo em AdvPL, CN = note-juliow-ssd
Subject Public Key Info:
Public Key Algorithm: rsaEncryption
RSA Public-Key: (4096 bit)
Modulus:
00:bc:5b:56:04:e2:8a:58:88:65:22:86:ce:33:42:
c4:15:1a:ca:9b:4e:ad:7e:7b:42:92:7e:64:e9:3b:
5c:30:f9:de:34:b0:c3:fb:e2:62:69:e7:d7:3c:f1:
cb:78:c2:84:da:15:c3:2b:22:2c:30:7d:71:3f:d4:
47:34:0a:39:50:d4:8f:bc:fa:17:0b:ee:6e:38:28:
1a:06:b3:38:82:51:64:bf:7b:d0:75:ea:fd:e1:f9:
85:ac:21:fa:f8:ee:fb:b5:79:6a:02:f5:83:15:fa:
95:b5:18:1f:76:e7:57:41:a1:f7:84:23:bd:8c:31:
ee:02:c6:58:43:6a:40:ff:24:41:b6:33:ce:eb:7b:
ad:14:d0:43:cf:bf:75:ec:6d:5a:be:94:83:6e:d7:
71:ec:40:b8:68:36:c8:2a:90:9c:d0:6e:64:9c:e6:
3e:28:6e:c5:3b:70:a4:c5:7d:ff:91:0d:f8:f2:f7:
27:dd:cd:04:06:2c:3a:ce:78:23:d3:43:2f:49:77:
e5:4e:13:2d:18:8c:43:5d:2e:7d:70:c4:db:5f:dc:
9a:b1:e6:05:37:14:f8:82:8a:5a:0c:47:22:07:ab:
61:20:43:83:b5:cc:8f:09:93:5f:b4:32:9b:19:b9:
50:35:91:2b:bb:8b:f7:5c:a6:7b:1e:29:b4:73:0d:
f8:93:e9:d6:9a:90:f8:49:a4:ab:89:8a:56:9f:8f:
6b:e1:7b:64:b4:aa:f8:11:6b:b9:92:66:aa:46:04:
5d:bb:5d:17:52:e1:17:36:86:80:70:1f:64:45:47:
6f:12:f2:cb:37:20:2b:6c:36:32:a7:ed:5a:9b:a9:
fa:47:34:a8:1a:79:8d:1d:84:e1:29:9e:a9:64:97:
f3:83:64:79:9e:9d:72:2a:05:0d:4e:58:ec:ff:2c:
d7:c5:a2:5c:b2:e6:08:26:8e:17:12:e3:99:2e:50:
7f:a6:3f:ef:6c:aa:45:08:0d:f8:db:fc:15:f4:42:
29:2d:13:d1:4c:2b:3e:72:1f:e9:0c:2a:bd:38:60:
5c:27:dd:63:7c:32:ac:c0:f0:06:85:1a:dc:64:67:
d9:44:bc:bc:83:fc:65:2a:1a:0c:04:77:ea:29:06:
f8:de:35:ba:4b:cd:23:c6:87:11:bf:4d:cc:d8:d0:
20:54:28:50:dd:b2:e3:21:f2:9d:67:1a:27:11:15:
6e:92:ac:3c:ac:66:70:86:31:bc:55:39:85:fe:9f:
65:02:29:d6:cc:36:51:f2:e2:15:93:13:6f:a1:b5:
47:af:cc:20:c7:94:fe:44:37:59:90:98:46:fd:be:
7a:1c:c1:87:7a:57:94:05:db:0a:60:f3:ca:42:1b:
01:89:35
Exponent: 65537 (0x10001)
X509v3 extensions:
X509v3 Authority Key Identifier:
DirName:/C=BR/ST=SP/L=Sao Paulo/O=Tudo em AdvPL/CN=note-juliow-ssd
serial:5E:A9:76:38:DB:C0:3C:5E:CF:F6:8B:E0:CF:7C:B8:5C:AD:87:59:D9

X509v3 Basic Constraints:
CA:FALSE
X509v3 Key Usage:
Digital Signature, Non Repudiation, Key Encipherment, Data Encipherment
X509v3 Subject Alternative Name:
DNS:note-juliow-ssd
Signature Algorithm: sha256WithRSAEncryption
78:55:d6:bf:57:6e:69:8b:c6:7e:27:7d:bc:e6:df:bc:17:26:
62:3b:73:e5:a5:46:51:6d:09:fa:45:c6:c3:6f:d3:9f:70:d3:
cb:2c:42:c8:48:fa:b5:ec:14:8a:d9:4c:c8:d9:eb:ab:bb:3a:
d7:35:e5:ad:88:90:1c:2d:91:ea:08:e1:87:f9:b8:dc:d4:d7:
58:77:8f:6b:b9:da:65:3c:e3:7a:fb:08:91:94:c4:43:08:53:
64:1f:ad:f9:93:66:bf:fa:58:af:25:a7:09:c8:f4:a9:77:fb:
4e:f5:33:02:f8:86:26:6e:f6:77:d1:7c:74:be:6d:09:4d:0e:
4a:38:44:08:bd:b8:a2:d3:85:c3:e7:a0:1d:81:57:3e:40:cc:
0e:08:8a:60:56:b7:c2:27:78:c1:6a:30:1e:58:ab:ab:44:a4:
9e:c6:7b:15:a9:e6:14:65:e0:b2:88:85:66:f1:cc:32:34:6b:
d9:a6:f5:e7:3d:5b:b5:a9:a8:a4:82:17:48:4e:f3:6d:26:80:
3c:72:66:23:c9:ee:d3:ed:0b:fe:0d:8d:04:b5:17:da:01:f4:
20:21:79:0f:18:7b:04:cd:c6:90:5f:a4:71:82:bd:90:d5:ce:
f0:c2:ea:1c:70:f7:5f:3c:d6:1c:cd:54:eb:7d:70:75:03:7c:
3f:49:02:3c:65:69:89:a2:c2:aa:9a:62:64:45:2f:e2:51:c2:
8f:d5:00:85:a1:e6:ec:48:4d:bb:5f:4f:09:73:4f:56:70:54:
04:0b:60:5f:2c:c0:cb:61:45:3b:5c:ff:cc:ac:67:9f:2a:67:
12:5e:38:16:af:b3:96:f1:24:2f:27:fe:63:9f:0b:d6:11:31:
08:7c:26:b9:2d:48:66:0b:b8:c3:4e:b3:0f:62:f7:79:17:0f:
a8:c7:fa:71:ad:40:7a:2c:92:a5:d8:5b:b9:ff:a5:4e:d7:88:
ad:59:ab:c4:1d:9b:ed:52:20:d0:20:70:dc:ed:09:03:f9:4e:
5d:5b:ed:51:86:74:d5:8d:62:ab:cc:5a:45:00:9d:28:d2:45:
bc:02:c1:c8:76:15:94:04:34:fe:68:69:52:72:68:13:31:ca:
6b:ac:17:0e:2c:3d:f0:4e:b8:ba:b3:48:f2:06:5b:e6:aa:be:
35:e8:81:48:78:14:3b:f6:48:eb:13:ef:bd:9b:3f:83:1c:4e:
24:be:b7:b8:c6:30:c0:00:cb:6c:74:50:3e:3d:84:d3:a5:ce:
fb:6e:ac:2c:62:eb:5f:96:d4:77:8a:46:e0:9f:6c:43:13:e9:
4f:09:3b:e1:9a:12:b6:35:83:19:8e:4b:59:78:a5:9f:cd:fc:
6d:ce:d6:53:fc:72:15:db

Se verificarmos o diretório atual, veremos que os dois arquivos — chave privada e certificado — foram criados. UFA … agora, vamos configurar o certificado e a chave privada para o Protheus utilizar e virar servidor HTTPS.

Passo 4:

Abra o arquivo de configurações do TOTVS Application Server ( appserver.ini ) , e acrescente o conteúdo abaixo:

[sslconfigure]
CertificateServer=C:\Protheus12LG\certificates\note-juliow-ssd.cer
KeyServer=C:\Protheus12LG\certificates\note-juliow-ssd.key

Com isso, eu informo ao Protheus que ele pode ser um servidor de conexão segura, usando o certificado e a chave privada informadas na configuração. Por hora é tudo o que precisamos saber e fazer pra mágica funcionar.

Aproveite que o arquivo de configuração está aberto, configure novamente a chave Enable=1 na seção [HTTPS] e salve o arquivo de configuração.

Passo 5: 

Inicie novamente o TOTVS Application Server. Se tudo estiver certo, agora ele tem que subir "certinho"

[INFO ][SERVER] [Thread 13104] HTTP Server started on port 80.

Http server is ready.
Root path is c:\protheus12lg\http\note-juliow-ssd\
Listening port 80 (default)

[ERROR][STORE] Certificates loaded from [C:\Protheus12LG\certificates\note-juliow-ssd.cer].
[INFO ][SERVER] [Thread 13104] HTTPS Server started on port 443.

Https server is ready.
Listening port 443 (default)

Passo 6: 

Abra o Chrome, e informe a URL de acesso da página de testes, mas agora usando HTTPS ao invés de HTTP

https://note-juliow-ssd/index.html

E, para nossa surpresa ….

chrome_cert_authority_invalid

O navegador de internet fez o “HandShake” com o HTTPS do Protheus, e viu que o certificado usado não tem uma Autoridade Certificadora válida. Logo, ele avisa que o site que você está acessando pode ser uma roubada …. Reparem no erro no meio da tela : ERR_CERT_AUTHORITY_INVALID. Como o certificado foi eu mesmo que gerei, e está na minha máquina, e é para testes, eu posso fazer o navegador “confiar cegamente” no meu certificado e funcionar redondinho sem mostrar mais essa mensagem.

Passo 7:

Vamos importar o certificado de testes utilizado para dentro do navegador, dizendo pra ele que este certificado é um “Trusted Root Certificate” — ATENÇÃO : PELO AMOR DE DEUS, NÃO FACA ISSO COM UM CERTIFICADO QUE NÃO FOI VOCÊ QUE GEROU OU QUE VOCÊ NÃO SABE A PROCEDÊNCIA. O certificado que a gente acabou de gerar é confiável por que você gerou ele e vai usar na sua máquina.

No Google Chrome, clique nos três pontinhos do canto superior direito da janela’e clique em “Configurações”. Ao abrir a janela de configurações, expanda a opção “Avançado”, e clique em “Privacidade e Segurança”, e depois em “Gerenciar Certificados”

chrome_avancado

Será mostrada a janela de controle de certificados. Localize e posicione na aba “Trusted Root Certification Authorities”, e depois no botão “Import”

chrome_certificates

Ao clicar em “Import”, será aberto um assistente de importação de cerificado digital:

chrome_import_1

Clique em “Next”, e na próxima tela informe o caminho completo do certificado a ser importado, ou use o botão “Browse” para localizar e selecionar o certificado que acabamos de criar.

chrome_import_2

Ao clicar em “Next” será mostrada uma tela para perguntar onde o certificado deve ser importado. Já vi vir preenchido com “Trusted Root Certification Authorities”, clique em “Next”.

chrome_import_3

Chegamos na última tela, o assistente mostra o resumo do que será feito. Clique em “Finish”.

chrome_import_4

Ao clicar em Finish, é mostrado um aviso de segurança, dizendo que você está para instalar uma Autoridade Certificadora que não pode ser verificada — por que não existe — e que diz representar o nome do host da sua máquina, e diz também que se você fizer isso, o Windows vai passar a confiar em qualquer certificado subsequente que seja emitido a partir desse. Pode confirmar 😀

chrome_import_5

Pronto! Insalado 😀

chrome_import_6

Passo 8:

Agora sim, abra novamente a página de testes com o protocolo seguro — pode ser necessário fechar e abrir novamente o navegador para o acesso funcionar. Tudo dando certo, devemos ver isso aqui:

chrome_agora_foi

Repare no “cadeado” destacado em vermelho. Isso informa que, agora sim, finalmente, estamos usando uma conexão SEGURA / CRIPTOGRAFADA. Agora, para ver os detalhes dessa criptografia do ponto de vista do browse, clique no cadeado:

chrome_valido

Vamos ver mais detalhes, clicando em cima da palavra  “Certificado”, e consultar as suas propriedades.

certificate_properties

Na aba de detalhes do certificado, podemos ver todas as propriedades que colocamos ao criá-lo, e como instalamos ele como um certificado confiável na máquina, o Windows agora confia nele 😀

Conclusão

Bem, com apenas esses “poucos passos” conseguimos montar um servidor de páginas HTTP com conexão segura … agora, no próximo post, vou mostrar o que um hacker consegue ver ao rastrear uma conexão não segura e uma conexão segura 😀

Agradeço a todos pela audiência, e lhes desejo TERABYTES DE SUCESSO !!!

Referências

 

 

Utilizando THF com AdvPL – Parte 01

Introdução

O THF (Totvs Html Framework) é um projeto aberto e colaborativo de um Framework para aplicações WEB responsivas e dispositivos móveis. Baseado em Angular e Typescript, disponibiliza uma série de componentes que facilitam a criação de aplicações. O site oficial com tudo sobre o THF é https://thf.totvs.com.br/

É altamente recomendável que você tenha algum conhecimento sobre Angular e TypeScript, senão você corre o risco de ficar meio perdido — assim como eu fiquei — ao tentar usar o THF.

Iniciando

Basta seguir o tutorial do site para instalar o NodeJS e NPM, e o AngularCli. Sem truques, apenas next-next-finish. Uma vez instaladas as aplicações necessárias, uma dica importante na criação de um novo projeto — Ao configurar as dependências do projeto criado para instalação, é disponibilizada uma lista de chaves e valores das dependências no site. Para atualizar o arquivo package.json que foi criado com o novo projeto, procure cada chave informada no site no arquivo gerado para o seu projeto. Caso a chave já exista, atualize o valor da versão, e caso a chave não exista, acrescente ela no arquivo.

Criação do Projeto AgendaTHF

Em posts anteriores, sobre o CRUD AdvPL, foi construída uma agenda simples de contatos para ser acessada via SmartClient. Então, num projeto posterior, foi construída uma interface em AdvPL ASP usando HTML/CSS e JavaScript “puro”, sem framework algum. Vamos ver agora como seria a criação de uma interface para esta Agenda usando o Totvs Html Framework.

Todas as etapas para a criação do projeto foram feitas de acordo com as instruções detalhadas no tópico “Começando com o THF”, inclusive as etapas foram seguidas até o final, com a criação do componente “hello-world” e de um menu do THF para chamá-lo na tela.

Ao criar um novo projeto, devemos abrir um prompt de comando do Windows (cmd) e usar  o comando abaixo: — onde AgendaTHF é o nome do projeto.

ng new AgendaTHF --skip-install

Após reconfigurar as dependências e versões de dependências no arquivo package.json, gerado dentro da nova pasta AgendaTHF — criada automaticamente para o Projeto — executamos o comando de instalação das dependências, e depois a instalação do THS neste projeto, executando os seguintes comandos dentro da pasta AgendaTHF:

npm install
npm install @totvs/thf-ui --save

Não se assuste, a instalação de tudo pode demorar mais de um minuto. As sub-pastas com os componentes instalados somam quase 300 MB de arquivos.

Configuração do módulo principal e demais etapas

Procure o arquivo “app.module.ts“, ele deve estar dentro da pasta “AgendaTHF\src\app“. Neste arquivo é que deve ser acrescentado o import do THFModule — os detalhes estão no Passo 2.2 do tutorial “Começando com o THF”

Os demais procedimentos são relativamente simples, ligados a edição dos arquivos modelo gerados para a aplicação, para adequá-lo ao uso do THF. A criação do componente “hello-world” é feita pelo prompt de comando ma pasta raiz do projeto (AgendaTHF), e o resultado final — depois de colocar um servidor http no ar usando o comando “ng serve”, deve ser algo assim:

THF Hello World

Teste do Hello World

A primeira impressão é muito bonita, e logo percebe-se a responsividade da interface alterando o tamanho da janela do Browser. Em tela maximizada, a página do Hello World é aberta na área de conteúdo e o menu continua visível. Ao reduzir o tamanho da interface, o menu é automaticamente “escondida”, sendo acessível novamente por um link na parte superior esquerda.

THF Hello World Option

Atualmente existem aproximadamente 81 componentes disponíveis para você codificar uma aplicação responsiva usando o THF — Vide índice de API no link “https://thf.totvs.com.br/documentation“. Além dos componentes, são disponibilizados Serviços, Diretivas, Modelos de Dados — para armazenamento em dispositivo local — enfim, têm muita coisa.

A pasta criada com este projeto é para desenvolvimento. No momento de fazer a publicação, deve-se executar uma instrução de build, explicada no portal do THF, que vai gerar uma pasta com os arquivo estáticos e scripts. A publicação da aplicação exige apenas um servidor HTML estático, o Protheus como servidor HTTP atende perfeitamente — exceto por alguns detalhes de configuração de página não encontrada, que precisam ser customizados.

Conclusão

Este artigo ficou na banheira desde o Natal de 2018, provavelmente a documentação já deve ter mais atualizações e componentes. O projeto da Agenda usando o THF vai ser continuado ao longo dos próximos posts, em breve 😀

Novamente agradeço a audiência e desejo a todos TERABYTES DE SUCESSO !!! 

Referências

 

CRUD em AdvPL ASP – Parte 04

Introdução

No post anterior (CRUD em AdvPL ASP – Parte 03), foi implementada a consulta básica da Agenda em ADVPL ASP — pelo menos os quatro botões de navegação (Primeiro, Anterior, Próximo e Último). Agora, vamos ver como mostrar no Browse a imagem de cada contato, gravada no Banco de Dados.

Trocando o retorno para o Browse

Qualquer link apw solicitado ao Web Server do Protheus executará uma função AdvPL definida em uma configuração de pool de processos, que por default devem retornar uma string em AdvPL, que será interpretada pelo Web Browse como sendo texto HTML.

Este comportamento default é implementado pelo Web Server, que por default informa no Header HTTP de retorno da requisição a informação Content-Type: text/html — Quando utilizamos a função AdvPL HTTPCTTYPE(), dentro do processamento de uma requisição AdvPL ASP, nós podemos TROCAR o tipo do conteúdo de retorno. Por exemplo, se eu quiser retornar uma imagem do tipo PNG para o Web Browse, a partir de uma requisição de link .apw, basta eu chamar a função HttpCTType(“image/png”), e ao invés de retornar ao Browse um HTML, eu retorno o conteúdo (bytes) do arquivo da imagem.

Logo, vamos implementar o retorno da imagem de forma BEM SIMPLES. Primeiro, vamos aproveitar a requisição “agenda.apw”, e verificar se ela recebeu um identificador adicional na URL, que vamos chamar de IMGID. Este identificador vai conter o numero do registro do contato da agenda que nós gostaríamos de recuperar a imagem. E, no fonte WAGENDA.PRW, vamos acrescentar o seguinte tratamento, logo no inicio da função WAGENDA() — pouco depois de abrir a tabela ‘AGENDA” no Banco de Dados.

If !empty(HTTPGET->IMGID)
  DbSelectArea("AGENDA")
  Dbgoto( val(HTTPGET->IMGID) )
  cBuffer := AGENDA->IMAGE
  HTTPCTType('image/png') 
  Return cBuffer
Endif

Simples assm, caso seja recebida a URL agenda.apw?IMGID=nnn, o programa vai posicionar no registro correspondente da tabela de agenda, ler o conteúdo da imagem gravada no campo memo “IMAGE”, e retornar ela ao Browser, avisando antes pela função HTTPCTType() que o Browse deve interpretar este conteúdo como uma IMAGEM.

Agora, dentro do fonte WAGENDA.APH, que compõe a página da agenda, vamos fazer uma alteração na tag “img”, responsável por mostrar a imagem do contato atual da agenda.

<tr><td>
<%If HTTPPOST->_SHOWRECORD .and. !Empty(AGENDA->IMAGE) %>
<img style="width: 120px;height: 160px;" src="agenda.apw?IMGID=<%=cValToChar(recno())%>">
<% else %>
<img style="width: 120px;height: 160px;" src="./images/Agenda_3x4.png">
<% Endif %>
</td></tr>

Dentro do APH em questão, eu já estou com a tabela da Agenda aberta. Caso eu vá mostrar o registro de algum contato no Browse, e o campo de imagem deste contato possua conteúdo, eu coloco que a imagem deve ser buscada no endereço

agenda.apw?IMGID=<%=cValToChar(recno())%>

Desta forma, quando o Browse receber o HTML de retorno de uma página da Agenda, o Browse vai desenhar a tela, e na hora de mostrar a imagem, o Browse internamente dispara mais uma requisição para a a URL agenda.apw, informando via URL o identificador da imagem desejada.

Vou fazer um teste no Browse, por exemplo retornando a foto do registro 2 da agenda no meu ambiente, digitando direto a URL “http://localhost/agenda.apw?IMGID=2

WEB Agenda IMGID

Ao navegar na agenda, e posicionar no contato, a imagem passa a ser atualizada na tela, veja exemplo abaixo:

WEB Agenda Imagem

Conclusão

Utilizando a troca do retorno, eu consigo informar ao browser o que eu estou retornando, e isso me permite eu retornar por exemplo XML, Imagens, Documentos, o céu (e a String AdvPL) são o limite!

Agradeço novamente a audiência, e desejo a todos TERABYTES DE SUCESSO !!! 

Referências

 

CRUD em AdvPL ASP – Parte 03

Introdução

No post anterior (CRUD em AdvPL ASP – Parte 02), foi mostrado um pouco da forma como as páginas de uma aplicação WEB foram e são atualizadas e o que é o AJAX, e a primeira verão — apenas desing — da página da Agenda em HTML. Agora, vamos começar a adaptar alguns fontes da Agenda para utilizá-los no AdvPL ASP.

Separar processamento da interface

Este é um dos mandamentos do bom desenvolvimento de sistemas. O projeto do CRUD em AdvPL, com foco em uma agenda de contatos, foi escrito da forma mais simples de desenvolver um sistema: O mesmo código é responsável pela interface e pelo processamento e acesso aos dados.

Embora seja uma das formas mais simples e rápidas de desenvolver, o processamento depende das interface, e isso acaba dobrando o trabalho para você criar uma nova interface para fazer as mesmas tarefas. No exemplo do CRUD, o processo em execução é persistente, isto é, o SmartClient possui uma conexão com o Protheus Server, e nesta conexão o arquivo da agenda é mantido aberto e posicionado, e a conexão da aplicação é mantida junto com o seu contexto até o usuário finalizar a interface.

Já em uma aplicação Web, cada chamada de atualização de interface cria uma nova conexão com o servidor de aplicação, que pode ser atendida por qualquer um dos processos (também chamados de Working Threads, ou processos de trabalho), então não há persistência do contexto de execução. Existem apenas as variáveis de seção (HTTPSESSION), onde você pode armazenar informações que são exclusivas daquela seção de navegação, que como vimos nos tópicos sobre AdvPL ASP, podem ser usadas por exemplo para guardar a existência por exemplo de uma seção de login.

Logo, como o paradigma da nova interface não parte de uma conexão persistente, precisamos desamarrar o processamento da interface, e criar algum mecanismo de persistência de um mínimo de contexto, ou emular a persistência de um contexto, para que uma ação disparada pela interface saiba de onde partir para ser executada.

A separação entre processamento e interface será feita em doses homeopáticas, vamos primeiro fazer a implementação funcionar com o que temos disponível implementando alguns pontos por vez.

Iniciando o processo de consulta

Como já temos a agenda criada em AdvPL, e com uma base preenchida, vamos começar a interface WEB pela consulta dos dados da agenda. Partimos de uma tabela no banco de dados, com dois índices previamente criados. Vamos iniciar a visualização dos dados a partir do primeiro registro da tabela em ordem alfabética. Para isso, inicialmente vamos replicar (duplicar) alguns códigos na interface WEB, para realizar as operações de consulta.

Uma vez que seja mostrada ao usuário uma tela com os dados preenchidos, o processamento daquela requisição terminou. Logo, quando eu clicar por exemplo no botão “Próximo” — para mostrar o próximo registro da agenda — eu preciso pelo menos saber qual era o registro que estava sendo visualizado, para eu saber de onde partir para buscar o próximo registro.

Eu até poderia usar variáveis de seção (HTTSESSION), mas como o objetivo é minimizar o uso destas variáveis, eu posso criar dentro do meu formulário — por exemplo — um input em HTML, do tipo “hidden” — ou escondido — onde eu coloco o numero do registro atual que eu estou visualizando, no momento que eu gerei a página a ser retornada para o Browse.

Conectando com o DBAccess

Antes de mais nada, eu preciso que as minhas Working Threads tenham acesso ao Banco de Dados. Lembram-se da função U_ASPINIT(), executada para preparar cada um dos processos para AdvPL ASP? Vamos alterá-la para ela estabelecer logo de cara uma conexão com o DBAccess, que será mantida no processo e aproveitada por todas as requisições que precisarem usar o SGDB. Esta função fica no fonte ASPThreads.prw

User Function ASPInit()
Local nTopHnd

SET DATE BRITISH
SET CENTURY ON

nTopHnd := TCLInk()
If nTopHnd < 0 
  ConsoleMsg("ASPINIT - Falha de conexão "+cValToChar(nTopHnd))
  Return .F.
Endif

SET DELETED ON

ConsoleMsg("ASPINIT - Thread Advpl ASP ["+cValToChar(ThreadID())+"] Iniciada")

Return .T.

Reparem que apenas o trecho em negrito foi acrescentado na função. Agora, vamos implementar alguns controles no programa responsável pela agenda na interface AdvPL ASP — Fonte wagenda.prw

Implementando a consulta nos fontes

Dentro da User Function WAGENDA(), responsável pela interface HTML da Agenda para AdvPL ASP, vamos abrir o alias da tabela da Agenda, caso ele ainda não esteja aberto dentro do contexto do processo de trabalho (Working Thread) atual.

If Select("AGENDA") == 0 
   USE (cFile) ALIAS AGENDA SHARED NEW VIA "TOPCONN"
   DbSetIndex(cFile+'1')
   DbSetIndex(cFile+'2')
Endif
DbSelectArea("AGENDA")

Partimos de duas premissas. A primeira é que a tabela e os índices existem. E, a segunda é que, uma vez que eu deixe o alias da tabela aberto neste processo, as próximas requisições que precisarem consultar esta tabela vão encontrar ela já aberta.

As operações feitas no CRUD em AdvPL foram numeradas. Vamos seguir a mesma numeração. Como por hora estamos implementando apenas a consulta de dados, as operações são: 4 – Primeiro contato, 5 = Contato anterior, 6 = Próximo contato e 7 = Último contato. Para este controle, vamos usar um campo input de formulário escondido no HTML, chamado “OP”. Caso este campo não seja recebido na requisição, eu assumo que a agenda está sendo aberta, e que a operação default é “4” = Primeiro registro.

If empty(HTTPPOST->OP)
  // Caso nao tenha sido informada operação, a operação 
  // default é posicionar no primeiro registro da tabela
  // em ordem alfabérica
  HTTPPOST->OP := '4'
Endif

Antes do fonte chamar a montagem da tela da agenda, encapsulada no arquivo wagenda.aph — correspondendo a chamada de função H_WAGENDA() em AdvPL — vamos tratar as quatro opções possíveis.

If HTTPPOST->OP == '4' // Primeiro

  DBSetOrder(2)
  Dbgotop()
  HTTPPOST->_SHOWRECORD := .T.

ElseIf HTTPPOST->OP == '5' // Anterior

  DBSetOrder(2)
  Dbgoto( val(HTTPPOST->RECNO) )
  DbSkip(-1)
  IF bof()
    HTTPPOST->_ERRORMSG := 'Este é o primeiro contato.'
  Endif
  HTTPPOST->_SHOWRECORD := .T.

ElseIf HTTPPOST->OP == '6' // Próximo

  DbSetOrder(2) 
  Dbgoto( val(HTTPPOST->RECNO) )
  DbSkip() 
  If Eof()
    Dbgobottom()
    HTTPPOST->_ERRORMSG := 'Este é o último contato.'
  Endif
  HTTPPOST->_SHOWRECORD := .T.

ElseIf HTTPPOST->OP == '7' // Último

  DBSetOrder(2)
  Dbgobottom() 
  HTTPPOST->_SHOWRECORD := .T.

Endif

Dentro deste fonte nós criamos duas variáveis dentro do alias virtual HTTPPOST, chamadas de _SHOWRECORD e _ERRORMSG. Estas variáveis foram criadas desta fora para servirem como containers de troca de valores entre o programa AdvPL que estamos rodando agora, e a página AdvPL ASP que vamos rodar em seguida.

Cada operação já assume que o Alias da tabela de AGENDA está aberto, e realiza o posicionamento do registro correspondente. Para fazer o posicionamento no registro anterior ou no próximo do registro mostrado no Web Browse, vamos no AdvPL ASP criar um campo do tipo INPUT HIDDEN, escondido em um formulário de controle invisível, e cada requisição de POST feita para trazer um novo registro vai receber o registro que estava sendo visto no Browse naquele momento.

Implementando no AdvPL ASP

Agora que já preparamos as operações dentro do fonte AdvPL, vamos preparar o fonte AdvPL ASP para tratar a existência destes dados e as operações solicitadas. Primeiro, dentro do fonte WAGENDA.APH, na parte de Scripts no inicio da página, vamos acrescentar duas funções novas em JavaScript.

function onLoad()
{
<%If HTTPPOST->_SHOWRECORD %>
<% aStru := DbStruct() %>
<% For nI := 1 to fCount() %>
<% If aStru[ni][2] != 'M' %>
document.getElementById("I_<%=alltrim(fieldname(nI))%>").value = "<%=cValToChar(fieldget(nI))%>";
<% Endif %>
<% Next %>
<% Endif %>
<% IF !empty(HTTPPOST->_ERRORMSG) %>
window.alert("<%=HTTPPOST->_ERRORMSG%>");
<% Endif %>
};

function CallOp(nOp)
{
document.getElementById("I_OP").value = nOp;
var f = document.getElementById("F_STATUS"); 
f.action="agenda.apw"
f.submit();
}

A primeira função chama-se onLoad(), e vamos fazer o Browse chamá-la imediatamente após a carga da página, inserindo na tag de abertura do corpo da página HTML (body) a chamada desta função:

<body onload="onLoad()" style="font-family:Courier New;font-size: 12px;background-color:rgb(128,128,128);">

O corpo da função onLoad() será montado dinamicamente para cada requisição. Seu objetivo é, quando houver a visualização de um determinado registro, este script vai encontrar todos os campos INPUT da página, destinados a mostrar os valores dos campos da Agenda, e preencher estes campos com os valores lidos do Banco de Dados.

Já a função CallOp() será chamada em JavaScript pelos botões de navegação de consulta na página HTML. Os novos botões vão ficar assim:

<tr><td><a class="agbutton" href="javascript:void(0)" onclick="CallOp(4)">Primeiro</a></td></tr>
<tr><td><a class="agbutton" href="javascript:void(0)" onclick="CallOp(5)">Anterior</a></td></tr>
<tr><td><a class="agbutton" href="javascript:void(0)" onclick="CallOp(6)">Próximo</a></td></tr>
<tr><td><a class="agbutton" href="javascript:void(0)" onclick="CallOp(7)">Último</a></td></tr>

E, finalmente, antes do final do corpo do HTML (/body), vamos acrescentar um formulário de controle, para através dele fazer as requisições via POST — para não deixar rastro, cache ou histórico no Web Browse — das operações solicitadas de navegação.

<form id="F_STATUS" action="agenda.apw" method="post">
<input type="hidden" id="I_OP" name="OP" type="text" value="">
<input type="hidden" id="I_RECNO" name="RECNO" type="text" value="<%=cValToChar(recno())%>">
</form>

Neste formulário vamos criar apenas dois campos de INPUT HIDDEN, um para guardar a operação a ser solicitada, e outro para guardar o registro que está atualmente sendo visualizado. No momento que eu pedir por exemplo o próximo registro, o botão “Próximo” vai executar a função javascript CallOp(6), que vai preencher o input “I_OP” com o valor “6” e submeter novamente o formulário para o link “agenda.apw

Desta forma, o programa U_WAGENDA() vai receber a operação e o registro anteriormente posicionado, reposicionar no registro, fazer a navegação, e se por um acaso não houver registro anterior ou próximo, será mostrada na tela uma mensagem de alerta no Browse, repare no finalzinho da função onLoad().

Implementação funcionando

Com tudo atualizado e compilado, o comportamento esperado do acesso ao link http://localhost/agenda.apw deve ser:

  • Caso o usuário não esteja logado na seção atual, será exibida a tela de login
  • Após o login, deve ser exibida a tela da agenda com o primeiro contato em ordem alfabética.
  • Os quatro botões de navegação devem funcionar atualizando a página inteira e trazendo o contato correspondente.

WEB Agenda 3

Houve mais uma alteração importante no arquivo WAGENDA.APH: Todos os campos INPUT do formulário, que antes estavam com a propriedade “disabled”, passaram para “readonly”. Caso um campo não esteja habilitado, ele sequer pode receber ou mostrar um valor novo.

Conclusão

Aos poucos vamos implementando as funcionalidades da agenda original. Normalmente eu começaria pelo mais complicado — API de Cadastro — porém, como o estado default da interface é a consulta, vamos começar por ela. No próximo post, vamos fazer a consulta atualizar a foto do contato da agenda, sem precisar gravar a imagem que está no banco de dados na pasta de publicações Web.

Agradeço a todos novamente as curtidas, compartilhamentos, comentários e afins, e lhes desejo TERABYTES DE SUCESSO !!!

Referências

 

CRUD em AdvPL ASP – Parte 02

Introdução

No post anterior (CRUD em AdvPL ASP – Parte 01), montamos um controle de login, usando um formulário em AdvPL ASP, e uma variável de seção (HTTPSESSION). E, para servir de base para a continuação do CRUD, foram publicados uma sequência de posts para abordar o “básico” do AdvPL ASP:

Agora, vamos começar a montar a interface em HTML e JavaScript para montar as funcionalidades da Agenda via WEB.

Design da Interface

Poderíamos usar algum framework Web, por exemplo o THF – Totvs Html Framework , mas por hora vamos fazer a tela usando o arroz-com-feijão das páginas WEB com AdvPL ASP, e de quebra ver um pouco mais sobre como as coisas funcionam por dentro, e ver algumas alternativas de desenvolvimento. A interface da agenda está sendo desenhada originalmente para ter um layout muito parecido com o layout do programa original, que roda pelo SmartClient.

A ideia é que a interface HTML permita realizar todas as operações a partir da mesma tela, porém cada operação que requer atualização de dados desta tela deve submeter uma requisição via POST para o Web Server, que receberá novamente a tela inteira para executar a ação desejada.

Atualização de Páginas

Nos primórdios da Internet, navegar em qualquer Web Site na Internet construído com HTML, não tinha a possibilidade de alteração ou atualização dinâmica de conteúdo. Imagine um Web Site com um layout de menu lateral, com vários links e opções, uma área de topo ou Header com um logotipo e algumas informações sobre a parte do site que você está navegando, e uma área de conteúdo mostrando duas ou três notícias. Caso você clicasse em um link para ver a próxima página de notícias, a tela inteira é apagada e recarregada, com os mesmos menus laterais, a mesma área de topo, e uma página nova de notícias.

Isso acabava tornando pesada a navegação em alguns Web Sites. Com a possibilidade de criação de páginas de FRAMES em HTML —  acho que a partir do Internet Explorer 4 — você poderia definir um lay-out com frames — por exemplo um superior, um lateral e um central, onde a carga de uma página de notícias apenas recarregava uma página do frame, colocando as notícias desejadas.

Com as melhorias feitas no JavaScript, e sendo possível alterar dinamicamente o conteúdo de um HTML já desenhado na tela do Web Browser, vários Web Sites usavam um frame “escondido” na tela, onde através dele era feita uma requisição de uma nova página. Nesta página, que na verdade não era visível — frame escondido — era carregado um JavaScript retornado pelo Web Server, para atualizar dinamicamente o conteúdo da página sendo mostrada no frame de conteúdo — esse sim visível — sem a necessidade de recarregar (ou fazer REFRESH) da página inteira.

Algum tempo depois, foram descobertas vulnerabilidades nos navegadores Web relacionados ao uso de Frames, que poderiam mascarar um Web Site malicioso que poderia usar o JavaScript para interagir — e roubar dados e credenciais por exemplo — com frames de outros domínios, e a mecânica de atualização dos Frames tornava mais complicado desde o desenvolvimento do site, até a usabilidade do usuário — como usar o botão Voltar do Browse ou mesmo fazer um BookMark.

AJAX – Seus problemas terminaram

Mais melhorias e implementações foram feitas no JavaScript, e surgiu  o AJAX  —  Asynchronous Javascript And XML. NA verdade, este recurso é a união de duas funcionalidades: Primeira, uma função assíncrona do JavaScript foi criada para enviar ou solicitar informações para um Web Server, sem a necessidade de recarga da página atual. E, quando o Web Server retornar os dados solicitados (não necessariamente precisa ser um XML, pode ser usado texto plano, JSON, …), um JavaScript é acionado para processar os dados da requisição, que podem ser usados para atualizar o conteúdo HTML da página sendo atualmente exibida ao usuário.

Vamos pegar por exemplo o FACEBOOK — Uma vez que você  entra no seu “Feed de Notícias”, a URL permanece a mesma, e conforme você vai rolando a tela para baixo, usando a barra de rolagem do lado direito do Browse, ou mesmo o botão “Scroll” presente hoje até nos modelos mais simples de Mouse, novos posts vão sendo trazidos para a sua tela, sem a necessidade de repintar a tela inteira.

É claro que podemos usar AJAX com AdvPL ASP, inclusive vamos abordar este assunto mais para a frente, no decorrer do desenvolvimento do CRUD em AdvPL ASP, por hora estamos entrando neste assunto para fins informativos e didáticos.

Página AdvPL ASP da AGENDA

Sem mais delongas, vamos ver como está a primeira versão — ainda não funcional, mas apresentável — da interface AdvPL ASP da Agenda — futuro arquivo “agenda.aph”

<!DOCTYPE html>
<html>
<head>
<meta charset="ANSI">
<title>Agenda em AdvPL ASP</title>
<style>
.agbutton {
display: inline-block;
text-decoration : none;
width: 120px;
height: 18px;
background: rgb(240, 240, 240);
text-align: center;
color: black;
padding-top: 4px;
}
.agget { 
display: block;
width: 110px;
height: 22px; 
color: black; 
padding-top: 6px; 
text-align: right;
}
.aginput { 
width: 320px;
height: 22px; 
color: black; 
padding-top: 0px; 
padding-right: 10px; 
text-align: left;
}
</style>
</head>

function doLogoff() { 
var f = document.getElementById("F_AGENDA"); 
f.action="/logoff.apw"
f.submit();
};

<body style="font-family:Courier New;font-size: 12px;background-color:rgb(128,128,128);">

<table align="left">
<tr>

<!-- Primeira Tabela - Opções e Imagem 3x4 -->
<td align="left" valign="top">
<table>
<tr><td><a class="agbutton" href="?Op=1">Incluir</a></td></tr>
<tr><td><a class="agbutton" href="?Op=2">Alterar</a></td></tr>
<tr><td><a class="agbutton" href="?Op=3">Excluir</a></td></tr>
<tr><td style="height: 22px;">&nbsp;</td></tr>
<tr><td><a class="agbutton" href="javascript:void(0)" onclick="doLogoff()">Sair</a></td></tr>
<tr><td style="height: 22px;">&nbsp;</td></tr>
<tr><td><img style="width: 120px;height: 160px;" src="./images/Agenda_3x4.png"></td></tr>
</table>
</td>

<!-- Segunda Tabela - Mais Opções -->
<td align="left" valign="top">
<table>
<tr><td><a class="agbutton" href="?Op=4">Primeiro</a></td></tr>
<tr><td><a class="agbutton" href="?Op=5">Anterior</a></td></tr>
<tr><td><a class="agbutton" href="?Op=6">Próximo</a></td></tr>
<tr><td><a class="agbutton" href="?Op=7">Último</a></td></tr>
<tr><td><a class="agbutton" href="?Op=8">Pesquisa</a></td></tr>
<tr><td><a class="agbutton" href="?Op=9">Ordem</a></td></tr>
<tr><td><a class="agbutton" href="?Op=10">Mapa</a></td></tr>
<tr><td><a class="agbutton" href="?Op=11">G-Mail</a></td></tr>
<tr><td><a class="agbutton" href="?Op=12">Foto 3x4</a></td></tr>
</table>
</td>

<!-- Terceira Tabela - Dados do Contato -->
<td align="left" valign="top"> 
<form id="F_AGENDA" action="#" method="post">
<table style="border-spacing: 1px; background: rgb(192,192,192);">
<tr><td colspan="2" style="width: 500px; height: 22px; color: white; padding-top: 4px; background: rgb(0,0,128);">&nbsp;Ordem ...</td></tr>
<tr><td class="agget">ID</td> <td class="aginput"><input id="I_D" type="text" name="ID" disabled size="6" ></td></tr>
<tr><td class="agget">Nome</td> <td class="aginput"><input id="I_NOME" type="text" name="NOME" disabled size="50" ></td></tr>
<tr><td class="agget">Endereço</td> <td class="aginput"><input id="I_ENDER" type="text" name="ENDER" disabled size="50" ></td></tr>
<tr><td class="agget">Complemento</td><td class="aginput"><input id="I_COMPL" type="text" name="COMPL" disabled size="20" ></td></tr>
<tr><td class="agget">Bairro</td> <td class="aginput"><input id="I_BAIRRO" type="text" name="BAIRRO" disabled size="30" ></td></tr>
<tr><td class="agget">Cidade</td> <td class="aginput"><input id="I_CIDADE" type="text" name="CIDADE" disabled size="40" ></td></tr>
<tr><td class="agget">UF</td> <td class="aginput"><input id="I_UF" type="text" name="UF" disabled size="2" ></td></tr>
<tr><td class="agget">CEP</td> <td class="aginput"><input id="I_CEP" type="text" name="CEP" disabled size="9" ></td></tr>
<tr><td class="agget">Fone 1</td> <td class="aginput"><input id="I_FONE1" type="text" name="FONE1" disabled size="20" ></td></tr>
<tr><td class="agget">Fone 2</td> <td class="aginput"><input id="I_FONE2" type="text" name="FONE2" disabled size="20" ></td></tr>
<tr><td class="agget">e-Mail</td> <td class="aginput"><input id="I_EMAIL" type="text" name="EMAIL" disabled size="40" ></td></tr>
<tr>
<td class="agget">&nbsp;</td>
<td>
<a class="agbutton" id="btnConfirm" href="?Op=13">Confirmar</a>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
<a class="agbutton" id="btnVoltar" href="?Op=14">Voltar</a>
</td>
</tr>
</table>
</form>

</td>
</tr>
</table>

</body>
</html>

Por hora, o resultado da apresentação desta tela no Web Browse pode ser visto abaixo:

Web Agenda 2

Ainda não há conteúdo dinâmico, as ações dos botões ainda estão sendo escritas, e a foto mostrada na tela é o arquivo “Agenda_3x4.png”, que por hora precisa estar dentro de uma pasta chamada “images” a partir da pasta raiz de publicação WEB (No caso do meu ambiente, a pasta images deve estar dentro do Path especificado na seção HTTP).

Pontos de Atenção

  • Por se tratar de uma rotina de consulta dinâmica, não há interesse em manter ou permitir que sejam feitos “BookMarks” das operações da Agenda, mesmo a de consulta. Por isso, boa parte das operações desta rotina vão requisitar o próprio link da agenda.apw passando parâmetros usando POST, e para isso os campos INPUT do formulário. E, inclusive, existe um tipo de campo INPUT que pode ser usado dentro do código HTML, mas que não é mostrado para o usuário, permitindo submeter valores adicionais dentro da requisição POST feita pelo Web Browse, onde podemos por exemplo usar JavaScript para colocar valor nestes campos. Veremos isso em detalhes no decorrer desta sequência de posts.
  • Mesmo que o JavaScript possa nos permitir colocar algumas validações no lado do Web Browse, como não submeter uma página com campos obrigatórios faltando, ou até mesmo implementar algumas verificações de consistência, não devemos confiar cegamente que isto será respeitado. Isto é, como qualquer script retornado ao Web Browse pode e vai ser interpretado pelo mesmo, qualquer um com um pouco de maldade no coração pode abrir uma interface de desenvolvedor no Web Browse, alterar ou desligar dinamicamente uma validação da página em questão, e burlar a validação ou consistência do lado do Client. Logo, sempre critique os dados vindos do Browse, pois eles não necessariamente podem vir da forma que você está esperando. Quer um exemplo simples? Você cria uma página de busca de itens em uma loja virtual, e permite no Browse que o cliente escolha receber os dados em páginas de 10, 20 ou 50 registros, e exige que o usuário informe um termo de busca com pelo menos três letras. Estes parâmetros são enviados junto da requisição de busca, que no servidor monta uma Query no banco, e limita os resultados baseado no número de linhas por página informado. Se esta validação estiver apenas no lado do cliente, um cidadão com más intenções pode burlar esta validação, e submeter uma busca pela letra “A”, estipulando um tamanho de página de  30 mil registros. Pronto, o Web Server vai disparar uma Query contra o banco de dados que vai retornar um volume medonho de registros, e o Web Server vai sofrer pra montar uma página de retorno que pode passar de 1 MB para retornar isso ao Browse.
  • Para evitar dores de cabeça desnecessárias, independente da metodologia ou ferramental aplicado, procure conhecer e seguir o principio “Keep It Simple” — MANTENHA SIMPLES — valorização da simplicidade e descarte de toda a complexidade desnecessária.

Conclusão

Devagar a gente chega lá. Fazer uma tela de login com dois campos é moleza… agora fazer uma tela com atualização dinâmica de um cadastro de 11 campos, 14 opções e uma foto 3×4, vamos por partes.

Referências

 

Protheus e AdvPL ASP – Parte 05

Introdução

No post anterior (Protheus e AdvPL ASP – Parte 04), vimos mais dois alias virtuais — HTTPHEADIN e HTTPHEADOUT. Agora, vamos ver o alias virtual HTTPCOOKIES, que permite lidar diretamente com Cookies de seção, e depois algumas propriedades estendidas ou campos reservados de cada um dos alias virtuais do AdvPL ASP.

Quer ler sobre esse assunto desde o início?
Veja os links de referência no final do post.

O que é um Cookie ?

Bem superficialmente, um Cookie é um fragmento de dados enviado por um Web Server ao Web Browser (ou Client), que é transmitido de volta pelo Client ao Web Server em próximas requisições. Recomendo a leitura do documento “Cookies HTTP“, ele explica bem os tipos de Cookies, escopo, persistência, utilização, e inclusive como evitar vulnerabilidades.

Um Cookie também é uma tupla chave/valor, e podemos transferir um ou mais Cookies do Servidor HTTP ao Web Browser, e receber seus valores de volta. Estas informações são trafegadas em chaves específicas dentro do Header da requisição HTTP. Para tornar mais fácil a manutenção destas informações, e você não precisar buscar as informações de Cookies e fazer a interpretação delas dentro da sua aplicação, o Protheus como Web Server faz este trabalho, e permite ler as informações de Cookies recebidos, e gravar novos cookies ou redefinir os valores dos Cookies a serem enviados como retorno ao Web Browse.

Alias Virtual HTTPCOOKIES

De forma similar aos demais alias virtuais, podemos recuperar valores de Cookies previamente retornados ao Web Browse, e e criar novas chaves com novos valores a serem armazenados no Web Browser, bem como trocar valores de cookies já existentes — desde que eles tenha sido criados pela nossa aplicação, e pertençam pelo menos ao mesmo host ou domínio — veja sobre escopo de Cookies no link “Cookies HTTP“.

No exemplo de requisição HTTP mostrado no post anterior (Protheus e AdvPL ASP – Parte 04), um dos campos do Header HTTP continha a seguinte informação:

Cookie: SESSIONID=41a79408567715479073622c71988001

Este Cookie foi criado internamente pelo Web Server do Protheus, para identificar a seção atual do usuário. Por isso ele foi recebido. Na primeira requisição que este usuário fez para o Web Server, após iniciar o Web Browser, este Cookie não existia, então o Web Server Protheus sabe que trata-se de uma nova seção, e cria um identificador para aquela seção, e retorna ele no header de retorno (HTTPHEADOUT) do HTTP.

Set-cookie: SESSIONID=41a79408567715479073622c71988001

Utilizando o alias virtual HTTPCOOKIES, eu poderia ler diretamente este cookie sem precisar fazer o tratamento do valor de HTTPHEADIN->COOKIE, que poderia inclusive conter mais de um Cookie. Por exemplo, para ler o cookie SESSIONID, eu utilizaria HTTPCOOKIES->SESSIONID. O Cookie SESSIONID é de uso interno do mecanismo de controle de sessões de usuário — inclusive das variáveis de seção ( HTTPSESSION ). Logo, não devemos mexer neste valor.

Vale lembrar que a soma de chaves e valores de Cookies têm capacidade limitada de armazenamento — eu pessoalmente não usaria nada maior que 256 bytes — lembrando que a transmissão destes valores em cada requisição podem acabar interferindo no peso e desempenho das requisições.

Propriedades estendidas dos Alias Virtuais

Agora que nós já vimos todos os alias virtuais disponíveis para o AdvPL ASP, vamos ver algumas propriedades estendidas de cada um deles, que podem ser muito úteis em várias situações.

HTTPGET->AGETS

O alias virtual HTTPGET possui um campo reservado, chamado “AGETS”. Trata-se de um array de strings, contendo em cada elemento o nome de um identificador de parâmetro informado na URL da requisição. Este campo montado dinamicamente pelo servidor Protheus antes de alocar um processo (Working Thread) para processar uma requisição de link .apw

Por exemplo, vejamos a URL abaixo:

http://localhost/info.apw?a=1&b=2&c

Ao processar esta requisição em AdvPL ASP, podemos recuperar o valor de cada um dos parâmetros informados no AdvPL, usando o alias virtual HTTPGET, desde que saibamos os nomes dos parâmetros, usando por exemplo:

cValorA := HTTPGET->A
cValorB := HTTPGET->B

Caso eu queira saber quais foram os identificadores usados na URL, eu posso consultar o campo HTTPGET->AGETS, por exemplo:

For nI := 1 to len(HTTPGET->AGETS)
  conout("GET "+cValToChar(nI)+" Identificador "+HTTPGET->AGETS[nI]
Next
// o resultado esperado no log de console do 
// servidor de aplicação é:
// GET 1 Identificador a
// GET 2 Identificador b
// GET 3 Identificador c

Reparem que “c” foi um identificador passado na URL, porém ele não teve valor atribuído. Agora, existe uma forma de recuperarmos os conteúdos destes identificadores, a partir dos nomes, usando macro-execução:

For nI := 1 to len(HTTPGET->AGETS)
  cIdent := HTTPGET->AGETS[nI]
  conout("GET "+cValToChar(nI)+" Id "+ cIdent + ;
  " Valor "+cValToChar( &("HTTPGET->"+cIdent) ) )
Next
// o resultado esperado no log de console do 
// servidor de aplicação é:
// GET 1 Id a Valor 1
// GET 2 Id b Valor 2
// GET 3 Id c Valor

HTTPPOST->APOST

Para o alias virtual HTTPPOST, existe o campo reservado “APOST“, com o mesmo propósito e funcionamento do AGETS. A diferença é que somente são informados valores nestes campos caso a requisição HTTP seja enviado o comando POST, o tipo de conteúdo da requisição seja “application/x-www-form-urlencoded“, e o corpo da requisição contenha uma ou mais tuplas chave/valor codificadas.

A mesma técnica utilizada para recuperar os valores associados aos identificadores recebidos em AGETS pelo alias virtual HTTPGET pode ser utilizada para recuperar os valores recebidos em APOST pelo alias virtual HTTPPOST.

HTTPHEADIN->AHEADERS

Para o alias virtual HTTPHEADIN, existe o campo reservado AHEADERS, criado com o propósito de tornar possível receber todas as linhas do cabeçalho da requisição HTTP vindas do Web Browse ou do Web Client que fez a solicitação. No caso, cada linha do cabeçalho HTTP, formado por uma tupla chave/valor, é retornada como um elemento deste array — e não apenas o nome do identificador, como é retornado por AGETS (do Alias HTTPGET) ou APOST (do alias HTTPPOST). Por exemplo:

<pre>
<% For nI := 1 to len(HTTPHEADIN->AHEADERS) %>
<%= HTTPHEADIN->AHEADERS[nI] %>
<% Next %>
</pre>

O trecho acima, dentro de um fonte APH, retornaria ao Browse em texto pré-formatado (ou fonte de tamanho fixa) todas as linhas do Header HTTP recebido pelo Web Server do Protheus para a requisição atual.

HTTPHEADIN->COMMAND

A primeira linha do HTTP Header enviada por um Web Browser ou Web Client do protocolo HTTP contém um comando — normalmente GET ou POST.  Para saber qual foi o comando recebido, podemos usar o campo reservado “COMMAND”.

HTTPHEADIN->MAIN

Ao receber uma requisição de link .apw, para processamento de AdvPL ASP, usamos o campo reservado “main”, do alias virtual HTTPHEADIN, alimentado apenas com o nome da página que foi requisitada, desconsiderando URL, domínio, path e parâmetros. Por exemplo, ao solicitar http://seuhostouip/suapasta/meulink.apw?a=1&b=2, o resultado de HTTPHEADIN->MAIN será apenas “meulink

HTTPHEADIN->CMDPARMS

De forma similar ao campo reservado MAIN, o campo CMDPARMS recebe o nome do link .apw chamado, e os eventuais parâmetros recebidos na URL, no formato de uma única string, onde apenas é removida a extensão “.apw” da requisição. Por exemplo, ao solicitar http://seuhostouip/suapasta/meulink.apw?a=1&b=2, o resultado de HTTPHEADIN->CMDPARMS será apenas “meulink?a=1&b=2

HTTPCOOKIES->ACOOKIES

Este campo reservado do alias virtual HTTPCOOKIES funciona exatamente igual ao AGETS e APOST. Ele traz apenas os nomes dos Cookies enviado pelo Cliente Web ao Web Server Protheus em uma requisição de AdvPL ASP.

<pre>
<% For nI := 1 to len(HTTPCOOKIES->ACOOKIES) %>
<%= HTTPCOOKIES->ACOOKIES[nI] %>
<% Next %>
</pre>

O trecho acima, dentro de um fonte APH, mostrará todos os nomes dos identificadores de Cookies recebidos pela requisição atual.

HTTPSESSION->SESSIONID

Funciona de forma similar ao HTTPCOOKIES->SESSIONID, porém com uma diferença: Na primeira requisição recebida pelo AdvPL ASP de uma nova seção do Web Browser, o identificador do SESSIONID vêm em branco, pois o Cookie identificador de seção ainda não foi retornado ao Browse. Já o HTTPSESSION->SESSIONID já vem preenchido com o identificador criado para esta seção antes mesmo dele ser retornado ao browse.

Exemplo COMPLETO

Vamos ver o que a podemos recuperar de uma requisição HTTP vinda de um Web Browse em AdvPL ASP, agora em um único exemplo? Primeiro, criamos um desvio na função U_ASPConn(), para executar a função H_ASPINFO() caso seja informado no browse a chamada para o link “aspinfo.apw“.

case cAspPage == 'aspinfo'
  cReturn := H_ASPINFO()

Agora, criamos o arquivo “aspinfo.aph“, com o seguinte conteúdo:

<html><body>
<pre>
HTTP HEADER INFO
<hr>
<% For nI := 1 to len(HTTPHEADIN->AHEADERS) %>
<%= HTTPHEADIN->AHEADERS[nI] %>
<% Next %>

Command .....: <%=HTTPHEADIN->COMMAND%>
Main ........: <%=HTTPHEADIN->MAIN%>
CmdParms ....: <%=HTTPHEADIN->CMDPARMS%>
Remote Addr .: <%=HTTPHEADIN->REMOTE_ADDR%>
Remote Port .: <%=HTTPHEADIN->REMOTE_PORT%>

<% If len(HTTPCOOKIES->ACOOKIES) > 0 %>
COOKIES
<hr>
<% For nI := 1 to len(HTTPCOOKIES->ACOOKIES) %>
<% cVar := HTTPCOOKIES->ACOOKIES[nI] %>
<%= cVar + " = [" + &("HTTPCOOKIES->"+cVar) + "]" %>
<% Next %>
<% Endif %>

<% If len(HTTPGET->AGETS) > 0 %>
GET PARAMS
<hr>
<% For nI := 1 to len(HTTPGET->AGETS) %>
<% cVar := HTTPGET->AGETS[nI] %>
<%= cVar + " = [" + &("HTTPGET->"+cVar) + "]" %>
<% Next %>
<% EndIf %>

<% If len(HTTPPOST->APOST) > 0 %> 
POST PARAMS
<hr>
<% For nI := 1 to len(HTTPPOST->APOST) %>
<% cVar := HTTPPOST->APOST[nI] %>
<%= cVar + " = [" + &("HTTPPOST->"+cVar) + "]" %>
<% Next %>
<% EndIf %>

REQUEST DETAILS
<hr>
HttpOtherContent() ......: <%=HttpOtherContent()%>
HttpRCTType() ...........: <%=HttpRCTType()%>
HttpRCTDisp() ...........: <%=HttpRCTDisp()%>
HttpRCTLen() ............: <%=HttpRCTLen()%>
</pre>
</body></html>

Agora vamos ver a mágica funcionando. Com tudo compilado e o Protheus no ar, entramos com a seguinte URL: http://localhost/aspinfo.apw?a=1Veja abaixo o resultado esperado no Web Browse:

HTTP HEADER INFO

GET /aspinfo.apw?a=1 HTTP/1.1
Host: localhost
Connection: keep-alive
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
Accept-Encoding: gzip, deflate, br
Accept-Language: pt-BR,pt;q=0.9,en-US;q=0.8,en;q=0.7,es;q=0.6
Cookie: SESSIONID=72b298eea3eed9544ad95459e833e7c2

Command .....: GET
Main ........: aspinfo
CmdParms ....: aspinfo?a=1
Remote Addr .: 127.0.0.1
Remote Port .: 52764

COOKIES

SESSIONID = [f62f4ac8afc9f1b3456faafc93551bc5]

GET PARAMS

A = [1]

REQUEST DETAILS

HttpOtherContent() ......: 
HttpRCTType() ...........: 
HttpRCTDisp() ...........: 
HttpRCTLen() ............: -1

E se fosse um POST ?

Claro, vamos ver o que eu receberia em um post. Aproveitando o exemplo de um post anterior, vamos editar o arquivo formpost.aph, e trocar o ACTION do formulário de “/postinfo.apw” para “/aspinfo,apw”, e ver  o que acontece.

<html><body>
<p>Formulário de POST</p>
<form action="/aspinfo.apw" method="post">
First name:<br>
<input type="text" name="firstname"><br>
Last name:<br>
<input type="text" name="lastname">
<hr>
<input type="submit" value="Enviar">
</form>
</body></html>

Agora, vamos abrir a URL http://localhost/formpost.apw, e no caso, eu vou digitar meu nome e sobrenome nos campos:

Web formpost

Após clicar no botão ENVIAR, o retorno esperado no Browse deve ser algo assim:

HTTP HEADER INFO

POST /aspinfo.apw HTTP/1.1
Host: localhost
Connection: keep-alive
Content-Length: 34
Cache-Control: max-age=0
Origin: http://localhost
Upgrade-Insecure-Requests: 1
Content-Type: application/x-www-form-urlencoded
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
Referer: http://localhost/formpost.apw
Accept-Encoding: gzip, deflate, br
Accept-Language: pt-BR,pt;q=0.9,en-US;q=0.8,en;q=0.7,es;q=0.6
Cookie: SESSIONID=fe5ab7f15a1b9788fd945bf14581af82

Command .....: POST
Main ........: ASPINFO
CmdParms ....: ASPINFO
Remote Addr .: 127.0.0.1
Remote Port .: 53500

COOKIES

SESSIONID = [f9d8c4a25dc307251bf05300f59bf3e4]

POST PARAMS

FIRSTNAME = [Júlio]
LASTNAME = [Wittwer]

REQUEST DETAILS

HttpOtherContent() ......: 
HttpRCTType() ...........: application/x-www-form-urlencoded
HttpRCTDisp() ...........: 
HttpRCTLen() ............: 34

Conclusão

Com este post, cobrimos o básico do AdvPL ASP. Nos próximos posts deste assunto, vamos abordar algumas capacidades de retorno diferenciado da interface HTTP, e como utilizá-las quando necessário. Afinal, podemos retornar ao Web Browser ou ao Client HTTP utilizado muito mais do que apenas HTML 😀

Agradeço novamente a audiência, e desejo a todos TERABYTES DE SUCESSO !!!! 

Referências