Tutorial Google App Engine Python

Servindo conteúdo

O App Engine gerencia automaticamente as instâncias de sua aplicação. Uma instância é um servidor web que é inicializado com as configurações e com o código de sua aplicação. Ele permite que você produza conteúdo dinâmico, por meio de frameworks compatíveis com o padrão WSGI do Python.

Nem todo o conteúdo de sua aplicação muda frequentemente. Arquivos de imagem, como logomarcas ou botões, arquivos de script e folhas de estilo CSS, são bons exemplos de recursos que não são alterados com frequência.

Arquivos estáticos

A infra-estrutura do Google App Engine permite que você disponibilize arquivos estáticos utilizando servidores dedicados a esta tarefa. Esses servidores são projetados para otimizar a entrega do conteúdo, utilizando diferentes níveis de cache.

O App Engine trata todos os arquivos que estão dentro da pasta de seu projeto como arquivos disponíveis para sua aplicação, mas os arquivos não são servidos como estáticos.

Isso significa que uma imagem que estiver no caminho ola-mundo/imagens/logo.png, por exemplo, não estará visível se você tentar acessá-la pelo URL http://localhost/imagens/logo.png.

Para utilizar esta infra-estrutura de arquivos estáticos, nós configuramos um novo handler no arquivo app.yaml, como o arquivo abaixo:

handlers:
- url: /imagens
  static_dir: imagens

- url: /.*
  script: main.application

Na configuração acima, estamos definindo que que todos os arquivos dentro da pasta imagens, devem ficar disponíveis para qualquer URL que começar com /imagens.

Com a configuração acima, o App Engine vai utilizar os servidores de arquivos estáticos dedicados, sem que nossa aplicação seja cobrada pelo processamento para servir este conteúdo. Neste caso, apenas pagamos pelo volume trafegado. O tempo de vida do cache dos arquivos é definido por padrão para 10 minutos.

Conteúdo dinâmico

Como vimos no nosso olá mundo, podemos produzir conteúdo dinamicamente em resposta a uma requisição. Fazemos isso por meio de RequestHandlers, que configuramos em nosso aplicativo WSGI.

O acesso ao conteúdo dinâmico é realizado conforme as regras definidas em dois parâmetros. No arquivo app.yaml definimos qual o arquivo de script que vai processar nosso conteúdo. Em nosso "olá mundo", utilizamos o script main.application para responder por todas as requisições.

Dentro do script main.py, nós direcionamos quais os RequestHandlers são responsáveis por cada caminho. Em nosso olá mundo, nós utilizamos apenas um caminho, a raiz da aplicação ("/").

Nos dois locais, podemos utilizar expressões regulares para mapear scripts e RequestHandlers para diferentes caminhos. Expressões regulares são formas de descrever diferentes possibilidades de combinações de texto, utilizando alguns caracteres especiais.

Desenvolvendo um Website

Vamos utilizar os recursos de conteúdo estático e dinâmico do Google App Engine para desenvolver um pequeno website.

O website que iremos desenvolver terá como base um template, inicialmente estático, e um formulário de contato implementado utilizando o serviço de e-mails do Google App Engine.

Utilizando um template Boostrap

