Google App Engine e Datastore

Sumário

Em um post anterior, vimos como manipular no servidor os dados vindos de um formulário HTML, com Google App Engine (GAE) e Python. Porém, até agora não vimos como armazenar dados vindos do cliente em um banco de dados.

Neste post vamos incrementar a aplicação de envio de mensagens via formulários HTML que construímos em um post anterior, possibilitando que as mensagens enviadas pelos usuários sejam gravadas em um banco de dados e que sejam listadas logo abaixo do formulário na página principal. Vamos começar vendo o mecanismo para armazenamento de dados do GAE: o datastore.

Datastore

Diferentemente do que acontece quando estamos lidando diretamente com um banco de dados relacional, onde criamos as tabelas com suas propriedades no banco de dados, para então ajeitar a aplicação para que possa acessar tal banco, com o GAE nós poderemos implementar uma app inteira sem ver código SQL algum.

Para criar uma entidade no BD, basta definir uma classe contendo os atributos que desejamos que façam parte de nosso BD. Vamos criar uma entidade chamada de Mensagem, que irá armazenar os atributos relacionados às mensagens enviadas pelo usuário:

from google.appengine.ext import db

class Mensagem(db.Model):
    nome_usuario = db.StringProperty(required=True)
    url = db.LinkProperty()
    email = db.EmailProperty()
    comentario = db.TextProperty(required=True)

Nossa classe estende a classe db.Model, o que é um requisito para que ela possa representar uma entidade no BD. Veja que a definição de uma entidade é bem simples, bastando declarar os atributos desejados como variáveis de classe. Já definimos também os tipos dos dados que cada atributo irá representar. No exemplo acima, StringProperty é o tipo que usamos para definição de campos de texto com menos de 500 caracteres. Já o tipo TextProperty é usado para a definição de campos para textos mais longos. Para conhecer a lista completa dos tipos de atributos disponíveis no framework de persistência do Google App Engine, consulte a documentação oficial.

Persistência de objetos no datastore

Uma vez que definimos uma entidade para representação de nossas mensagens no banco de dados, criar e salvar dados no BD é tão simples quanto criar um objeto:

msg = Mensagem()
msg.nome_usuario = "stummjr"
msg.url = "https://pythonhelp.wordpress.com"
msg.email = "stummjr@someemail.com"
msg.comentario = "Um comentário bem interessante!!!"
msg.put()

O procedimento é bem simples. Primeiramente, instanciamos um objeto da classe Mensagem (linha 1). Após isso, setamos os atributos desse objeto (linhas 2-5). Por fim, salvamos tal objeto no BD (linha 6).

Também poderíamos passar os valores para todos os atributos como parâmetros nomeados na construção do objeto:

msg = Mensagem(
    nome_usuario="stummjr",
    url="https://pythonhelp.wordpress.com",
    email="stummjr@someemail.com",
    comentario="Um comentário bem interessante!!!"
)
msg.put()

Agora já podemos integrar o código acima em nossa app do post anterior. Basta criar o objeto Mensagem e persistir esse objeto no BD dentro do método post, que irá tratar os dados enviados pelo usuário via formulário:

def post(self):
    msg = Mensagem(
        nome_usuario=self.request.get('nome_usuario'),
        url=self.request.get('url'),
        email=self.request.get('email'),
        comentario=self.request.get('comentario')
    )
    msg.put()
    self.redirect('/')

Observe que, ao final do método post(), fizemos uma chamada ao método self.redirect('/'), que faz com que o usuário seja redirecionado para o recurso recebido como parâmetro (nesse caso, a raiz do site). Fizemos isso porque se apenas enviarmos ao usuário uma resposta de confirmação, ele poderá causar a submissão duplicada do formulário caso solicite ao navegador que este atualize a página (o famoso F5). Assim, redirecionamos a outro recurso para evitar que uma sequência de refreshes na página recebida como resposta a um POST possa causar várias inserções duplicadas.

