Esempio end-to-end

Questo articolo mostra come creare un'app App Engine in Python che invii email annotate agli utenti chiedendo di confermare l'abbonamento a una mailing list direttamente dalla loro posta in arrivo e raccolga gli abbonamenti in Datastore.

Prerequisiti e configurazione del progetto

Questa guida presuppone che tu abbia già installato l'SDK App Engine e sai come creare, eseguire e pubblicare progetti App Engine.

Per prima cosa, crea una directory per il tuo progetto. Inserisci tutti i file dell'applicazione in questa directory.

Copia il seguente codice in un file denominato app.yaml e sostituisci il segnaposto {{ APPID }} con il tuo ID app App Engine univoco:

application: {{ APPID }}
version: 1
runtime: python27
api_version: 1
threadsafe: true

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

libraries:
- name: jinja2
  version: latest

Crea un file denominato main.py nella cartella del tuo progetto App Engine e copia il codice seguente per configurare i gestori per la raccolta e l'elenco delle sottoscrizioni, nonché per l'invio di email annotate:

import webapp2

from emailsender import EmailSender
from subscribe import SubscribeHandler

app = webapp2.WSGIApplication([('/', SubscribeHandler), ('/email', EmailSender)], debug=True)

Aggiunta di dati strutturati all'email

Iniziamo con un'email molto semplice in cui si chiede all'utente di confermare l'iscrizione a una mailing list:

<html>
  <head>
    <title>Please confirm your subscription to Mailing-List XYZ?</title>
  </head>
  <body>
    <p>
      Dear John, please confirm that you wish to be subscribed to the
      mailing list XYZ
    </p>
  </body>
</html>

Puoi aggiungere dati strutturati in uno dei formati supportati (JSON-LD o Microdati) alla head dell'email per definire il ristorante e aggiungere un OneClickAction. Gmail supporta OneClickAction e mostra agli utenti una UI specifica per consentire loro di confermare l'abbonamento dalla posta in arrivo.

Copia il seguente markup in un file denominato mail_template.html:

JSON-LD

<html>
  <head>
  <title>Please confirm your subscription to Mailing-List XYZ?</title>
  </head>
  <body>
    <script type="application/ld+json">
    {
      "@context": "http://schema.org",
      "@type": "EmailMessage",
      "potentialAction": {
        "@type": "ConfirmAction",
        "name": "Confirm Subscription",
        "handler": {
          "@type": "HttpActionHandler",
          "url": "{{ confirm_url }}",
          "method": "http://schema.org/HttpRequestMethod/POST",
        }
      },
      "description": "Confirm subscription to mailing list XYZ"
    }
    </script>
    <p>
      Dear John, please confirm that you wish to be subscribed to the mailing list XYZ.
    </p>
  </body>
</html>

Microdati

<html>
  <head>
    <title>Please confirm your subscription to Mailing-List XYZ?</title>
  </head>
  <body>
    <div itemscope itemtype="http://schema.org/EmailMessage">
      <div itemprop="potentialAction" itemscope itemtype="http://schema.org/ConfirmAction">
        <meta itemprop="name" content="Approve Expense"/>
        <div itemprop="handler" itemscope itemtype="http://schema.org/HttpActionHandler">
          <link itemprop="url" href="https://myexpenses.com/approve?expenseId=abc123"/>
          <meta itemprop="url" content="{{ confirm_url }}"/>
          <link itemprop="method" href="http://schema.org/HttpRequestMethod/POST"/>
        </div>
      </div>
      <meta itemprop="description" content="Approval request for John's $10.13 expense for office supplies"/>
    </div>
    <p>
      Dear John, please confirm that you wish to be subscribed to the mailing list XYZ.
    </p>
  </body>
</html>

I dati strutturati riportati sopra descrivono una mailing list denominata "XYZ" e un elemento ConfirmAction. Il gestore dell'azione è un HttpActionHandler che invia richieste POST all'URL specificato nella proprietà url.

Invio di richieste di iscrizione agli utenti.