Nosso website será baseado em um template que podemos obter do site Start Bootstrap (http://s.ronoaldo.net/start-bs). Neste site, você encontrará diversos modelos que foram desenvolvidos utilizando o Bootstrap (http://getbootstrap.com/), que é um kit de desenvolvimento de interfaces produzido pela equipe do Twitter.

Para nosso exemplo, vamos utilizar o template Business Casual (http://s.ronoaldo.net/template-business). Esse template possui todas as páginas necessárias para nosso objetivo.

Template com Bootstrap

Como todo conteúdo de nosso site será inicialmente estático, vamos extrair o conteúdo do arquivo de template para uma nova pasta, chamada static e configurar os mapeamentos deste conteúdo para ser servido.

  1. Crie a pasta estática dentro de nosso projeto. No nitrous, você pode executar o comando:
     mkdir -p ~/workspace/ola-mundo/static
    
  2. Faça o download do template No nitrous, você pode executar o comando:
     wget http://s.ronoaldo.net/template-business -O ~/template.zip
    
  3. Extraia o arquivo para a raiz do projeto. No nitrous, você pode executar o comando:
     cd ~/workspace/ola-mundo/ ; unzip ~/template.zip
    
  4. Mova o conteúdo do template para a pasta static. No nitrous, você pode executar o comando:
     mv startbootstrap-business-casual-1.0.0/* static
     rmdir startbootstrap-business-casual-1.0.0
    

Você deve estar com o seguinte layout de projeto:

ola-mundo/
|--app.yaml
|--main.py 
|--static/
   |--css/
   |--fonts/
   |--img/
   |--js/
   |--LICENSE
   |--README.md
   |--about.html
   |--blog.html
   |--contact.html
   |--index.html

Vamos agora mapear todo o conteúdo da pasta estática para ser servido pelo App Engine. Edite o arquivo app.yaml e configure um novo handler antes do handler de script:

- url: /static
  static_dir: static

Ao acessar o endereço de preview de nossa aplicação em http://localhost:8080/static/index.html, você deve visualizar o site com o template já em funcionamento.

Para exemplificar a flexibilidade do mapeamento de arquivos estáticos, vamos utilizar expressões regulares para disponbilizar nossos arquivos html e as pasta css, fonts, img e js à partir da raiz da aplicação. Vamos substituir os handlers anteriores pelos seguintes:

- url: /
  static_files: static/index.html
  upload: static/index.html

- url: /(.*\.html)$
  static_files: static/\1
  upload: static/.*\.html$

- url: /css
  static_dir: static/css
- url: /fonts
  static_dir: static/fonts
- url: /img
  static_dir: static/img
- url: /js
  static_dir: static/js

- url: /.*
  script: main.application

O primeiro handler mapeia a raiz da aplicação / para o arquivo static/index.html. O segundo handler utiliza o poder de expressões regulares para capturar qualquer requisição de arquivo terminado por '.html', e o mapeia para o arquivo equivalente na pasta estática.

Esses handlers diferem-se dos demais por serem um mapeamento de arquivos e não de diretórios. Portanto precisamos informar também o parâmetro upload, que indica qual arquivo corresponde à cada mapeamento.

Os demais handlers apenas mapeiam as pastas estáticas que possuem imagens, folhas de estilo e scripts como diretórios estáticos, mas disponíveis à partir da raiz, como fizemos anteriormente.

Com estas modificações, o nosso site agora está disponível para ser servido à partir da raiz da aplicação, utilizando a infra-estrutura de recursos estáticos do Google App Engine, e o recurso de cache que otimiza o carregamento das páginas.

Implementando o formulário de contato

Para concluir nosso exemplo, vamos transformar o template do formulário de contato em um formulário que realmente processe a requisição do usuário. Para isso, iremos primeiro ajustar o conteúdo do template contact.html com as seguintes alterações:

  1. Localize a tag form e inclua dois atributos:
     <form role="form" method="post" action="/contato">
    
  2. Localize as tags de input deste formulário, e inclua o atribut name nas mesmas, com os valores:
    • name="name" para o campo Name
    • name="email" para o campo Email Address
    • name="phone" para o campo Phone Number
    • name="message" para o campo Message

Vamos agora implementar o tratamento do nosso formulário de contato, utilizando um RequestHandler e o serviço de envio de e-mails do App Engine. Abra o arquivo main.py e substitua nosso olá mundo pelo o seguinte código:

import webapp2

from google.appengine.api import mail, app_identity

class Contato(webapp2.RequestHandler):

    def post(self):
        name = self.request.get("name")
        email = self.request.get("email")
        phone = self.request.get("phone")
        message = self.request.get("message")

        noreply = "no-reply@%s.appspotmail.com" %\
        app_identity.get_application_id()

        body="""Prezado %s,
Obrigado pelo seu contato.
Em breve retornamos.

Mensagem recebida:
%s
--
www.orangeinstitute.com.br""" % (name, message)

        mail.send_mail(sender=noreply,
                       to=email,
                       subject="Obrigado pelo contato",
                       body=body)
        self.response.write('Email enviado com sucesso!')

application = webapp2.WSGIApplication([
    ('/contato', Contato)
], debug=True)

No código acima, estamos utilizando duas APIs do App Engine. A primeira permite que nosso código identifique o ID da aplicação que está em execução. A segunda, é o serviço de e-mails (mail), que permite enviar mensagens. Como remetente, o App Engine permite que você utilize alguns endereços:

  • O e-mail de um usuário logado com a conta do Google.
  • O e-mail de um usuário que está adicionado ao projeto.
  • O e-mail dentro do domínio appspotmail.com, que contenha o seu ID de aplicação.

No código acima, utilizamos as duas APIs em conjunto para produzir um código resiliente que pode ser executado em ambiente local ou de produção.

Para fazer o teste de envio do formulário, basta acessar a página no preview em http://localhost:8080/contact.html. Prencha o formulário e clique no botão "Submit".

Formulário de Contato

O browser deverá mostrar a mensagem de sucesso, como escrevemos esta mensagem na resposta. No terminal onde o servidor de desenvolvimento está em execução, você poderá visualizar o log do POST para '/contato', que contém os parâmetros utilizados para o envio. O servidor de desenvolvimento não envia os e-mails, apenas exibe a informação de log.

Formulário de Contato

Concluímos com isso nosso website, que está agora funcional e pronto para o ambiente de produção.