Adicionamos os campos nome_usuario, url e email ao formulário contido na variável html:

html = """
    <html>
        <head>
            <title></title>
        </head>
        <body>
            <form method="POST" action="/">
                Nome: <p><input type="text" name="nome_usuario"></p>
                URL: <p><input type="text" name="url"></p>
                Email: <p><input type="text" name="email"></p>
                Comentário:<p><textarea name="comentario" cols="40" rows="10"></textarea></p>
                <button type="submit">Enviar</button>
            </form>
        </body>
    </html>
"""

Feito isso, agora nosso app já é capaz de salvar no BD os comentários enviados pelos usuários. Caso já tenha visto o post anterior, apenas complemente aquele código com o que foi apresentado até aqui e execute servidor de desevolvimento local. Se não viu o post anterior, o código completo é apresentado na seção “O projeto completo”.

A interface administrativa local

Enquanto estamos desenvolvendo nosso app, podemos querer verificar se determinado objeto foi realmente persistido no BD ou não. Caso ainda não tenhamos desenvolvido algum método para listagem de objetos em nosso app, podemos utilizar a interface administrativa local do GAE, que fica disponível na URL http://localhost:8080/_ah/admin/ (obs.: o servidor de testes precisa estar em execução)

http7.1

Basta selecionar o tipo de objeto e solicitar a listagem.

Consultas ao datastore

Para realizar consultas aos objetos que estão no banco de dados, basta utilizar os métodos all(), filter(), dentre outros que são comuns a todas as classes que estendem db.Model. Veja alguns exemplos:

# obtém todos os registros de Mensagem do BD
msgs = Mensagem.all()

# pega somente Mensagens do "John Doe"
msgs.filter('nome_usuario =', "John Doe")

# ordena pelo endereço do email, de forma DESC (-)
msgs.order('-email')

for msg in msgs:
    print msg.email

Veja mais métodos para consulta de dados na documentação oficial.

Em nosso projeto, precisamos apenas adicionar ao método get() a funcionalidade de listagem das mensagens já existentes no BD, logo abaixo do formulário de submissão de mensagens. Com base nos métodos de consulta que vimos, podemos montar nosso método:

def get(self):
    self.response.out.write(html)
    self.response.out.write('<ul>')
    for msg in Mensagem.all():
        self.response.out.write('<li>' + unicode(msg.comentario) + '</li>')
    self.response.out.write('</ul>')

Tenha em mente que imprimir código HTML diretamente não é uma boa abordagem para desenvolvimento web. Existem técnicas mais recomendadas, como a utilização de modelos de páginas. Mas isso é um assunto para um outro post.

O projeto completo

Como já vimos anteriormente, a estrutura do diretório do nosso projeto deve ficar como:


projeto/
  handlers.py
  app.yaml

Segue abaixo o código dos arquivos relacionados:

Arquivo handlers.py:

import webapp2
from google.appengine.ext import db
from google.appengine.ext.webapp.util import run_wsgi_app

html = """
    <html>
        <head>
            <title></title>
        </head>
        <body>
            <form method="POST" action="/">
                Nome: <p><input type="text" name="nome_usuario"></p>
                URL: <p><input type="text" name="url"></p>
                Email: <p><input type="text" name="email"></p>
                Comentário:<p><textarea name="comentario" cols="40" rows="10"></textarea></p>
                <button type="submit">Enviar</button>
            </form>
        </body>
    </html>
"""

class PostHandler(webapp2.RequestHandler):

    def get(self):
        self.response.out.write(html)
        self.response.out.write('<ul>')
        for msg in Mensagem.all():
            self.response.out.write('<li>' + unicode(msg.comentario) + '</li>')
        self.response.out.write('</ul>')

    def post(self):
        msg = Mensagem(
            nome_usuario=self.request.get('nome_usuario'),
            url=self.request.get('url'),
            email=self.request.get('email'),
            comentario=self.request.get('comentario')
        )
        msg.put()
        self.redirect('/')