Copia il seguente codice in un file denominato emailsender.py nella cartella del progetto App Engine:

import jinja2
import os
import webapp2

from google.appengine.api import mail
from google.appengine.api import users

from urlparse import urlparse

class EmailSender(webapp2.RequestHandler):

  def get(self):
    # require users to be logged in to send emails
    user = users.get_current_user()
    if not user:
      self.redirect(users.create_login_url(self.request.uri))
      return

    email = user.email()

    # The confirm url corresponds to the App Engine app url
    pr = urlparse(self.request.url)
    confirm_url = '%s://%s?user=%s' % (pr.scheme, pr.netloc, user.user_id())

    # load the email template and replace the placeholder with the confirm url
    jinja_environment = jinja2.Environment(
        loader=jinja2.FileSystemLoader(os.path.dirname(__file__)))
    template = jinja_environment.get_template('mail_template.html')
    email_body = template.render({'confirm_url': confirm_url})

    message = mail.EmailMessage(
        sender = email,
        to = email,
        subject = 'Please confirm your subscription to Mailing-List XYZ',
        html = email_body)

    try:
      message.send()
      self.response.write('OK')
    except:
      self.error(500)

Il corso EmailSender richiede che l'utente abbia eseguito l'accesso per poter recuperare il suo indirizzo email. Quindi, carica il corpo dell'email da mail_template.html, sostituisce il segnaposto confirm_url al suo interno con l'URL principale dell'app App Engine (https://APP-ID.appspot.com) e invia l'email all'utente che ha eseguito l'accesso con i suoi stessi dati.

Raccolta e creazione di un elenco di iscrizioni

Copia il seguente codice in un file denominato subscribe.py nella cartella del progetto App Engine:

import webapp2

from emailsender import EmailSender
from google.appengine.ext import db


class SubscribeHandler(webapp2.RequestHandler):

  def post(self):
    user_id = self.request.get('user')

    # insert the subscription into the Datastore
    subscription = Subscription(user_id=user_id)
    subscription.put()

  def get(self):
    # retrieve up to 1000 subscriptions from the Datastore
    subscriptions = Subscription.all().fetch(1000)

    if not subscriptions:
      self.response.write('No subscriptions')
      return

    count = len(subscriptions)

    for s in subscriptions:
      self.response.write('%s subscribed<br/>' % (s.user_id))

    self.response.write('<br/>')
    self.response.write('%d subscriptions.' % (count))


class Subscription(db.Model):
    user_id = db.TextProperty(required=True)

Il parametro SubscribeHandlerclass listens to bothPOSTandGETrequests sent to the app root url (https://APP-ID.appspot.com).POSTrequests are used by Gmail to insert new subscriptions including theuser_id" che corrisponde all'utente, come nell'esempio seguente:

https://subscribe.appspot.com/?user_id=123abcd

Il gestore delle richieste verifica semplicemente che lo user_id richiesto sia definito e poi archivia l'abbonamento nel Datastore. In questo modo viene restituito un codice di risposta HTTP 200 a Gmail per segnalare che la richiesta è andata a buon fine. Nel caso in cui la richiesta non includa il campo obbligatorio, il gestore delle richieste restituirà un codice di risposta HTTP 400, che segnala la richiesta non valida.

Le richieste GET all'URL principale dell'app vengono utilizzate per elencare le sottoscrizioni raccolte. Il gestore delle richieste recupera prima tutte le sottoscrizioni da Datastore e poi le stampa nella pagina, insieme a un semplice contatore.

Test dell'app

Esegui il deployment della tua applicazione in App Engine e visita https://APP-ID.appspot.com/email (sostituisci APP-ID con il tuo ID applicazione App Engine) per inviare l'email annotata a te stesso.

Azioni in Gmail

Dopo aver eseguito il deployment dell'app e aver inserito alcuni abbonamenti, visita l'app all'indirizzo https://APP-ID.appspot.com per visualizzare una pagina di riepilogo degli abbonamenti