Python 適用的 Mail API

本頁面說明如何搭配標準環境的 Python 執行階段使用 Mail API (舊版套裝服務之一)。您的應用程式可以透過 Python 專用的 App Engine 服務 SDK存取套裝組合服務。

總覽

在 Python 中,郵件處理功能包含在 google.appengine.api.mail 模組中。這與 Python 2 不同,在 Python 2 中,mail_handlers 模組是由 webapp 提供。Python 的 Mail API 可用於接收電子郵件和退信通知。

使用 Mail API

當電子郵件以 HTTP POST 要求的內容傳送時,應用程式就會收到郵件。應用程式必須將網址與路徑 /_ah/mail/[ADDRESS] 比對,才能處理傳入的電子郵件。路徑中的 [ADDRESS] 部分通常是含有後置字串 @<Cloud-Project-ID>.appspotmail.com 的電子郵件地址。以這種格式傳送至應用程式的電子郵件會傳送至函式。

Python 不要求應用程式在 app.yaml 檔案中指定處理常式指令碼,因此您可以移除 app.yaml 中的所有 handler 區段。

app.yaml 檔案應保留以下程式碼行:

inbound_services:
- mail
- mail_bounce

傳送郵件

升級至 Python 時,您不需要變更應用程式的設定。傳送郵件的行為、功能和設定說明皆維持不變。詳情請參閱下列指南:

接收郵件

如要接收郵件,您必須匯入 google.appengine.api.mail 模組,並使用 InboundEmailMessage 類別來代表電子郵件。這個類別需要初始化,才能從傳入的 HTTP 要求中擷取電子郵件內容。

在先前的 Python 2 中,應用程式可以透過覆寫 webapp 處理常式 InboundEmailHandler 中的 receive() 方法,存取 InboundEmailMessage 類別。在 Python 中不需要這麼做,而是需要讓應用程式將新物件例項化。

網路架構

使用 Python 網路架構時,InboundEmailMessage 建構函式會擷取 HTTP 要求主體的位元組。在 Python 中建立 InboundEmailMessage 物件的方式有很多種。以下是 Flask 和 Djago 應用程式的範例:

Python 3 (Flask)

在 Flask 中,request.get_data() 會提供要求位元組。

@app.route("/_ah/bounce", methods=["POST"])
def receive_bounce():
    bounce_message = mail.BounceNotification(dict(request.form.lists()))

    # Do something with the message
    print("Bounce original: ", bounce_message.original)
    print("Bounce notification: ", bounce_message.notification)

    return "OK", 200

Python 3 (Django)

在 Django 中,request.body 會提供 HTTP 要求主體的位元組。

def receive_mail(request):
    message = mail.InboundEmailMessage(request.body)

    print(f"Received greeting for {message.to} at {message.date} from {message.sender}")
    for _, payload in message.bodies("text/plain"):
        print(f"Text/plain body: {payload.decode()}")
        break

    return HttpResponse("OK")

如要查看本指南的完整程式碼範例,請前往 GitHub

其他符合 WSGI 的架構

對於其他符合 WSGI 規範的架構,建議您使用與 Flask 和 Django 範例相同的方法建立 InboundEmailMessage。當 HTTP 要求主體的位元組可直接取得時,這個方法就會運作。

沒有網頁架構的 WSGI 應用程式

如果您的應用程式是未使用網頁架構的 WSGI 應用程式,則可能無法直接取得 HTTP 要求主體的位元組。如果 HTTP 要求主體的位元組可直接使用,建議您使用 Python 網路架構。

在 Python 中,為 InboundEmailMessage 定義名為 from_environ 的工廠方法。這個方法是類別方法,會將 WSGI environ 字典做為輸入內容,可用於任何 WSGI 應用程式。

請注意,在下列範例中,environ 會做為輸入內容,以便取得 mail_message

Python 3 (WSGI 應用程式)

def HelloReceiver(environ, start_response):
    if environ["REQUEST_METHOD"] != "POST":
        return ("", http.HTTPStatus.METHOD_NOT_ALLOWED, [("Allow", "POST")])

    message = mail.InboundEmailMessage.from_environ(environ)

    print(f"Received greeting for {message.to} at {message.date} from {message.sender}")
    for content_type, payload in message.bodies("text/plain"):
        print(f"Text/plain body: {payload.decode()}")
        break

    response = http.HTTPStatus.OK
    start_response(f"{response.value} {response.phrase}", [])
    return ["success".encode("utf-8")]

接收退件通知