class Mensagem(db.Model):
    nome_usuario = db.StringProperty(required=True)
    url = db.LinkProperty()
    email = db.EmailProperty()
    comentario = db.TextProperty(required=True)


mapeamento = [
    ('/', PostHandler),
]
app = webapp2.WSGIApplication(mapeamento)
run_wsgi_app(app)

Arquivo app.yaml:


application: gaepost
version: 1
runtime: python
api_version: 1

handlers:
- url: /.*
  script: handlers.py

Com os arquivos acima, você já pode executar o projeto usando o ambiente de testes.

Aprenda mais

Caso tenha dificuldades para entender o funcionamento do Google App Engine, leia os posts anteriores nos quais introduzimos o assunto:

  1. Desenvolvendo com Python e Google App Engine
  2. Tratando dados de formulários com Google App Engine

Se quiser ir mais a fundo no GAE, sugiro que leia a documentação oficial, e também o livro Programming Google App Engine.

Função zip em Python

zipper

Aviso aos navegantes: a função zip, apesar do seu nome, não serve para zipar um arquivo. Se estiver procurando por isso, veja o módulo zipfile.


Escreva em Python uma função que, dadas duas listas não vazias e de mesmo comprimento, retorne como resultado uma nova lista contendo em cada posição a soma dos elementos correspondentes nas duas listas recebidas. Exemplo:

def soma_listas(l1, l2):
    # seu código aqui
    # ...
    return l3

l1 = [1, 2, 3]
l2 = [4, 5, 6]
print soma_listas(l1, l2) # deve imprimir [5, 7, 9]

Uma ideia que pode surgir é:

def soma_listas(l1, l2):
    l3 = []
    for i in range(0, len(l1)):
        l3.append(l1[i] + l2[i])
    return l3

Isso para quem ainda não conhece a função builtin zip. A documentação da função diz (tradução própria):

Esta função retorna uma lista de tuplas, onde a i-ésima tupla contém o i-ésimo elemento de cada um dos argumentos.

O exemplo usado na documentação oficial é bem claro:

>>> x = [1, 2, 3]
>>> y = [4, 5, 6]
>>> print zip(x, y)
[(1, 4), (2, 5), (3, 6)]

O que a função fez foi: para cada elemento da lista x, juntou o elemento correspondente em posição da lista y a ele em uma tupla e a adicionou a uma lista.


[ 1, 2, 3 ]
-zip--------------- ====> [(1, 4), (2, 5), (3, 6)]
[ 4, 5, 6 ]

Sacou por que o post inicia com a imagem de um zíper?

Isso é particularmente útil quando precisarmos percorrer duas sequências de elementos em paralelo. A função soma_listas, usando zip, fica:

def soma_listas(l1, l2):
    l3 = []
    for x in zip(l1, l2):
        l3.append(x[0]+x[1])
    return l3

Ou então, usando list comprehensions:

def soma_listas(l1, l2):
    return [x[0]+x[1] for x in zip(l1, l2)]

Para finalizar, vamos escrever agora uma funçãozinha que receba duas listas como entrada e retorne como resultado uma nova lista contendo, em cada posição, o maior valor da posição correspondente nas duas listas. Por exemplo:

maiores([1, 2, 9], [2, 1, 7]) -> [2, 2, 9]

Com a função zip, fica barbada:

def maiores(l1, l2):
    l3 = []
    for x, y in zip(l1, l2):
        l3.append(x if x > y else y)
    return l3

Como a função zip retorna uma lista de tuplas de 2 valores, podemos desempacotar essas tuplas diretamente em x e y no próprio for, assim como fizemos no código acima. (for x, y in zip(l1, l2))

Obs.: Caso não tenha entendido o código l3.append(x if x > y else y), leia também o post “Seleção Condicional de Valores”.

Tratando dados de formulários com Google App Engine

