前陣子在企業授課 OAuth 2.0 的時候,我原本想現場示範走 Google 的 OAuth 2.0 流程,並且在取得 Access Token 之後怎樣透過原始的 REST API 發信。原本以為這是個很簡單的任務,誰知道膝蓋中了一箭,拿 Token 都很簡單,唯讀「發信」的困難度超出了我的想像,原因無他,就是 Google 的參考文件實在是寫太爛了。這篇文章我打算跟大家說個官方文件沒寫的資訊,教大家怎樣順利透過 Gmail API 發出郵件。
這邊我先跳過 OAuth 2.0 的流程,假設大家已經取得 Access Token 的前提下,該如何成功發信。若想學習 OAuth 2.0 授權協定,我預計在 2025 上半年開辦一堂新課,好好的幫大家梳理一下 OAuth 2.0 協定。
TL;DR
我直接顯示能發信成功完整送出 HTTP 要求並能成功發信的完整封包內容給大家看,明眼人應該一看就懂:
POST https://gmail.googleapis.com/upload/gmail/v1/users/username@gmail.com/messages/send
Authorization: Bearer ya29.a0ARW5m77F24cmI0AeVdecSp0Hk77zYW3ktfo6o3Khcb7zAIAgSixM3XIQEf_9FS6ITz5cQI5gqoU8Th5XCHpFuSLm0ixbbV7DMYAzwoRwtPIv3cwp1PzPIrkeEZJ-hamOIDUaSRwmOL4gB69xJa3w2bi2K6YHr6oYAq-dtiTHaCgYKAb0SARESFQHGX2Miv7cgoQaUd6pJBYow-fQFOw0175
Content-Type: message/rfc822
From: "Will from Gmail" <username@gmail.com>
To: user@example.com
Subject: Hello World
MIME-Version: 1.0
Content-Type: text/html; charset=utf-8
這是一封測試信件
這份 HTTP 封包,比較特別的地方有:
-
Content-Type
必須使用 message/rfc822
內容類型。
這是 RFC822 規範的格式,大部分人應該不會特別去學這種格式。
-
整個 HTTP 的要求主體 (Request Body) 並非 JSON 格式,而是你必須要瞭解 RFC822 規範才知道怎麼寫。
我們從結果出發,整件事看起來當然是非常簡單。但你要知道這樣寫,本身就必須經歷一連串鬼打牆過程,原因就是 Google 文件完全沒有提及。
鬼打牆的過程
OAuth 2.0 的概念非常簡單,說穿了就是要規範授權的流程,其最終的產出物就是讓用戶端拿到 Access Token 而已。但是要能深入理解 OAuth 2.0 所有授權流程,要讓學員能夠全盤瞭解,真的不是非常容易,所以我需要花上一整天的時間讓學員理解全貌。
我們拿到 Access Token 之後,理論上透過 RFC 6750: OAuth 2.0 Bearer Token Usage 的方式將 Access Token 加入到 Authorization
標頭即可呼叫資源伺服器 (Resource Server)。
所以,當我想透過 Gmail API 發信,在既有的 OAuth 2.0 架構下,開發人員應該要能想到以下步驟:
- 先決定你想呼叫哪個 API
- 再查詢你想呼叫的 API 需要哪些權限 (
scope
)
- 想辦法完成 OAuth 2.0 授權流程並取得 Access Token
- 呼叫你想呼叫的 API 並帶入 Access Token
是的,就上述四步驟而已。相信我,雖然 OAuth 2.0 不是很好學,但最難的有時候不一定是在 OAuth 的環節!😅
因為我已經完成了上述前 3 個步驟,第四個步驟不就查一下文件就可以了嗎?
沒錯,你看看 Gmail API 文件,你能找到主要發信的 REST Resource: v1.users.messages 相關說明。
我真的不相信有人類第一次用的時候看的懂啊!😒
雖然 Google 還是有一份 Sending Email 文件,很詳盡的提供了範例,但是只有提供 Java 與 Python 程式語言的範例而已。這兩個範例主要使用了 Google 開發的 Google APIs 套件,使用上並不複雜,但是 C# 怎麼辦?沒說!要透過 curl 搭配 bash 呼叫怎麼辦?沒說!我覺得這並不是一份開發人員友善(Developer Friendly)的文件!連 REST API 文件都沒有!差評!🔥
最後,我還是要靠 Felo Search 幫我到處上網找答案!
但是找到的答案也不正確,網路上的資料依然很亂,還需要靠 AI 進行「深度推理」才能理解怎樣處理,我最常遇到的錯誤訊息是:
Error 400:Media type 'application/json' is not supported.
簡單來說,Gmail API 根本不支援 application/json
這種內容類型,但 Google 的 Gmail API 文件都用 JSON 呈現,會讓人誤以為只能用 JSON 來呼叫,這文件完全誤導了開發人員理解的方向。
最後我才理解到,要求主體 (Request Body) 格式是錯誤的,應該用 RFC822 格式才對吧?所以我在 Felo Search 追問,而且還用不同的「提示」來問,這才得到正確的解答:
我得到以下錯誤:
Error 400:Media type 'application/json' is not supported.
但是目前 curl 的 -d 似乎還是傳 JSON 格式,應該調整成什麼內容才對?
最終我得到以下答案:
curl -X POST \
-H "Authorization: Bearer <YOUR_ACCESS_TOKEN>" \
-H "Content-Type: message/rfc822" \
--data-raw "From: sender@example.com
To: recipient@example.com
Subject: Test Email
Content-Type: text/plain
This is the email body." \
"https://gmail.googleapis.com/gmail/v1/users/me/messages/send"
如此一來,我終於可以成功發信了!👍
相關連結