Python 適用的 Mail API

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

總覽

在 Python 中,郵件處理功能包含在 google.appengine.api.mail 模組中。這與 Python 2 不同,後者是由 webapp 提供 mail_handlers 模組。您可以使用 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 和 Django 應用程式的範例:

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 類似,Python 2 的 BounceNotification 類別可透過覆寫 webapp 處理常式 BounceNotificationHandler 中的 receive() 方法存取。

在 Python 中,應用程式需要例項化 BounceNotification 物件,視使用的 Python 網路架構而定,建立方式可能有所不同。

網路架構

BounceNotification 物件會以呼叫 post_vars.get(key) 擷取的值初始化。

使用 Python 網路架構 (例如 Flask 或 Django) 時,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': ['bob@example.com', 'alice@example.com']})。所有欄位的預設值都是空字串。這些值會填入 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 中,request.form 型別為 werkzeug.datastructures.MultiDict 會提供 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 中,request.POST 型別的 django.http.QueryDict 會提供 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