http6.0Os formulários HTML são uma das formas mais usadas para passagem de dados dos usuários para os servidores na web. Você, usuário, quantas vezes por dia preenche algum formulário e clica em um botãozinho próximo a ele? Se você faz uma busca no google.com, você preenche um campo de entrada de um formulário e clica no botão “Buscar”. Quando envia um tweet ou uma mensagem no Facebook, você preenche campos de entrada de um formulário e os submete a um servidor. Quando faz login no GMail, você preenche dois campos de entrada (usuário e senha) e então clica no botão “Login” para submeter os dados ao servidor para que este verifique suas credenciais. Ou seja, você usa formulários pra caramba!

Neste post rápido daremos sequência ao post anterior, vendo como implementar na prática o tratamento de dados vindos de formulários em um app web usando Python e Google App Engine (GAE). (caso você não conheça o Google App Engine, leia antes o post anterior)

POSTando dados na web

O objetivo é criar uma aplicaçãozinha web bem simples na qual o usuário possa escrever uma mensagem em um formulário e receber de volta essa mensagem no navegador. Vamos criar esse app do mesmo modo que fizemos no post anterior. Crie um diretório mural e dentro dele crie os arquivos app.yaml (que vai conter as configurações) e handlers.py (que vai conter o código Python para manipular as requisições).

app.yaml:

application: mural
version: 1
runtime: python
api_version: 1

handlers:
- url: /.*
  script: handlers.py

O código acima apresentado é o conteúdo do arquivo de configurações app.yaml, que define alguns parâmetros para o projeto.
O manipulador de requisições (handlers.py) deverá ser implementado da seguinte forma:

  1. Ao receber uma requisição GET, este deve retornar um HTML contendo um formulário para que o usuário escreva sua mensagen;
  2. Ao receber uma requisição POST (vinda do formulário recebido pelo usuário quando executa o GET), este deve apresentar uma página HTML contendo o texto submetido pelo usuário.

Para simplificar a nossa implementação, vamos colocar o seguinte código HTML dentro de uma string no nosso código Python:

<html>
    <head><title></title></head>
    <body>
        <form method="POST" action="/">
            <textarea name="comentario" cols="40" rows="10">
            </textarea>
            <button type="submit">Enviar</button>
        </form>
    </body>
</html>

Faremos isso para que, ao receber uma requisição do tipo GET, nosso appzinho possa simplesmente gravar em self.response.out a string contendo o código acima, fazendo com o formulário HTML seja passado como resposta à requisição GET do cliente. (você já sabe, do post anterior, que ao escrever um valor qualquer em self.response.out estamos adicionando esse valor ao corpo da mensagem de resposta)

Segue abaixo o código de nosso app:

import webapp2
from google.appengine.ext.webapp.util import run_wsgi_app


html = """
<html>
    <head><title></title></head>
    <body>
        <form method="POST" action="/">
            <textarea name="comentario" cols="40" rows="10">
            </textarea>
            <button type="submit">Enviar</button>
        </form>
    </body>
</html>
"""

class PostHandler(webapp2.RequestHandler):
    def getself):
        self.response.out.write(html)

    def post(self):
        comentario = self.request.get('comentario')
        self.response.out.write(comentario)


mapeamento = [
    ('/', PostHandler),
]
app = webapp2.WSGIApplication(mapeamento)
run_wsgi_app(app)

Observe que a escrita do formulário HTML como resposta ao cliente ocorre dentro do método get(), que é chamado pelo ambiente de suporte sempre que houver uma requisição GET para /. No form temos dois atributos:

  1. method="POST": indica o método HTTP a ser usado para transmitir os dados ao servidor quando o usuário clicar no botão Enviar;
  2. action="/": indica para qual recurso serão enviados os dados do form quando o usuário submeter as informações clicando no botão.

