Het bouwen van een webapplicatie met behulp van Google App Engine 

Waar je vroeger een server moest kopen of huren om je nieuwe website of -applicatie te hosten, zet je die nu met een paar klikken in de cloud. Iedereen is inmiddels wel bekend met de voordelen van de cloud: goedkoop, robuust, meer flexibiliteit en makkelijker schaalbaar. In deze workshop zetten we onze eigen code in de cloud—die van Google—en bouwen we een webapplicatie met behulp van Google App Engine.

Het begrip ‘cloud computing’ is een verzamelterm van diensten die te maken hebben met het centraliseren van hardware, software en data. De verschillende cloud diensten worden meestal opgedeeld in lagen, waarvan de namen allemaal eindigen op –aaS, wat voor ‘as a Service’ staat. Hier draait het om in de cloud: zoals het lichtnet electriciteit op aanvraag levert, doet de cloud dat met hardware, software en data. De lagen zijn:

          Infrastructure as a Service (IaaS): via virtualisatie wordt infrastructuur als servers, load balancers en opslag aangeboden. De gebruiker heeft complete vrijheid over de hardware en voert zelf de configuratie en het onderhoud uit.

          Platform as a Service (PaaS): via een platform, framework of programmeertaal, krijgt de gebruiker toegang tot een gedeelte van de infrastructuur, binnen vastgestelde grenzen. Onderhoud wordt gedaan door de dienstaanbieder, waarbij het opstarten en monitoren van een nieuwe applicatie is vereenvoudigd.

          Software as a Service (SaaS): de dienstaanbieder heeft volledige controle over de applicatie en biedt een bepaalde dienst, zoals e-mail, klantbeheer of administratie, aan vanuit de cloud.

Vanzelfsprekend vereist de IaaS laag het meeste kennis en moeite: je moet eigenlijk alles zelf doen qua onderhoud. Daar tegenover staat dat de kosten voor IaaS diensten over het algemeen lager zijn, omdat je zelf de verantwoordelijkheid draagt. Mocht je het onderhoud en de details van het systeembeheer echter liever willen uitbesteden, dan is PaaS een beter alternatief—dat wordt dan allemaal voor je geregeld. Je kunt meteen aan de slag met je applicatie, maar je vrijheid wordt daarbij wel deels ingeperkt en je kunt alleen binnen de vastgestelde grenzen te werk gaan. De laatste laag van de cloud is de laag waarmee iedereen in het digitale tijdperk meer dan bekend is. In feite is elke webapplicatie, van Google tot Facebook tot Twitter, een voorbeeld van een SaaS dienst.

Google biedt diensten aan in alle drie de lagen van de cloud, maar speerpunt van webapplicatie ontwikkeling voor Google is de Google App Engine (GAE). De App Engine is een voorbeeld van een PaaS dienst, waarbij we het platform gebruiken om zo snel en efficient mogelijk apps te bouwen, die we vervolgens met gemak in de Google cloud kunnen parkeren. In deze workshop is dat precies wat we gaan doen: we bouwen een prikbord webapp in Python met behulp van Google App Engine, in de Google cloud.

Google App Engine
Het idee achter de dienst is simpel: ontwikkelaars verliezen veel tijd met het van de grond af opbouwen van een applicatie—tijd die beter besteed zou zijn aan het bouwen en uitbreiden van de daadwerkelijke functionaliteiten. Elke ontwikkelaar weet dat je een hoop tijd kwijt bent aan randvoorwaarden, waarbij het opzetten van de infrastructuur een groot deel van de tijd in beslag neemt. Het is alsof je eerst zelf de wielen op je auto moet monteren voordat je kunt instappen—wat als je dat allemaal kunt overslaan, en je dankzij de Google App Engine meteen onderweg bent?

 

Je kunt je webapplicatie via Google Apps onderbrengen op je eigen domeinnaam, of gratis onder jouwnaam.appspot.com. Het platform is beschikbaar in Java, Python, PHP en Go, waarbij de ondersteuning voor de laatste twee programmeertalen nog in experimentele staat verkeert. In deze workshop gebruiken we Python, omdat het lekker makkelijk wegleest en we met erg weinig regels een behoorlijk complexe applicatie kunnen schrijven. De code van deze workshop is te vinden in GitHub via github.com/linuxmagNL, het resultaat is live te bekijken op linmagnl.appspot.com/.

