Webapp2

“Linguagem não é simplesmente um dispositivo para relatar experiências, mas um framework que as define.” 1
- Benjamin Whorf


Introdução

A palavra framework significa um esquema, um conjunto de passos, que serve para resolver determinado problema. Apesar do termo ser geral, ele é muito utilizado em computação como sinônimo de biblioteca. Ou seja, um conjunto de códigos que se utilizados facilitam a construção de um sistema.

Por conta disso é importantíssimo saber quais são as questões que um framework busca resolver. Se alguém pedisse para uma pessoa se vestir a caráter, a pergunta óbvia seria: “Qual a ocasião?”. Sendo assim, seguir os passos de uma biblioteca sem saber seu objetivo é análogo a ir vestido de fraque em uma partida de futebol.

Nesse capítulo será explicado o funcionamento do framework Webapp2 e seu objetivo.

O que é Webapp2?

Webapp2 é uma biblioteca de código aberto utilizada na documentação oficial introdutória ao GAE. Ela implementa o padrão WSGI (Web Server Gateway Interface) e pode ser utilizada em outras plataformas que forneçam integração com esse padrão.

Não será ela a biblioteca base utilizada para construir a maior parte dos exemplos nesse livro. Contudo, seu entendimento é fundamental por duas razões:

  1. O framework Tekton irá utilizá-la como base;
  2. Algumas vezes é necessário fazer uso do Webapp2, utilizando objetos como ‘Request’, ‘Response’ e ‘Handler’.

A seguir constam seções explicando os diferentes componentes dessa ferramenta.

Arquivo app.yaml

No projeto criado no capítulo anterior existe um arquivo de configuração chamado app.yaml conforme listagem 2.01:

Listagem 2.01: Arquivo app.yaml


application: new-project-template
version: 1
runtime: python27
api_version: 1
threadsafe: yes

libraries:
- name: webapp2
  version: "2.5.2"

handlers:
- url: /favicon\.ico
  static_files: favicon.ico
  upload: favicon\.ico

- url: .*
  *script*: main.app

Esse arquivo contém as principais informações sobre o projeto. Sempre que se quiser entender sua estrutura geral, deve-se verificar o conteúdo desse arquivo, que será detalhado nas próximas seções.

Cabeçalho Inicial

No cabeçalho inicial do arquivo se encontram informações básicas sobre o projeto, conforme listagem 2.02:

Listagem 2.02: Cabeçalho


1 application: new-project-template
2 version: 1
3 runtime: python27
4 api_version: 1
5 threadsafe: yes

Na linha 1, application, consta o identificador da aplicação. Conforme foi visto na seção deploy do capítulo anterior, esse código deve ser o mesmo utilizado na criação da aplicação no console do GAE. Através dele o SDK identifica o projeto e consegue publicar o site corretamente na nuvem.

Já na linha 2 consta a versão da aplicação. É importante notar que o GAE permite a existência de várias versões simultâneas. A figura 2.01 mostra uma aplicação com múltiplas versões:

Figura 2.01: Múltiplas Versões

Figura 2.01: Múltiplas Versões

Nesse exemplo a versão 27 é padrão e, portanto, ela serve a aplicação quando acessada através do endereço http://picprolabs.appspot.com. Para acessar diferentes versões, como a 16 por exemplo, seu número deve ser concatenado ao início do domínio. Uma primeira forma de fazer isso é acessar o endereço http://16-dot-picprolabs.appspot.com/. Outro endereço válido é http://16.picprolabs.appspot.com/. Recomenda-se utilizar o primeiro, pois ele evita problemas no caso de acesso seguro via https.

Cabe ressaltar que essa funcionalidade é muito útil para se testar a aplicação antes de torná-la disponível aos clientes. Ou seja, publica-se o site em uma versão específica, alterando a versão no arquivo de configuração. Suas funcionalidades são conferidas e homologadas no respectivo endereço dedicado. Em caso de sucesso, a nova versão é definida como padrão, ficando disponível a todos usuários. Outra vantagem é poder retornar à versão anterior em caso de problemas. E tudo isso é feito com apenas um clique no painel de administração.

As demais linhas definem a versão da linguagem e api. Além disso, informam se uma instância da aplicação pode processar requisições em paralelo.

Bibliotecas Embutidas

O GAE fornece um conjunto de bibliotecas embutidas. Para instalação, deve ser editada a seção libraries do arquivo de configuração, conforme listagem 2.03.

Cada item define o nome da biblioteca a ser instalada e, opcionalmente, sua respectiva versão. No exemplo foi instalada a versão 2.5.2 da biblioteca Webapp2.