Ou seja, após preencher o campo comentario e clicar no botão Enviar, o usuário faz com que o navegador envie uma mensagem HTTP POST ao recurso / do servidor. Essa mensagem contém, além do cabeçalho, a variável comentario com o conteúdo escrito pelo usuário nesse campo.

Como todas as requisições ao recurso / são tratadas por instâncias de PostHandler (veja a variável mapeamento), definimos em PostHandler um método chamado post() para lidar com requisições HTTP POST a esse recurso. Assim como ocorre para o método get(), o framework que estamos usando (webapp2) define que requisições do tipo POST serão tratadas por um método chamado post() implementado na classe responsável por manipular as requisições.

Sabendo disso, agora observe nosso método post() (chamado pelo ambiente de suporte quando for recebida uma requisição POST).

def post(self):
    comentario = self.request.get('comentario')
    self.response.out.write(comentario)

Veja que temos um atributo self.request que contém os campos enviados pelo usuário e que os valores desses campos podem ser acessados através de self.request.get('nome_do_campo').

Para simplificar nosso app, estamos apenas ecoando a mensagem enviada pelo usuário, de forma que após clicar em Enviar, este receba de volta a mensagem que enviou ao servidor (self.response.out.write(comentario)).

Agora, vamos testar o nosso app.

Testando o app

Para testar o app recém desenvolvido, siga os passos descritos no post anterior, trocando apenas o nome do diretório para o nome do diretório do projeto que criamos neste post.

GET x POST

No código do nosso appzinho, o manipulador trata dois tipos de requisições HTTP: GET e POST. Vamos ver rapidamente as diferenças entre elas.

GET

Método usado geralmente para envio de requisições que não resultem em alterações no servidor. Como o nome já diz, é indicado para a obtenção de dados.

Os parâmetros são passados para o servidor através da própria URL:

http://www.google.com/search?q=teste

No exemplo acima, passamos a variável q com o valor teste para o recurso /search, de forma que o servidor da Google vai acessar essa variável e realizar a busca usando o valor dela como filtro.

POST

Método usado quando a requisição causa alterações no servidor, como em uma requisição que envia dados a serem gravados num banco de dados. Diferentemente do método GET, quando usamos o POST em uma requisição HTTP, os parâmetros não são codificados na URL, mas sim no corpo da mensagem HTTP a ser enviada ao servidor.

Atenção

Deve ficar claro que a forma como armazenamos o conteúdo HTML do formulário (em uma variável no código) não é a melhor solução. Fizemos isso com a única finalidade de simplificar nosso exemplo. Em projetos “de verdade”, normalmente usamos um mecanismo para renderização de templates, que vem incluso com a maioria dos frameworks web. Se quiser saber mais, veja a documentação oficial do GAE sobre templates.

Sabendo como tratar dados recebidos do usuário e como manipular requisições GET e POST, você já pode começar a fazer projetinhos maiores. Vá em frente! 🙂

Desenvolvendo com Python e Google App Engine

Importante: a documentação oficial do Google App Engine aqui referenciada está disponível em português. Basta selecionar o idioma no canto inferior direito, caso esteja em outro idioma.

Sumário do post:

No último post nós vimos como desenvolver um serviço web bem simples usando Python e CGI. Mas, conforme vimos no próprio post, CGI não é o mecanismo mais adequado para dar suporte a um aplicativo web.

Do seu início até final da década de 90, a web era usada principalmente para a publicação de conteúdo em HTML. Porém, algum tempo depois ela passou a ser usada também como um ambiente para execução de aplicações completas.

O crescimento das aplicações possibilitou a observação de que boa parte dos projetos web tinham alguns componentes básicos em comum, gerando uma grande quantidade de retrabalho a cada novo projeto, o que acarretaria um alto custo para cada projeto, e também aumentaria as chances de o sistema apresentar defeitos. Visando eliminar boa parte do retrabalho, foram criados os frameworks de desenvolvimento, que fornecem vários serviços básicos para serem reutilizados pela aplicação, de forma que esta não precise reimplementá-los. (obs.: framework poderia ser traduzido como arcabouço)