Ontwikkelomgeving
De eerste stap is het downloaden van de ontwikkelomgeving. De Google App Engine Software Development Kit (SDK) is te downloaden via de website en is beschikbaar voor Linux, Windows en Mac OS X. De SDK bestaat uit de benodigde libraries plus enkele programma’s die we gebruiken voor de lokale ontwikkeling van de applicatie en het uitrollen (deployen) van de applicatie naar de productieomgeving in de cloud, op bijvoorbeeld appspot.com. Windows en Mac gebruikers klikken respectievelijk op de ‘Run’en ‘Deploy’ buttons van de Google App Engine Launcher om hetzelfde te bereiken zonder commando’s uit te hoeven voeren. Er is overigens ook een Eclipse plugin beschikbaar. Op Linux installeren we de SDK door simpelweg het zip bestand op de gewenste locatie uit te pakken.

 

Hallo wereld
Zoals de aloude traditie dicteert, beginnen we met het begroeten van de wereld. Maak in dezelfde map als waar we de SDK hebben uitgepakt, bijvoorbeeld in onze home directory, de map ‘hallowereld/ ‘aan. Daarin plaatsen we twee bestanden: een python bestand voor de webapplicatie, plus een configuratie bestand genaamd ‘app.yaml’. Ons python bestand ‘hallowereld.py’ ziet er als volgt uit:

 

import webapp2
class MainPage(webapp2.RequestHandler):

    def get(self):
        self.response.headers[‘Content-Type’] = ‘text/plain’
        self.response.write(‘Hallo, Wereld!’)

application = webapp2.WSGIApplication([(‘/’, MainPage),],
debug=True)

 

De code is erg simpel: we gebruiken een class om HTTP requests af te handelen, waarbij we als antwoord de tekst ‘Hallo, Wereld! teruggeven. In ‘app.yaml ‘plaatsen we:

 

application: linmagnl
version: 1
runtime: python27
api_version: 1
threadsafe: true

handlers:
– url: /.*
  script: hallowereld.application

Dit bestand is vergelijkbaar met het manifesto bestand dat we kennen van bijvoorbeeld Android apps, maar we kunnen ook aparte handlers aangeven, wat weer lijkt op de routes tabel in bijvoorbeeld Rails apps. Alle configuratie vindt plaats in dit bestand. Voor nu hoeven we er echter niet al te veel meer naar te kijken.

 

Om te kijken of het werkt, starten we de ontwikkelingswebserver op, die we kunnen vinden in de SDK in de vorm van het ‘dev_appserver.py’ programma. Als we zelf ‘python hallowereld.py’ proberen uit te voeren dan krijgen we een foutmelding omdat python zelf nog nooit van webapp2 heeft gehoord: dat is immers onderdeel van de SDK, niet van Python. We gebruiken het volgende commando (als je op een andere server ontwikkelt, voeg dan ‘–host=0.0.0.0’ toe):

 

$ google_appengine/dev_appserver.py –host=0.0.0.0 hallowereld/
INFO     2013-07-28 03:27:00,120 sdk_update_checker.py:244] Checking for updates to the SDK.
INFO     2013-07-28 03:27:00,415 sdk_update_checker.py:272] The SDK is up to date.
INFO     2013-07-28 03:27:00,457 api_server.py:138] Starting API server at: http://localhost:58873
INFO     2013-07-28 03:27:00,467 dispatcher.py:164] Starting module “default” running at: http://0.0.0.0:8080
INFO     2013-07-28 03:27:00,470 admin_server.py:117] Starting admin server at: http://localhost:8000
INFO     2013-07-28 03:27:05,084 module.py:595] default: “GET / HTTP/1.1” 200 13
INFO     2013-07-28 03:27:05,355 module.py:595] default: “GET /favicon.ico HTTP/1.1” 404 154

Als alles goed is gegaan kan je nu in je browser naar localhost:8080 gaan en zie daar—Hallo, Wereld! De ontwikkelingswebserver houdt je code in de gaten, dus je hoeft het niet elke keer opnieuw op te starten als je een verandering aanbrengt.

 