Listagem 2.03: Instalação do framework Webapp2


1 libraries:
2 - name: webapp2
3   version: "2.5.2"

Roteamento via Arquivo de Configuração

Roteamento é uma questão a ser resolvida por qualquer framework web. É ele quem define qual código será executado no servidor de acordo com o path acessado no navegador. O início do roteamento se dá na definição da seção handlers, no arquivo de configuração, conforme listagem 2.04:

Listagem 2.04: Roteamento


1 handlers:
2 - url: /favicon\.ico
3   static_files: favicon.ico
4   upload: favicon\.ico
5 
6 - url: .*
7   *script*: main.app

Nas linhas 2 a 4 é definido um handler para servir o arquivo estático favicon.ico no path /favicon.ico. Esse endereço padrão é utilizado pelo navegador para colocar uma imagem respectiva ao site na aba em que ele está aberto, conforme figura 2.02:

Figura 2.02: favicon.ico

Figura 2.02: favicon.ico

Mas o assunto de arquivos estáticos será melhor abordado no capítulo 4, Frontend. Nesse, o foco é a execução de código Python que ocorre quando um endereço é digitado no navegador.

Na linha 6, contendo a expressão url, estão sendo mapeados todos os paths através da expressão regular “.*”. Essa expressão se traduz como: “qualquer cadeia de caracteres”. Cabe ressaltar que os handlers são processados na ordem em que aparecem. Por essa razão o primeiro trata a chamada em /favicon, enquanto o segundo irá tratar todos os demais.

Uma vez mapeado o path, deve ser informado qual será o script que deverá processar a requisição. Isso é feito na linha 7, onde é configurado o arquivo ‘main.py’. Outros handlers poderiam ser adicionados ao arquivo para processar outros endereços.

Roteamento via Webapp2

Uma vez que o arquivo de configuração aponta para um script, é importante entender seu conteúdo, que se apresenta na listagem 2.05 a seguir:

Listagem 2.05: Script main.py


 1 # -*- coding: utf-8 -*-
 2 from __future__ unicode_literals
 3 import webapp2
 4 
 5 
 6 class HomeHandler(webapp2.RequestHandler):
 7     def get(self):
 8         self.response.write('Olá Mundo!')
 9 
10 
11 app = webapp2.WSGIApplication([('/', HomeHandler)],
12                               debug=True)

Na linha 3 é importado o módulo webapp2 pertencente ao framework de mesmo nome. Como se quer construir um handler para tratar requisições, é construída a classe HomeHandler herdando de RequestHandler na linha 6. Nessa classe foi sobrescrito o método referente ao respectivo verbo HTTP. No caso do código, sobrescreveu-se o método get na linha 7, referente ao verbo HTTP GET.

Por fim, é muito comum em Python termos várias classes declaradas em um módulo. Sendo assim, apenas acrescentar o script no arquivo de configuração não é suficiente para se saber qual handler deve ser executado. Por essa razão é necessário fazer também o roteamento dentro do código, como consta na linha 12. Nela é criado o parâmetro app construindo-se um objeto do tipo WSGIApplication.

O primeiro parâmetro da construção é uma lista de tuplas, onde o primeiro argumento é a expressão regular mapeando os paths. No exemplo, está sendo mapeada a raiz do projeto '/'. Já o segundo parâmetro indica a classe, HomeHandler, que irá ser utilizada para tratar a requisição.

Com esse código escrito e depois de executar o servidor, é possível obter a mensagem “Olá Mundo” no navegador, conforme a figura 1.03:

Figura 2.03: Mensagem "Olá Mundo!" no navegador

Figura 2.03: Mensagem “Olá Mundo!” no navegador

Seguindo a mesma filosofia, o script main.py pode ser editado para responder “Olá Wepapp2!” quando se acessa o path /outra. As mudanças se encontram no Código 2.01:

Código 2.01: Script main.py


 1 class HomeHandler(webapp2.RequestHandler):
 2     def get(self):
 3         self.response.write('Olá Mundo!')
 4 
 5 
 6 class OutroHandler(webapp2.RequestHandler):
 7     def get(self):
 8         self.response.write('Olá Wepapp2!')
 9 
10 
11 app = webapp2.WSGIApplication([('/', HomeHandler)], debug=True)
12 app = webapp2.WSGIApplication([('/', HomeHandler),('/outra', OutroHandler)],
13                               debug=True)

Na figura 2.04 é exibido o resultado do acesso ao path no navegador:

Figura 2.04: Mensagem "Olá Webpp2!" no navegador

Figura 2.04: Mensagem “Olá Webpp2!” no navegador

Dessa maneira, os passos para se fazer o roteamento são:

  1. Configurar o arquivo app.yaml toda vez que se deseja criar um script Python;
  2. Configurar cada Handler dentro de seu respectivo script com o Webapp2.

Request

Quando o usuário acessa um site no navegador, ele está enviando uma requisição HTTP. O Webapp2 processa essa requisição, construindo um objeto do tipo Request. É através dessa interface que o código do servidor obtêm acesso às informações e parâmetros enviados pelo usuário.

Parâmetros podem ser enviados através do que se chama query string, que é parte da url localizada após o sinal “?”. Sendo assim, quando se faz uma chamada HTTP do tipo GET, é possível editar os valores enviados modificando a url no navegador. Na figura 2.05 consta um exemplo onde são passados os parâmetros nome com valor Renzo e sobrenome com valor Nuccitelli. Cada parâmetro é divido utilizando-se o sinal “&”:

Figura 2.05: Url com query string" no navegador

Figura 2.05: Url com query string” no navegador

É importante notar que acessando a url, a mensagem apresentada não é mais estática. Ela é construída com base nos parâmetros enviados. Para isso se utiliza o método get do objeto Request, fornecendo o nome do parâmetro do qual se deseja extrair o valor. Os valores obtidos das requisições sempre são do tipo string. O código 2.02 apresenta o handler com os métodos get, nas linhas 3 e 4, em destaque:

Código 2.02: Handler com extração de parâmetros


1 class ParametrosHandler(webapp2.RequestHandler):
2     def get(self):
3         nome = self.request.get('nome')
4         sobrenome = self.request.get('sobrenome')
5         self.response.write('Olá %s %s!' % (nome, sobrenome))

Cabe ressaltar que se o parâmetro inspecionado não estiver presente na query string, o método get irá retornar None como valor.

Muitas outras informações podem ser extraídas do objeto, tais como cookies, cabeçalhos HTTP e domínio. Mas esses outros métodos serão vistos no decorrer do livro, sendo a obtenção de parâmetros o foco nesse momento.

Response

Após o recebimento de uma requisição, o servidor deve enviar uma resposta utilizando protocolo HTTP. Para cumprir esse objetivo, é utilizado o objeto do tipo Response. Ele provê métodos para auxiliar no envio de dados.

Nos exemplos anteriores seu método write foi utilizado para enviar uma string como resposta às requisições. O código 2.03 contém lógica que se utiliza desse método, na linha 5, em destaque:

Código 2.03: Método write para escrita de strings


1 class ParametrosHandler(webapp2.RequestHandler):
2     def get(self):
3         nome = self.request.get('nome')
4         sobrenome = self.request.get('sobrenome')
5         self.response.write('Olá %s %s!' % (nome, sobrenome))

De forma semelhante ao Request, o objeto Response possui métodos para se alterar cookies e cabeçalhos HTTP. Esses métodos também serão vistos nos próximos capítulos.

Redirect

Muitas vezes ao se acessar uma url o usuário é redirecionado para outra. Isso ocorre com frequência após a submissão de um formulário. Seu objetivo é evitar que a requisição para salvamento de dados seja enviada novamente, caso o navegador tenha seu botão de atualizar pressionado.

Para executar esse redirecionamento, o objeto RequestHandler fornece o método redirect. A ele deve ser fornecido como parâmetro a url completa, no caso de um servidor externo. No caso de um endereço interno da aplicação, apenas o path precisa ser utilizado. O código 2.04 contém código exemplificando os dois casos:

Código 2.04: Método redirect


 1 class RedirecionaParaOutroHandler(webapp2.RequestHandler):
 2     def get(self):
 3         self.redirect('/outra')
 4 
 5 
 6 class GoogleHandler(webapp2.RequestHandler):
 7     def get(self):
 8         self.redirect(str('http://www.google.com'))
 9 
10 
11 app = webapp2.WSGIApplication([('/', HomeHandler),
12                                ('/outra', OutroHandler),
13                                ('/redirecionar', RedirecionaParaOutroHandler),
14                                ('/google', GoogleHandler),
15                                ('/parametros', ParametrosHandler)],
16                               debug=True)

Dessa maneira, ao acessar http://localhost:8080/redirecionar, o usuário será redirecionado para http://localhost:8080/outra. Por outro lado, se acessar http://localhost:8080/google, será redirecionado para http://www.google.com.