退件通知是電子郵件系統自動傳送的郵件,用於指出應用程式訊息傳送發生問題。如要處理退回通知,應用程式必須將傳入的網址路徑與 /_ah/bounce 路徑比對。

如同 InboundEmailMessage,您可以透過覆寫 webapp 處理常式 BounceNotificationHandler 中的 receive() 方法,存取 Python 2 的 BounceNotification 類別。

在 Python 中,應用程式需要將 BounceNotification 物件例項化,而這可以透過多種方式建立,具體取決於使用的 Python 網路架構。

網路架構

BounceNotification 物件會使用呼叫 post_vars.get(key) 所擷取的值進行初始化。

使用 Flask 或 Django 等 Python 網路架構時,BounceNotification 建構函式會擷取名為 post_vars 的字典,其中包含表單資料的 POST 要求。如要擷取資料,請在輸入物件上定義 get() 方法。key 是輸入值清單,可供 BounceNotification 讀取及擷取,且可為下列任一值:

original-to, original-cc, original-bcc, original-subject, original-text, notification-from, notification-to, notification-cc, notification-bcc, notification-subject, notification-text, raw-message

在大多數的網頁架構中,這類資料會以多重字典的形式出現在要求物件中。這些類型的大部分都可以轉換為以字串做為索引的字典。

raw-message 以外的所有鍵,值可以是任何內容。通常,值會是單一值 (例如字串),或是值清單 (例如 {'to': ['[email protected]', '[email protected]']})。所有欄位的預設值為空白字串。這些值會填入 originalnotification 屬性。

對於 raw-message 鍵,值必須是 EmailMessage 建構函式的有效輸入內容。可以是單一值或單一值清單。raw-message 鍵用於初始化物件的 original_raw_message 屬性。

Python 2 (webapp2)

class LogBounceHandler(BounceNotificationHandler):
    def receive(self, bounce_message):
        logging.info('Received bounce post ... [%s]', self.request)
        logging.info('Bounce original: %s', bounce_message.original)
        logging.info('Bounce notification: %s', bounce_message.notification)

Python 3 (Flask)

在 Flask 中,werkzeug.datastructures.MultiDict 類型的 request.form 會提供 POST 變數。不過,此類型的 get() 方法只會傳回一個值,即使鍵有多個值也不例外。

如要取得與鍵相對應的所有值,應用程式需要呼叫 dict(request.form.lists()),這會產生一個字典,其中每個值都是清單。

@app.route("/_ah/bounce", methods=["POST"])
def receive_bounce():
    bounce_message = mail.BounceNotification(dict(request.form.lists()))

    # Do something with the message
    print("Bounce original: ", bounce_message.original)
    print("Bounce notification: ", bounce_message.notification)

    return "OK", 200

Python 3 (Django)

在 Django 中,django.http.QueryDict 類型的 request.POST 會提供 POST 變數。不過,此類型的 get() 方法只會傳回一個值,即使鍵有多個值也不例外。

如要取得與鍵相對應的所有值,應用程式需要呼叫 dict(request.POST.lists()),這會產生一個字典,其中每個值都是清單。

def receive_bounce(request):
    bounce_message = mail.BounceNotification(dict(request.POST.lists()))

    # Do something with the message
    print(f"Bounce original: {bounce_message.original}")
    print(f"Bounce notification: {bounce_message.notification}")

    return HttpResponse("OK")

沒有網頁架構的 WSGI 應用程式

如果您的應用程式是未使用網路架構的 WSGI 應用程式,則可能無法在字典中直接使用 HTTP POST 要求的表單變數。

在 Python 中,系統會為 BounceNotification 定義名為 from_environ 的工廠方法。這個方法是類別方法,會將 WSGI environ 字典做為輸入內容,可用於任何 WSGI 應用程式。

請注意,在下列範例中,environ 會做為輸入內容,以取得 bounce_message

Python 3

def BounceReceiver(environ, start_response):
    if environ["REQUEST_METHOD"] != "POST":
        return ("", http.HTTPStatus.METHOD_NOT_ALLOWED, [("Allow", "POST")])

    bounce_message = mail.BounceNotification.from_environ(environ)

    # Do something with the message
    print("Bounce original: ", bounce_message.original)
    print("Bounce notification: ", bounce_message.notification)

    # Return suitable response
    response = http.HTTPStatus.OK
    start_response(f"{response.value} {response.phrase}", [])
    return ["success".encode("utf-8")]

程式碼範例

如要查看本指南的完整程式碼範例,請前往 GitHub