Gebruikers
In bijna elke serieuze web applicatie hebben we authenticatie en authorisatie nodig. Dit zijn twee verschillende concepten, die vaak met elkaar worden verward. Authenticatie is het verifieren van de identiteit van de gebruiker; in het geval van authorisatie controleren we of een gebruiker toegang heeft tot de opgevraagde informatie. In moderne applicaties wordt het steeds gebruikelijker om authenticatie af te laten handelen door een derde partij, via bijvoorbeeld Facebook Connect of OpenID. Dat scheelt een hoop moeite, dus laten we hetzelfde doen, via de Users API wordt dat namelijk allemaal voor ons geregeld. Ons hallowereld.py script ziet er nu zo uit:

 

from google.appengine.api import users
import webapp2

providers = {
    ‘Google’   : ‘https://www.google.com/accounts/o8/id’,
    ‘Yahoo’    : ‘yahoo.com’,
    ‘MyOpenID’ : ‘myopenid.com’,
}

class MainPage(webapp2.RequestHandler):
    def get(self):
        user = users.get_current_user()
        if user:
            self.response.out.write(‘Hallo, <em>%s</em>! [<a href=”%s”>uitloggen</a>]’ % (
                user.nickname(), users.create_logout_url(self.request.uri)))
        else:
            self.response.out.write(‘Hallo, wereld! Log in via: ‘)
            for name, uri in providers.items():
                self.response.out.write(‘[<a href=”%s”>%s</a>]’ % (
                    users.create_login_url(federated_identity=uri), name))

application = webapp2.WSGIApplication([(‘/’, MainPage),], debug=True)

 

Als we de pagina nu vernieuwen, zien we dat we kunnen inloggen via OpenID, Google en Yahoo. Je zou zelf nog extra aanbieders kunnen toevoegen, want een steeds groter wordend aantal bedrijven biedt OpenID authenticatie aan. Via de Users API controleren we eerst of de gebruiker is ingelogd. Als dat zo is, begroeten we de gebruiker. Zo niet, dan bieden we een login link voor onze authenticatie providers. De inlog URL maakt de API automatisch voor ons aan via create_login_url(). Als we via onze ontwikkelwebserver proberen in te loggen gebruiken we echter niet de OpenID login, dat gebeurt alleen in de productieomgeving. In plaats daarvan zien we een ‘mock’ login, die simuleert wat er bij een echte login gebeurt. Als we op de Login knop drukken zien we inderdaad de tekst ‘Hallo, test@example.com!’:

Afbeelding 1

Prikbord
Laten we overgaan van het hello world idee op wat we in deze workshop echt gaan maken. We hebben al flinke stappen gemaakt, met een werkende ontwikkelomgeving, het begin van een webapp die requests afhandelt, en authenticatie via OpenID aanbieders. De volgende stap is om die gebruikers iets te laten doen, en wat is er dan makkelijker als gebruikers, zodra ze zijn ingelogd, een berichtje achter kunnen laten op de pagina. In feite bouwen we dus een gastenboek, maar dat klinkt erg ouderwets, dus laten we het een prikbord noemen. Zo’n prikbord is een goed voorbeeld voor een workshop, omdat we van alle aspecten van een webapplicatie gebruik moeten maken: authenticatie, authorisatie, data opslag, het opvragen van data, en de user interface.

 

Laten we beginnen met het hernoemen van ‘hallowereld.py’ naar ‘prikbord.py’. Vergeet niet dat je ‘hallowereld.application’ in app.yaml dan dus ook moet veranderen naar ‘prikbord.application’. Vervolgens voegen we een <form> toe aan de pagina die we te zien krijgen als we zijn ingelogd, waar gebruikers hun berichten op kwijtkunnen. Formulieren communiceren normaalgesproken met de webserver via een HTTP POST request, dus die moeten we apart afhandelen, waar we een eigen class voor definieren:

from google.appengine.api import users

import webapp2, cgi

providers = {
    ‘Google’   : ‘https://www.google.com/accounts/o8/id’,
    ‘Yahoo’    : ‘yahoo.com’,
    ‘MyOpenID’ : ‘myopenid.com’,
}

