DESENVOLVIMENTO WEB COM BROOK FRAMEWORK 3.0 FUNCIONAMENTO DE UMA APLICAÇÃO BROOK; HELLO WORLD; MÉTODO WRITE; RENDERIZAÇÃO DE ARQUIVOS Orientador: Silvio Clécio RESUMO Neste artigo, entenderemos como funciona uma aplicação Brook, logo em seguida, criaremos o nosso primeiro Hello world. Veremos também as diversas maneiras de escrever texto na tela usando o método Write. Por fim, entenderemos como renderizar arquivos na tela usando o método Render, e conheceremos algumas técnicas para disponibilizar um arquivo online. PALAVRAS CHAVE: Brook 3.0. Aplicação. Hello world. Write. Render. Petrolina PE 2014
2 SUMÁRIO 1 FUNCIONAMENTO DE UMA APLICAÇÃO BROOK... 3 2 PRIMEIRO HELLO WORLD WEB... 3 2.1 ESCOLHENDO A APLICAÇÃO DO TIPO CGI... 3 2.2 IMPLEMENTANDO NOSSA ACTION... 5 2.3 CONFIGURANDO O PROJETO... 5 2.4 COMPILANDO E TESTANDO O PROJETO... 6 3 DIVERSAS MANEIRAS DE USAR O MÉTODO WRITE... 7 4 RENDERIZANDO ARQUIVOS NA TELA... 11 5 PRÓXIMOS PASSSOS... 13 6 EXERCÍCIOS... 14 6.1 EXERCÍCIO 1... 14 6.2 EXERCÍCIO 2... 14
3 1 FUNCIONAMENTO DE UMA APLICAÇÃO BROOK Aplicações Brook assemelham-se a aplicações console, ou seja, rodam em modo texto e não possuem interface gráfica. Não é possível e nem necessário, por exemplo, adicionar um TForm neste tipo de aplicação, pois a LCL não é compatível com aplicações console, justamente por ela implementar interfaces de widgets que uma aplicação console não suporta. Entretanto, para aqueles mais familiarizados em usar um TForm como repositório para componentes não visuais, é possível substituí-lo por um TDataModule, que é visível apenas em modo de design e completamente compatível com aplicações console. No entanto, uma aplicação Brook fornece recursos para não ser necessário um programador precisar usar nem mesmo um TDataModule. Nos próximos artigos deste curso, estudaremos os tipos de aplicações Brook, qual aplicação escolher na hora de criar um projeto, e como fazer as devidas configurações para garantir o funcionamento da aplicação com uma boa performance. Agora que já temos uma noção do que é uma aplicação Brook, iniciaremos nossos primeiros testes, começando pelo lendário Hello world. 2 PRIMEIRO HELLO WORLD WEB O primeiro hello world a gente nunca esquece!. Nesta sessão, criaremos o nossa primeira aplicação web que imprime a palavra hello world no browser. Mãos à obra! 2.1 ESCOLHENDO A APLICAÇÃO DO TIPO CGI Primeiramente, no menu File New... do Lazarus, escolha a opção Brook framework > Simple CGI aplication e clique em OK. A Figura 1 ilustra este passo: Figura 1 Criando uma aplicação CGI
4 Ao clicar em OK, o expert da Brook gera três arquivos, são eles: 1. brokers.pas; 2. cgi1.lpr; 3. unit1.pas. A Figura 2 mostra todos eles logo após serem gerados: Figura 2 - Arquivos gerados pelo expert Brook O primeiro arquivo, brokers.pas, contém a declaração do broker para CGI, ou seja, ele aproveita todo o código FCLWeb que implementa o protocolo CGI. Nos próximos artigos entenderemos mais sobre brokers e a utilidade desta unit. O segundo arquivo, cgi1.lpr, é o nosso projeto Brook. É a partir deste arquivo que o Free Pascal irá gerar nossa aplicação console. Por fim, o terceiro arquivo, unit1.pas, contém a action Brook e nela implementaremos nosso hello world. Porém, antes de continuar os próximos passos, salve todos os arquivos gerados em alguma pasta 1, usando o botão Save all na toolbar do Lazarus, conforme Figura 3: Figura 3 - Botão Save all 1 Neste artigo escolhemos a pasta: C:\hello_world_brook.
5 2.2 IMPLEMENTANDO NOSSA ACTION Agora, com o arquivo unit1.pas gerado, temos nossa action, a TMyAction. É nela que implementaremos a mensagem hello world. Note que ela vem com a mensagem Your content here..., substitua por Hello world, conforme mostra o Código 1: unit Unit1; {$mode objfpc}{$h+} interface uses BrookAction; type TMyAction = class(tbrookaction) public procedure Get; override; implementation Write('Hello world'); initialization TMyAction.Register('*'); end. Código 1 - Implementando TMyAction O método Get será disparado quando acessarmos a URL da nossa action. Já o método Write, oriundo da classe TBrookAction, escreverá nossa mensagem na tela. Mais adiante veremos mais detalhes sobre este método. 2.3 CONFIGURANDO O PROJETO Agora faremos uma pequena configuração em nosso projeto, para ele gerar o executável CGI diretamente na pasta cgi-bin do Apache 2. Com o projeto aberto no Lazarus, pressione Shift+Ctrl+F11 para acessar suas propriedades. Logo em seguida, vá até a opção Compiler Options > Paths e, no campo Target file name (-o), defina: C:\websrv\cgi-bin\cgi1.bf. A Figura 4 mostra como ficou nossa configuração: 2 Artigo sobre instalação do Apache no Windows: https://www.dropbox.com/s/7pchulhx3tikqlo/instalando%20o%20apache%20no%20windows.pdf.
6 Figura 4 - Opções do projeto Após concluir esta configuração, vamos partir para a compilação. 2.4 COMPILANDO E TESTANDO O PROJETO Esta é a parte final do nosso teste. Para compilar o projeto, use Ctrl+9, que irá gerar o executável cgi1.bf na pasta C:\websrv\cgi-bin, conforme Figura 5: Figura 5 - Executável CGI Agora, com o executável gerado, basta acessar a seguinte URL no browser: http://localhost/cgi-bin/cgi1.bf.
7 Voilà, veja o resultado na Figura 6: Figura 6 - Hello world Chegando até este ponto sem nenhum erro, já demos o nosso primeiro passo no desenvolvimento web! 3 DIVERSAS MANEIRAS DE USAR O MÉTODO WRITE O método Write escreve na tela e permite ser usado de várias maneiras. Na Brook 3.0, ele suporta os seguintes tipos: String; Boolean; Integer; Double; TObject; TStream. Para os tipos String e TStream, a escrita é direta, ou seja, o conteúdo sai na tela exatamente da forma como foi escrito no código. Já para os tipo Boolean, Integer, Double e TObject, a saída é formatada, usando as configurações padrões de formatação da unit SysUtils. Portanto, se o programador desejar personalizar a saída para esses últimos tipos, ele terá que configurar a formatação padrão da SysUtils, como veremos mais adiante. É possível também escrever texto formatado, ou seja, formatando uma String através de parâmetros. No Código 2, temos alguns exemplos de uso do Write para alguns tipos e também com formatação: unit Unit1; {$mode objfpc}{$h+} interface uses
8 type BrookAction, SysUtils; TMyAction = class(tbrookaction) public procedure Get; override; implementation // Escrevendo um texto simples. O "<br />" representa quebra de linha no HTML. Write('Este é um texto simples.<br />'); // Escrevendo com alguns tipos. Write('Tipo Boolean sem formatação: '); Write(True); Write('<br />'); Write('Tipo Integer: '); Write(123); Write('<br />'); Write('Tipo Double sem formatação: '); Write(123.456); Write('<br />'); // Escrevendo uma string formatada por parâmetros. Write( 'Boolean formatado: %s<br />' + 'Double formatado: %f<br />' + 'Monetário formatado: %m<br />' + 'Científico formatado: %e<br />', [ BoolToStr(True, True), 123.450, 67.8, System.Pi ]); initialization TMyAction.Register('*'); end. Código 2 - Usando o Write com tipos primitivos Para o código acima, a saída na tela é: Este é um texto simples. Tipo Boolean sem formatação: -1 Tipo Integer: 123 Tipo Double sem formatação: 123,456 Boolean formatado: True Double formatado: 123,45 Monetário formatado: R$ 67,80 Científico formatado: 3,1415926535897932E+000 Saída gerada pelo Código 2 O uso do Write com Stream é ideal quanto precisamos escrever um conteúdo extenso. Seu uso é mais raro no dia a dia, no entanto, poderia ser usado facilmente numa situação como esta no Código 3:
9 var res: TStream; http: TBrookHttpClient; res := TMemoryStream.Create; http := TBrookHttpClient.Create('fclweb'); try http.get('http://silvioprog.github.io/brookframework/', res); res.seek(0, 0); Write(res) finally res.free; http.free; Código 3 - Exemplo de uso do Write com Stream No código acima, baixamos o conteúdo da home page da Brook http://silvioprog.github.io/brookframework/ e o renderizamos na tela. Podemos notar que o exemplo fez uso de TBrookHttpClient, detalhes sobre esta classe serão vistos em artigos futuros. A escrita de objetos é mais comum de se ver, ou seja, usar o Write do tipo TObject para escrever objetos na tela. Um exemplo disso pode ser visto no Código 4: unit Unit1; {$mode objfpc}{$h+} interface uses BrookAction; type { TPerson } TPerson = class(tobject) private FId: Int64; FName: string; published property Id: Int64 read FId write FId; property Name: string read FName write FName; TMyAction = class(specialize TBrookGAction<TPerson>) public procedure Get; override; implementation TheResponse.ContentType := 'text/plain'; Entity.Id := 10; Entity.Name := 'Albert Einstein';
10 Write(Entity); initialization TMyAction.Register('*'); end. E sua respectiva saída: Código 4 - Uso do Write com objeto Id=10 Name=Albert Einstein Saída gerada pelo Código 4 Note que o exemplo fez uso de generics, que serão abordados no decorrer do curso. Já o código TheResponse.ContentType := 'text/plain' é simplesmente para o browser renderizar o conteúdo em formato de texto puro ao invés de HTML. É possível também ignorar algumas propriedades do objeto, de modo que elas não apareçam na saída. Vamos adicionar a propriedade Age em nosso objeto, no entanto, ignoraremos a propriedade Id. Exemplo no Código 5: TPerson = class(tobject) private FAge: ShortInt; FId: Int64; FName: string; published property Id: Int64 read FId write FId; property Name: string read FName write FName; property Age: ShortInt read FAge write FAge; TheResponse.ContentType := 'text/plain'; Entity.Id := 10; Entity.Age := 76; Entity.Name := 'Albert Einstein'; Write(Entity, ['Id']); E a nova saída é: Código 5 - Ignorando propriedades no método Write Name=Albert Einstein Age=76 Saída gerada pelo Código 5 Como podemos ver, a propriedade Id foi ignorada e seu conteúdo não foi escrito na tela.
11 4 RENDERIZANDO ARQUIVOS NA TELA As actions Brook possuem o método Render que, tal como o Write, é bastante poderoso e permite ser usado de forma simples ou com parâmetros, no entanto, ao invés de escrever apenas texto, ele é usado para escrever arquivos inteiros na tela. Vejamos um exemplo básico no Código 6: unit Unit1; {$mode objfpc}{$h+} interface uses BrookAction; type TMyAction = class(tbrookaction) public procedure Get; override; implementation Render('C:\lazarus\COPYING.GPL.txt'); initialization TMyAction.Register('*'); end. Código 6 - Exemplo básico com o método Render Este código renderiza todo o conteúdo do arquivo COPYING.GPL.txt, que é a licença do Lazarus, e por ser apenas de um arquivo de texto, a renderização teve sucesso. No entanto, se fizéssemos: Render('C:\lazarus\lazarus.exe'); Ocorreria um problema na renderização, pois o arquivo trata-se do executável do Lazarus, que é um binário e tem um tamanho razoavelmente grande para ser processado na lista de strings interna do método Render. A solução para casos específicos assim, é ordenar o browser a disponibilizar o arquivo como download e lê-lo em modo binário: TheResponse.ContentStream := TFileStream.Create( 'C:\lazarus\lazarus.exe', fmopenread or fmsharedenywrite);
12 try TheResponse.ContentType := 'application/octet-stream'; TheResponse.SetCustomHeader('Content-Disposition', 'attachment; filename="lazarus.exe"'); TheResponse.SendContent; finally TheResponse.ContentStream.Free; TheResponse.ContentStream := nil; Código 7 - Disponibilizando um arquivo como download Ou seja, adicionando alguns headers no response, informamos que nosso arquivo trata-se de um binário ('application/octet-stream'), e deve ser disponibilizado como download (Content-Disposition: attachment), com o nome lazarus.exe (filename="lazarus.exe"). Já imagens, podemos disponibilizá-las como download ou renderizá-las. Para a primeira opção, o processo é o mesmo mostrado no Código 7, ou seja, usando Content-Disposition: attachment, para renderizá-la, usamos Content- Disposition: inline e o Content-Type image/png, conforme Código 8: TheResponse.ContentStream := TFileStream.Create( 'C:\lazarus\images\splash_logo.png', fmopenread or fmsharedenywrite); try TheResponse.ContentType := 'image/png'; TheResponse.SetCustomHeader('Content-Disposition', 'inline; filename="splash_logo.png"'); TheResponse.SendContent; finally TheResponse.ContentStream.Free; TheResponse.ContentStream := nil; Código 8 - Renderizando imagens na tela O resultado para o código acima é: Figura 7 - Mostrando a logo do Lazarus na tela
13 Podemos usar BrookHttpUtils para automatizar um pouco mais o código: uses BrookHttpUtils; const f = 'C:\lazarus\images\splash_logo.png'; TheResponse.ContentStream := TFileStream.Create( f, fmopenread or fmsharedenywrite); try TheResponse.ContentType := BrookMimeTypeFromFileName(f); TheResponse.SetCustomHeader('Content-Disposition', 'inline; filename="' + ExtractFileName(f) + '"'); TheResponse.SendContent; finally TheResponse.ContentStream.Free; TheResponse.ContentStream := nil; Código 8 melhorado A função BrookMimeTypeFromFileName obtém automaticamente o tipo MIME de um arquivo, ou seja, para nossa imagem splash_logo.png, temos image/png. Por fim, o método Render também aceita arquivos parametrizados. Supondo que tenhamos um arquivo test.txt com o seguinte conteúdo: Este é um %s de arquivo %s. Podemos formatá-lo assim: Render('C:\hello_world_brook\test.txt', ['arquivo', 'parametrizado']); O resultado é: Este é um arquivo de arquivo parametrizado. 5 PRÓXIMOS PASSSOS Neste artigo, entendemos o funcionamento de uma aplicação Brook, criamos o nosso primeiro Hello world, conhecemos as diversas maneiras de usar o método Write, entendemos sobre o método Render e as duas maneiras de disponibilizar arquivos online. No próximo artigo, aprenderemos como criar um HTTP próprio, com a possibilidade de rodá-lo também como serviço. Veremos como depurar uma aplicação Brook, dentre outros, como personalizar os erros 404 e 500.
14 6 EXERCÍCIOS 6.1 EXERCÍCIO 1 Mostrar uma exceção formatada na tela, usando recursos que a própria action Brook oferece. 6.2 EXERCÍCIO 2 Renderizar um arquivo na tela, porém, trocando algumas informações do conteúdo dele em tempo de execução.