Esempio end-to-end

Questo articolo illustra come creare un'app App Engine in Python che invii email annotate agli utenti chiedendo di confermare l'iscrizione 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 che sappia come creare, eseguire e pubblicare progetti App Engine.

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

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

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 progetto App Engine e copia il codice seguente per configurare i gestori per la raccolta e l'elenco degli abbonamenti, 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)

Aggiungere dati strutturati all'email.

Iniziamo con un'email molto semplice in cui viene chiesto 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 Microdata) alla head dell'email per definire il ristorante e aggiungere un elemento OneClickAction. Gmail supporta OneClickAction e mostra agli utenti una UI specifica per consentire loro di confermare l'iscrizione 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 in alto descrivono una mailing list chiamata "XYZ" e un ConfirmAction. Il gestore dell'azione è un HttpActionHandler che invia richieste POST all'URL specificato nella proprietà url.

Invio di richieste di abbonamento agli utenti

Copia il codice seguente 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)

La classe EmailSender richiede che l'utente abbia eseguito l'accesso in modo da poter recuperare l'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'applicazione App Engine (https://APP-ID.appspot.com) e invia l'email all'utente che ha eseguito l'accesso come se stesso.

Raccogliere e creare un elenco degli abbonamenti

Copia il codice seguente 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 SubscribeGestoriclass 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. Ciò determina l'invio del codice di risposta HTTP 200 a Gmail per segnalare la richiesta andata a buon fine. Se la richiesta non include il campo obbligatorio, il gestore di richieste restituirà un codice di risposta HTTP 400, segnalando la richiesta non valida.

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

Test dell'app

Esegui il deployment dell'app in App Engine e visita https://APP-ID.appspot.com/email (sostituisci APP-ID con il tuo ID app di App Engine) per inviare l'email con annotazioni.

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