MAIN_PAGE_HTML = “””\
<html>
  <body>
    <p>Hallo, <em>%s</em>! [<a href=”%s”>uitloggen</a>]</p>
    <form action=”/prik” method=”post”>
      <div><textarea name=”content” rows=”3″ cols=”60″></textarea></div>
      <div><input type=”submit” value=”Prik op het prikbord”></div>
    </form>
  </body>
</html>
“””

class MainPage(webapp2.RequestHandler):
    def get(self):
        user = users.get_current_user()
        if user:
            self.response.out.write(MAIN_PAGE_HTML % (
                user.nickname(), users.create_logout_url(self.request.uri)))
        else:
            self.response.out.write(‘Hallo, wereld! Log in via: ‘)
            for name, uri in providers.items():
                self.response.out.write(‘[<a href=”%s”>%s</a>]’ % (
                    users.create_login_url(federated_identity=uri), name))

class PrikBord(webapp2.RequestHandler):
    def post(self):
        self.response.write(‘<html><body>Je bericht:<pre>’)
        self.response.write(cgi.escape(self.request.get(‘content’)))
        self.response.write(‘</pre> [<a href=”/”>terug</a>]</body></html>’)

application = webapp2.WSGIApplication([(‘/’, MainPage), (‘/prik’, PrikBord)], debug=True)

 

Er vallen een aantal zaken op. Allereerst kunnen we duidelijk zien dat we niet zo door kunnen blijven gaan: de HTML code in de MAIN_PAGE_HTML variabele is natuurlijk geen gezicht. Stel dat we een stuk of tien verschillende pagina’s hebben? Daar moeten we natuurlijk templates voor gebruiken, waar we dan ook in de volgende workshop mee aan de slag gaan. Hetzelfde geldt voor de data opslag: de berichten moeten worden opgeslagen en worden weergegeven op de homepage, anders heeft het weinig nut. Voor nu kunnen we in ieder geval echter al duidelijk zien hoe makkelijk het is om een simpele web applicatie te schrijven en wat de basis componenten zijn. Aan de WSGIApplication class geven we een lijst mee met alle classes die bepaalde soorten requests, van GET of POST type, afhandelen door een response terug te schrijven in HTML.

Uitrollen
De app is nog lang niet af, maar laten we hem toch alvast uitrollen om te kijken of het OpenID inloggen werkt en of we ook in de toekomst makkelijk kunnen ‘deployen’. Het is van belang dat we de unieke identificatie van onze app al in app.yaml hebben staan. In ons geval hebben we linmagnl.appspot.com aangevraagd, en staat dus in dat bestand:

 

‘application: linmagnl’

Om onze applicatie naar de appspot URL te uploaden gebruiken we het ‘appcfg.py’ commando, wat ons vraagt in te loggen met ons Google account om vervolgens de meest recente versie van onze code online te zetten:

 

$ google_appengine/appcfg.py update linmagnl
03:55 AM Host: appengine.google.com
03:55 AM Application: linmagnl; version: 1
03:55 AM
Starting update of app: linmagnl, version: 1
03:55 AM Getting current resource limits.
Email: …
Password for …:
03:56 AM Scanning files on local disk.
03:56 AM Cloning 2 application files.
03:56 AM Uploading 2 files and blobs.
03:56 AM Uploaded 2 files and blobs
03:56 AM Compilation starting.
03:56 AM Compilation completed.
03:56 AM Starting deployment.
03:56 AM Checking if deployment succeeded.
03:56 AM Deployment successful.
03:56 AM Checking if updated app version is serving.
03:56 AM Completed update of app: linmagnl, version: 1

 

Als we nu via Google inloggen, zien we inderdaad dat het werkt:


Afbeelding 2

Nadat we hebben op de Accept knop hebben gedrukt, krijgen we inderdaad het formulier te zien:

 


Afbeelding 3

Volgende keer
We hebben nu het fundament gelegd voor onze eigen prikbord webapp in Google App Engine. De volgende keer maken we de app af: we slaan de berichten op, geven ze weer en laten alles er mooi uitzien met gelikte templates.

 

Links

 

cloud.google.com/appengine 
developers.google.com/appengine – documentatie en de SDK
appengine.google.com – je eigen gratis app in de cloud