A segurança de um aplicativo é um dos aspectos para os quais os frameworks possuem um conjunto de serviços para oferecer ao desenvolvedor. Isso é muito importante, pois não é todo programador que sabe como lidar com aspectos de segurança de uma forma adequada. Gerenciamento do banco de dados é outro serviço comumente oferecidos por frameworks para desenvolvimento web. Enfim, eles oferecem um ferramental bem interessante para que o desenvolvedor de uma aplicação web se preocupe principalmente com as funcionalidades e com a lógica de negócio do seu aplicativo, deixando um pouco “de lado” alguns aspectos que ficam sob responsabilidade do framework.

Alguns dos principais frameworks para desenvolvimento web em Python são:

O que nós vamos utilizar neste post não é apenas um framework, mas sim um conjunto completo para desenvolvimento, implantação e execução de aplicativos web, fornecido pela Google: o Google App Engine. Neste post, iremos apresentar o desenvolvimento de um aplicativozinho web que retorna alguma frase famosa para o usuário, algo como a frase do dia. Mas antes disso, vamos ver do que se trata o Google App Engine.

O Google App Engine

gaelogo

O Google App Engine (GAE) é uma plataforma para desenvolvimento de aplicativos web para serem hospedados na infraestrutura da Google. Além de oferecer uma interface bem interessante para gerenciamento dos aplicativos web (veja na figura abaixo), o GAE fornece de lambuja o balanceamento de carga da aplicação, espalhando a execução dela por vários servidores se assim for necessário, e alocando automaticamente mais recursos para a aplicação que estiver rodando nessa infraestrutura. Além da disponibilizar e gerenciar a infraestrura para o desenvolvedor, o GAE ainda provê uma série de facilidades para o desenvolvimento, como um framework para persistência de dados, para tratamento de requisições, para caching de dados, dentre outras coisas legais. E, pra ficar mais interessante ainda, é gratuito para quem quiser testar ou desenvolver aplicativos sem maiores pretensões.

http.5.1

Painel de controle do GAE

Bom, chega de propaganda e vamos ao que interessa!

Instalando e configurando o GAE

Como já foi comentado, a hospedagem de aplicativos escritos para o GAE fica por conta da Google, mas para podermos testar localmente nosso app, é preciso que instalemos um ambiente próprio para isso. Siga os passos abaixo:

  1. Baixe o arquivo correspondente à sua plataforma clicando aqui;
  2. Descompacte o arquivo acima no diretório de sua preferência;

Mais informações sobre a instalação do ambiente podem ser encontradas aqui.

Desenvolvendo nosso app

Vamos começar agora o desenvolvimento de nosso aplicativo de frase do dia. A primeira coisa a fazer é criar um diretório onde nosso app ficará localizado. Podemos chamar tal diretório de frasedodia, ou o nome que você achar mais conveniente. Dentro do diretório recém criado, crie um arquivo chamado frasedodia.py e outro chamado app.yaml. O primeiro é a nossa aplicação em si e o último é o arquivo que irá conter as configurações do nosso projeto.
A estrutura do nosso projeto ficará assim:

frasedodia/
  frasedodia.py
  app.yaml

Agora, precisamos escrever as configurações do nosso projeto. Para isso, cole o seguinte conteúdo dentro do arquivo app.yaml:

application: frasedodia
version: 1
runtime: python
api_version: 1

handlers:
- url: /.*
  script: frasedodia.py

O mais importante a entender neste momento é a opção handlers. Dentro dela, temos um item chamado url com valor /.* e um atributo script com o nome do nosso arquivo .py como valor. Essa configuração significa que requisições para qualquer recurso dentro do projeto (/.*) serão encaminhadas para o arquivo frasedodia.py. Se por acaso quiséssemos que somente as requisições para os recursos contidos em uma pasta app fossem encaminhadas ao arquivo handlers.py, faríamos o seguinte:

handlers:
- url: /app/.*
  script: handlers.py

Assim, uma requisição para frasedodia.appspot.com/app/qualquercoisa seria encaminhada para o script definido na configuração acima (handlers.py). Agora vamos nos concentrar no código que irá atender às requisições do usuário.

Manipulador de requisições

Se quisermos tomar proveito das vantagens que o GAE nos fornece, é interessante que não usemos CGI. Vamos utilizar um framework para web bem simples chamado webapp2 que já é fornecido juntamente com o GAE.

O primeiro passo que devemos realizar é definir quais classes terão suas instâncias responsáveis por manipular requisições para determinadas URLs. Uma vez que já definimos que todas as requisições para o aplicativo serão direcionadas a frasedodia.py, devemos agora definir, dentro desse arquivo, o mapeamento de URLs para classes. Insira o código a seguir em frasedodia.py:

import webapp2
from google.appengine.ext.webapp.util import run_wsgi_app

mapeamento = [
    ('/', FraseDoDia)
]
app = webapp2.WSGIApplication(mapeamento)
run_wsgi_app(app)

No código acima, definimos que toda requisição para a raiz de nosso app será tratada por instâncias da classe FraseDoDia. Mas, a classe FraseDoDia não é uma classe qualquer. Ela deve obrigatoriamente estender a classe webapp2.RequestHandler, para que suas instâncias sejam também instâncias de RequestHandler. Nosso app irá criar uma instância da classe FraseDoDia e irá chamar nessa instância o método apropriado para tratar a requisição (get() para o caso de requisição GET ou post() para o caso de requisição POST). Esse método irá tratar a requisição e devolver uma resposta ao cliente. Tudo isso é feito automagicamente pelo framework de suporte, ficando ao nosso cargo apenas criar as classes que irão ser responsáveis por tratar as requisições.

Objetos do tipo RequestHandler possuem dois atributos muito importantes para o desenvolvedor:

  • self.request: é uma instância de Request, contendo a requisição recebida do usuário;
  • self.response: é uma instância de Response, contendo a mensagem a ser enviada como resposta à requisição do usuário.

Dessa forma, instâncias da classe RequestHandler devem fazer o seguinte, a cada requisição recebida:

  1. Tratar os dados recebidos através do objeto self.request;
  2. Gravar os dados a serem enviados ao usuário no objeto self.response.

Assim, devemos então implementar nossa classe FraseDoDia como uma subclasse de RequestHandler e nela implementar o método get() para retornar a frase para o cliente. Vamos então adicionar o código da classe ao nosso arquivo frasedodia.py:

import webapp2
from google.appengine.ext.webapp.util import run_wsgi_app

class FraseDoDia(webapp2.RequestHandler):

    def get(self):
        self.response.headers['Content-Type'] = 'text/json'
        self.response.out.write('{"frase": "Uma frase qualquer!"}')

mapeamento = [
    ('/', FraseDoDia)
]
app = webapp2.WSGIApplication(mapeamento)
run_wsgi_app(app)

Vendo o código acima, dá pra entender que quando ocorre uma requisição GET para o recurso / de nossa aplicação, um objeto do tipo FraseDoDia será designado pelo framework de suporte para tratar tal requisição, executando o método get(). Esse método, por sua vez, faz duas coisas:

  1. Adiciona ao cabeçalho da resposta HTTP — self.response.headers — o tipo do conteúdo a ser enviado como resposta ao cliente (text/json), de forma que o navegador saiba o que fazer com o conteúdo recebido;
  2. Escreve no corpo da resposta HTTP — self.response — o conteúdo a ser enviado ao cliente, que são dados representados em formato JSON.

Tudo o que precisamos fazer é implementar a classe que vai tratar das requisições à determinadas URLs, sem a necessidade de chamá-las nem nada. Quem faz isso é o ambiente de suporte provido pelo framework webapp.