É importante ressaltar que em um redirecionamento é enviada uma resposta HTTP de código 30x. Portanto, existe tráfego de dados durante essa operação. Devem ser evitados múltiplos redirecionamentos consecutivos, pois a maioria dos navegadores não permitem mais do que 5 redirecionamentos encadeados. O objetivo é evitar o redirecionamento infinito e consumo excessivo de recursos de rede.

Resumo

Nesse capítulo foi apresentado um resumo do framework de código aberto Webapp2. Através de seus 3 principais objetos, Request, Response e RequestHandler é possível obter dados dos usuários, enviar informações do servidor e redirecioná-los para outros endereços.

Mais do que simplesmente utilizar essa biblioteca, foi importante entender que ela serve para abstrair o protocolo HTTP. Sendo assim, precisamos apenas conhecer seus componentes para construir um web site. Apesar de simples, os poucos componentes vistos são suficiente para construirmos toda a navegação de uma aplicação.

O Webapp2 não será o framework base para a construção dos exemplos desse livro. Mas seu conhecimento é fundamental, pois é com base nele que irá funcionar o framework Tekton, que será o assunto do próximo capítulo.

Questões

  1. Qual o nome do arquivo de configuração do Google App Engine?
  2. Para que serve o item application do arquivo de configuração?
  3. Para que serve o item version do arquivo de configuração?
  4. Qual endereço deve ser utilizado para acessar uma aplicação com id foo e versão 35?
  5. Para que serve a seção libraries do arquivo de configuração?
  6. Para que serve a seção handlers do arquivo de configuração?
  7. Como são definidos os paths mapeados no arquivo de configuração?
  8. Por que é necessário mapear RequestHandlers nos scripts Python?
  9. Para que serve a classe RequestHandler?
  10. Como se relacionam os métodos da classe RequestHandler e os do protocolo HTTP?
  11. Para que serve o objeto Request?
  12. Como se obtém os valores de parâmetros enviados via query string em uma chamada HTTP do tipo GET?
  13. Para que serve o objeto Response?
  14. Qual o método do objeto Response serve para enviar strings?
  15. Como é possível enviar uma resposta para redirecionamento?

Respostas

  1. O nome do arquivo de configuração do Google App Engine é app.yaml.
  2. O item application serve para identificar a aplicação. Ele deve conter o mesmo id definido no momento da criação da aplicação no painel de controle disponível em http://appengine.google.com.
  3. O item version serve para identificar qual versão da aplicação será utilizada no momento da publicação do site.
  4. O endereço para acessar a aplicação foo em sua versão 35 deve ser http://35.foo.appspot.com.br ou http://35-dot-foo.appspot.com.br. É recomendado utilizar a segunda forma para evitar problemas quando o acesso for feito via https.
  5. A seção libraries serve para configurar as bibliotecas a serem utilizadas na aplicação. O GAE fornece um conjunto de bibliotecas que podem ser instaladas dessa maneira.
  6. A seção handlers serve para mapear os scripts Python que serão executados de acordo com o path das requisições HTTP.
  7. Para definição dos paths são utilizadas expressões regulares.
  8. É necessário mapear RequestHandlers nos scripts Python porque é comum a definição de múltiplas classes em um arquivo. Sendo assim somente o mapeamento via arquivo de configuração não é suficiente para definir qual classe será utilizada para processar uma requisição.
  9. A classe RequestHandler serve para definir o código que irá processar uma requisição HTTP.
  10. Os métodos da classe RequestHandler devem ser sobrescritos para processar as chamadas HTTP de tipo com mesmo nome. Por exemplo, uma chamada HTTP do tipo GET será processada no método sobrescrito get, Uma chamada do tipo POST em um método post e assim or diante.
  11. O objeto Request serve como interface para acesso às informações sobre uma requisição HTTP.
  12. Os valores de parâmetros enviados via query string em uma chamada HTTP do tipo GET são obtidos utilizando-se o método get do objeto Request. A ele deve ser fornecido o nome do parâmetro do qual se quer extrair o valor.
  13. O objeto Response server como interface para construção de uma resposta HTTP. Ela contém os dados a serem enviados como resposta a uma requisição.
  14. O método write da classe Response serve para enviar strings.
  15. Para enviar uma resposta de redirecionamento deve ser utilizado o método redirect do objeto RequestHandler, fornecendo como parâmetro o path desejado, no caso de redirecionamento para a mesmo aplicação, ou a url completa em caso contrário.
  1. “Language is not simply a reporting device for experience but a defining framework for it.”