Bài viết này trình bày cách tạo một ứng dụng App Engine bằng Python để gửi email có chú thích cho người dùng yêu cầu xác nhận đăng ký danh sách gửi thư trực tiếp từ hộp thư đến của họ và thu thập các gói thuê bao trong Datastore.
Điều kiện tiên quyết và cách thiết lập dự án
Hướng dẫn này giả định rằng bạn đã cài đặt SDK App Engine và biết cách tạo, chạy và phát hành các dự án App Engine.
Trước tiên, hãy tạo một thư mục cho dự án của bạn. Đặt tất cả các tệp cho ứng dụng của bạn vào thư mục này.
Sao chép mã sau vào một tệp có tên là app.yaml và thay thế phần giữ chỗ {{ APPID }} bằng mã ứng dụng App Engine duy nhất của bạn:
application: {{ APPID }}
version: 1
runtime: python27
api_version: 1
threadsafe: true
handlers:
- url: /.*
script: main.app
libraries:
- name: jinja2
version: latest
Tạo một tệp có tên là main.py trong thư mục dự án App Engine rồi sao chép mã sau để thiết lập trình xử lý cho việc thu thập và liệt kê các gói thuê bao, cũng như để gửi email có chú thích:
import webapp2
from emailsender import EmailSender
from subscribe import SubscribeHandler
app = webapp2.WSGIApplication([('/', SubscribeHandler), ('/email', EmailSender)], debug=True)
Thêm dữ liệu có cấu trúc vào email
Hãy bắt đầu với một email rất đơn giản yêu cầu người dùng xác nhận đăng ký danh sách gửi thư:
<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>
Bạn có thể thêm dữ liệu có cấu trúc ở một trong các định dạng được hỗ trợ (JSON-LD hoặc Vi dữ liệu) vào head của email để xác định nhà hàng và thêm OneClickAction. Gmail hỗ trợ OneClickAction và hiển thị một giao diện người dùng cụ thể cho người dùng để cho phép họ xác nhận gói thuê bao từ hộp thư đến.
Sao chép mã đánh dấu sau vào một tệp có tên là 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>
Vi dữ liệu
<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>
Dữ liệu có cấu trúc ở trên mô tả một danh sách gửi thư có tên là "XYZ" và một ConfirmAction. Trình xử lý cho hành động này là HttpActionHandler gửi các yêu cầu POST đến URL được chỉ định trong thuộc tính url.
Gửi yêu cầu đăng ký cho người dùng
Sao chép mã sau vào một tệp có tên là emailsender.py trong thư mục dự án 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)
Lớp EmailSender yêu cầu người dùng phải đăng nhập để có thể truy xuất địa chỉ email của họ. Sau đó, lớp này tải nội dung email từ mail_template.html, thay thế phần giữ chỗ confirm_url trong đó bằng URL gốc của ứng dụng App Engine (https://APP-ID.appspot.com) và gửi email cho người dùng hiện đang đăng nhập dưới danh tính của chính họ.
Thu thập và liệt kê các gói thuê bao
Sao chép mã sau vào một tệp có tên là subscribe.py trong thư mục dự án 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)
Lớp 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` tham số tương ứng với người dùng, như trong ví dụ sau:
https://subscribe.appspot.com/?user_id=123abcd
Trình xử lý yêu cầu chỉ cần kiểm tra để đảm bảo rằng user_id bắt buộc được xác định, sau đó lưu trữ gói thuê bao trong Datastore. Điều này dẫn đến việc mã phản hồi HTTP 200 được gửi lại cho Gmail để báo hiệu yêu cầu thành công. Trong trường hợp yêu cầu không bao gồm trường bắt buộc, trình xử lý yêu cầu sẽ trả về mã phản hồi HTTP 400, báo hiệu yêu cầu không hợp lệ.
Yêu cầu GET đến URL gốc của ứng dụng được dùng để liệt kê các gói thuê bao đã được thu thập. Trước tiên, trình xử lý yêu cầu sẽ tìm nạp tất cả các gói thuê bao từ Datastore, sau đó in các gói thuê bao đó trên trang, cùng với một bộ đếm đơn giản.
Kiểm thử ứng dụng
Triển khai ứng dụng của bạn lên App Engine và truy cập vào https://APP-ID.appspot.com/email (thay thế APP-ID bằng mã ứng dụng App Engine của bạn) để gửi email có chú thích cho chính bạn.

Sau khi triển khai ứng dụng và chèn một số gói thuê bao, hãy truy cập vào ứng dụng của bạn tại https://APP-ID.appspot.com để xem trang tóm tắt các gói thuê bao