本文說明如何使用 Python 建構 App Engine 應用程式,向使用者傳送附註郵件,要求他們直接在收件匣中確認郵寄清單訂閱項目,並在 Datastore 中收集訂閱項目。
事前準備和專案設定
本指南假設您已安裝 App Engine SDK,並瞭解如何建立、執行及發布 App Engine 專案。
首先,請為專案建立目錄。將應用程式的所有檔案放在這個目錄中。
將下列程式碼複製到名為 app.yaml 的檔案,並將 {{ APPID }} 預留位置替換為專屬的 App Engine 應用程式 ID:
application: {{ APPID }}
version: 1
runtime: python27
api_version: 1
threadsafe: true
handlers:
- url: /.*
script: main.app
libraries:
- name: jinja2
version: latest
在 App Engine 專案資料夾中建立名為 main.py 的檔案,然後複製下列程式碼,設定用於收集及列出訂閱項目,以及傳送註解電子郵件的處理常式:
import webapp2
from emailsender import EmailSender
from subscribe import SubscribeHandler
app = webapp2.WSGIApplication([('/', SubscribeHandler), ('/email', EmailSender)], debug=True)
在電子郵件中加入結構化資料
首先,請傳送電子郵件,要求使用者確認郵寄清單訂閱:
<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>
您可以在電子郵件的 head 中,以其中一種支援的格式 (JSON-LD 或 Microdata) 新增結構化資料,定義電子郵件訊息並新增 OneClickAction。Gmail 支援 OneClickAction,並顯示 UI 元素,讓使用者在收件匣中確認訂閱。
將下列標記複製到名為 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>
微資料
<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="Confirm Subscription"/>
<div itemprop="handler" itemscope itemtype="http://schema.org/HttpActionHandler">
<meta itemprop="url" content="{{ confirm_url }}"/>
<link itemprop="method" href="http://schema.org/HttpRequestMethod/POST"/>
</div>
</div>
<meta itemprop="description" content="Confirm subscription to mailing list XYZ"/>
</div>
<p>
Dear John, please confirm that you wish to be subscribed to the mailing list XYZ.
</p>
</body>
</html>
上述結構化資料說明名為「XYZ」的郵寄清單和 ConfirmAction。動作的處理常式是 HttpActionHandler,會將 POST 要求傳送至 url 屬性中指定的網址。
重要事項:在上述程式碼中,請先將 {{ confirm_url }} 預留位置換成 App Engine 應用程式的網址 (例如 https://mailing-list.appspot.com),再傳送任何電子郵件。下列步驟所述的程式碼會處理這項替換作業。
傳送訂閱要求給使用者
將下列程式碼複製到 App Engine 專案資料夾中名為 emailsender.py 的檔案:
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)
EmailSender 類別需要使用者登入,才能擷取電子郵件地址。接著,系統會從 mail_template.html 載入電子郵件內文,將其中的 confirm_url 預留位置替換為 App Engine 應用程式的根網址 (https://APP-ID.appspot.com),然後將電子郵件傳送給登入的使用者。
收集及列出訂閱項目
將下列程式碼複製到 App Engine 專案資料夾中名為 subscribe.py 的檔案:
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)
SubscribeHandler 類別會處理傳送至應用程式根網址 (https://APP-ID.appspot.com) 的 POST 和 GET 要求。Gmail 會使用 POST 要求插入新訂閱項目,包括對應使用者的 user_id 參數,如下例所示:
https://subscribe.appspot.com/?user_id=123abcd
要求處理常式會檢查是否已定義必要的 user_id,並將訂閱項目儲存在 Datastore 中。
這會將 HTTP 200 回應代碼傳回 Gmail,表示要求成功。如果要求未包含必要欄位,要求處理常式會傳回 HTTP 400 回應代碼,表示要求無效。
對應用程式根網址發出的 GET 要求會列出已收集的訂閱項目。要求處理常式會從 Datastore 擷取所有訂閱項目,並在網頁上顯示這些項目和計數器。
測試應用程式
將應用程式部署至 App Engine,然後前往 https://APP-ID.appspot.com/email (將 APP-ID 替換為您的 App Engine 應用程式 ID),即可傳送註解電子郵件給自己。

部署應用程式並插入訂閱項目後,請前往「https://APP-ID.appspot.com」查看訂閱項目摘要頁面。