Ví dụ toàn diện

Bài viết này trình bày cách xây dựng một ứng dụng App Engine bằng Python để gửi email có chú thích đến 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 đăng ký 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 App Engine SDK và biết cách tạo, chạy và xuất bản 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 của ứng dụng vào thư mục này.

Sao chép mã sau vào tệp có tên 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 của bạn, rồi sao chép mã sau để thiết lập các 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 bằng 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 Microdata) 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 việc đăng ký từ hộp thư đến.

Sao chép mã đánh dấu sau vào tệp có tên 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 thao tác là một HttpActionHandler gửi 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 tệp có tên 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 đó, trình giữ chỗ này sẽ 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à tự gửi email đến người dùng hiện đã đăng nhập.

Thu thập và liệt kê gói thuê bao

Sao chép mã sau vào tệp có tên 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)

Thông số SubscriptionHandlerclass 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` 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ỉ kiểm tra để đảm bảo rằng user_id cần thiết đã được xác định, sau đó lưu trữ gói thuê bao trong Datastore. Điều này dẫn đến việc một 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 có trường bắt buộc, trình xử lý yêu cầu sẽ trả về một mã phản hồi HTTP 400 để báo hiệu yêu cầu không hợp lệ.

Các yêu cầu GET đối với 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 tìm nạp tất cả các gói thuê bao từ Datastore rồi 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 cho App Engine và truy cập https://APP-ID.appspot.com/email (thay thế APP-ID bằng id ứng dụng App Engine của bạn) để gửi email có chú thích cho chính bạn.

Thao tác trong Gmail

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 để tải trang tóm tắt các gói thuê bao