Tendo implementado uma versão básica de nosso serviço, vamos colocar nosso ambiente de testes em funcionamento.

Executando o ambiente de testes

Vá até o local onde você extraiu o ambiente do GAE em sua máquina. Dentro dessa pasta, execute o seguinte comando:

./dev_appserver.py /caminho/para/o/projeto/frasedodia/

O caminho mostrado acima, passado como argumento para o programa dev_appserver.py, é referente ao diretório onde criamos os arquivos app.yaml e frasedodia.py.

O comando acima iniciou o servidor local para desenvolvimento e testes na porta 8080. Podemos acessar o serviço através da URL: http://localhost:8080/. Ao acessar essa URL, você terá como resposta o conteúdo JSON retornado pelo nosso serviço Web:

{"frase": "Uma frase qualquer!"}

Por enquanto estamos retornando apenas uma frase constante, e a idéia do serviço é retornar uma frase aleatória. Para tanto, temos algumas opções:

  • Manter algumas strings em memória em uma lista;
  • Manter registros no banco de dados e buscar um ao receber uma requisição.

Por fins de simplicidade, vamos seguir a primeira opção. Vamos adicionar uma nova classe chamada FraseAleatoriaDoDia, implementar o método get(), já que essa classe vai estender RequestHandler, e adicionar a nova classe no mapeamento de URLs, relacionado a URL /random. Veja o código completo abaixo:

import random
import webapp2
from google.appengine.ext.webapp.util import run_wsgi_app

class FraseDoDia(webapp2.RequestHandler):
    def get(self):
        self.response.headers['Content-Type'] = 'text/json'
        self.response.out.write('{"frase": "Uma frase qualquer!"}')

class FraseAleatoriaDoDia(webapp2.RequestHandler):
    frases = [
        ('Insanity: doing the same thing over and over again and expecting different results', 'Albert Einstein'),
        ('The world is a dangerous place to live; not because of the people who are evil, but because of the people who don\'t do anything about it.', 'Albert Einstein'),
        ('A person who never made a mistake never tried anything new.', 'Albert Einstein'),
        ('Love all, trust a few, do wrong to none.', 'William Shakespeare'),
        ('A fool thinks himself to be wise, but a wise man knows himself to be a fool.', 'William Shakespeare'),
        ('Darkness cannot drive out darkness; only light can do that. Hate cannot drive out hate; only love can do that.', 'Martin Luther King, Jr.')
    ]

    def get(self):
        self.response.headers['Content-Type'] = 'text/json'
        i = random.randint(0, len(self.frases)-1)
        self.response.out.write('{"frase": "%s", "autor": "%s"}' % (self.frases[i][0], self.frases[i][1]))

mapeamento = [
    ('/', FraseDoDia),
    ('/random', FraseAleatoriaDoDia),
]
app = webapp2.WSGIApplication(mapeamento)
run_wsgi_app(app)

Rode o servidor de aplicação local com./dev_appserver.py /caminho/para/o/projeto/frasedodia/ e acesse http://localhost:8080/random para obter uma das frases.

Com o app funcionando, agora você pode registrar e fazer upload do mesmo para os servidores da Google, se assim quiser. Se desejar colocar em produção o app que implementamos aqui, siga as instruções contidas na documentação oficial, na seção que trata sobre o upload de um app.

Depois de fazer o upload de seu app, você poderá acessá-lo através de uma URL dentro de .appspot.com, como: http://id_da_aplicacao.appspot.com. Para ver informações sobre a sua aplicação, você pode acessar o painel de controle da sua conta no GAE através do endereço: appengine.google.com/.

O Google App Engine é uma ótima opção para dar suporte a uma aplicação web, visto que com ele eliminamos a necessidade de contratar um plano de hospedagem, de gerenciar o servidor web, de cuidar do balanceamento de carga, etc. Vale a pena dar uma estudada mais a fundo no assunto. 🙂

Aprenda mais