Gmail API 是一個 RESTful API,主要用來管理 Gmail 信箱與發送郵件,而要通過認證的方法,就是使用 Google 的 OAuth 來進行驗證與授權,若你要用 .NET 來開發 Google API 應用,可以說幾乎找不到完整的文件與範例程式可供參考,所以上手確實有點難度。今天我就整理一下今天的研究心得,記錄一些常見的程式寫法。
若為主控台應用程式該如何取得 OAuth 授權並發信
由於 OAuth 2.0 的 Authorization Code Flow 是一個需要人工介入的流程,例如你想用 abc@gmail.com
這個信箱發信,就要請 abc@gmail.com
這個帳號進行授權,讓你得到 Access Token 之後,才能代表 abc@gmail.com
這個帳號,拿著最終的 Access Token 來發信。但是 Console (主控台應用程式) 類型的應用程式並沒有瀏覽器可以跟使用者互動,所以解決方法就是另開瀏覽器來進行 OAuth 授權流程。
-
先到 Google Cloud console 網站初始化一些設定
主要是建立專案、啟用 Gmail API 服務、建立 OAuth 2.0 Client ID 憑證(請記得要選取 Desktop
或 Installed application
類型)、下載 client_secret.json
檔案 (裡面包含 Client Secrets 資訊)。
-
安裝一些 NuGet 套件
Install-Package MailKit
Install-Package Google.Apis.Auth
Install-Package Google.Apis.Gmail.v1
-
透過 GoogleWebAuthorizationBroker 進行驗證
這個 GoogleWebAuthorizationBroker.AuthorizeAsync()
會協助使用者進行驗證,並回傳 UserCredential 物件,會在第一次執行時全自動替用戶端開啟一個瀏覽器,幫助使用者進行 OAuth 2.0 的 Authorization Code Flow!
這個 GoogleWebAuthorizationBroker.AuthorizeAsync()
其實背後做了很多事,他會先在本機 Listen 一個 Port 專門用來當成 OAuth 的 Redirect URI 使用,好讓 OAuth 授權流程都完成後,自動取回 Access Token 與相關資訊,然後再關閉這個 Port 的使用。因此,請千萬不要在 Web 應用程式中使用這個 API 喔,你只能用在 Console 或 Windows Application 之類的應用上,也就是 Google 所稱呼的 Installed application!
using Google.Apis.Auth.OAuth2;
using Google.Apis.Gmail.v1;
using Google.Apis.Util.Store;
using Google.Apis.Services;
string[] scopes = { GmailService.Scope.GmailSend };
UserCredential credential;
using var stream = new FileStream("client_secret.json", FileMode.Open, FileAccess.Read);
var credentialsPath = ".credentials";
credential = await GoogleWebAuthorizationBroker.AuthorizeAsync(
GoogleClientSecrets.FromStream(stream).Secrets,
scopes,
"user",
CancellationToken.None,
new FileDataStore(credentialsPath, true));
if (credential.Token.IsExpired(Google.Apis.Util.SystemClock.Default))
{
if (!await credential.RefreshTokenAsync(CancellationToken.None))
{
throw new Exception("Access Token 已經過期,而且執行 Refresh Token 作業失敗!");
}
}
Console.WriteLine("Credential file saved to: " + credentialsPath);
上述程式碼的目的在於取得一個 UserCredential 型別的 credential
物件!
-
準備郵件內容
這裡我使用 MailKit 套件來準備郵件內容,程式碼如下:
var fromName = "寄件者名稱";
var fromEmail = "abc@gmail.com";
var toName = "收件者名稱";
var toEmail = "target@gmail.com";
var subject = "這是一封測試郵件";
var body = @"這是一封測試郵件,請勿回覆。";
var message = new MimeMessage();
message.From.Add(new MailboxAddress(fromName, fromEmail));
message.To.Add(new MailboxAddress(toName, toEmail));
message.Subject = subject;
var builder = new BodyBuilder();
builder.TextBody = body;
message.Body = builder.ToMessageBody();
-
準備 RFC2822 格式的郵件內容
我們可以透過 MimeMessage
型別下的 WriteTo
方法將內容先寫入到 MemoryStream 之中,再透過 Encoding.UTF8.GetString()
取得完整的內容。
using var mem = new MemoryStream();
message.WriteTo(mem);
var rfc2822 = Encoding.UTF8.GetString(mem.ToArray());
-
準備一個 Google.Apis.Gmail.v1.Data.Message
物件
其實 Gmail API 有點難用,他必須要你自己準備好 RFC2822 格式的郵件內容,然後用 Base64 編碼後才能送出郵件!
string Base64UrlEncode(string input)
{
var inputBytes = System.Text.Encoding.UTF8.GetBytes(input);
return Convert.ToBase64String(inputBytes).Replace("+", "-").Replace("/", "_").Replace("=", "");
}
var msg = new Google.Apis.Gmail.v1.Data.Message();
msg.Raw = Base64UrlEncode(rfc2822); // 一定要用 Base64 編碼過
-
建立一個 Gmail API 服務物件 (即 GmailService
實例)
這裡要帶入 credential
物件到 HttpClientInitializer
屬性中,他會在呼叫 RESTful API 時自動加入必要的 client_id
或 client_secret
資訊:
var service = new GmailService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = "Gmail Sender",
});
-
透過 Gmail API 發送郵件
// 這裡的 "me" 代表著通過 OAuth 驗證的那個使用者
service.Users.Messages.Send(msg, "me").Execute();
若為 ASP.NET Core 應用程式該如何取得 OAuth 授權並發信
-
先到 Google Cloud console 網站初始化一些設定
主要是建立專案、啟用 Gmail API 服務、建立 OAuth 2.0 Client ID 憑證(請記得要選取 Web application
類型)、下載 client_secret.json
檔案 (裡面包含 Client Secrets 資訊)。
-
透過標準的 OAuth 2.0 流程,在 redirect_uri
端點取得 Access Token
與 Refresh Token
-
發信之前要先取得一個 UserCredential 物件
string[] scopes = { GmailService.Scope.GmailSend };
var credentialsPath = ".credentials";
var access_token = "...";
var refresh_token = "...";
var jsonCredentials = Path.Combine(Path.GetDirectoryName(Util.CurrentQueryPath), "client_secret.json");
using var stream = new FileStream(jsonCredentials, FileMode.Open, FileAccess.Read);
var flow = new GoogleAuthorizationCodeFlow(new GoogleAuthorizationCodeFlow.Initializer
{
ClientSecrets = GoogleClientSecrets.FromStream(stream).Secrets,
Scopes = scopes,
DataStore = new FileDataStore(credentialsPath)
});
var myToken = new TokenResponse
{
AccessToken = access_token,
RefreshToken = refresh_token
};
var credential = new UserCredential(flow, Environment.UserName, myToken);
-
準備郵件內容
這裡我使用 MailKit 套件來準備郵件內容,程式碼如下:
var fromName = "寄件者名稱";
var fromEmail = "abc@gmail.com";
var toName = "收件者名稱";
var toEmail = "target@gmail.com";
var subject = "這是一封測試郵件";
var body = @"這是一封測試郵件,請勿回覆。";
var message = new MimeMessage();
message.From.Add(new MailboxAddress(fromName, fromEmail));
message.To.Add(new MailboxAddress(toName, toEmail));
message.Subject = subject;
var builder = new BodyBuilder();
builder.TextBody = body;
message.Body = builder.ToMessageBody();
-
準備 RFC2822 格式的郵件內容
我們可以透過 MimeMessage
型別下的 WriteTo
方法將內容先寫入到 MemoryStream 之中,再透過 Encoding.UTF8.GetString()
取得完整的內容。
using var mem = new MemoryStream();
message.WriteTo(mem);
var rfc2822 = Encoding.UTF8.GetString(mem.ToArray());
-
準備一個 Google.Apis.Gmail.v1.Data.Message
物件
其實 Gmail API 有點難用,他必須要你自己準備好 RFC2822 格式的郵件內容,然後用 Base64 編碼後才能送出郵件!
string Base64UrlEncode(string input)
{
var inputBytes = System.Text.Encoding.UTF8.GetBytes(input);
return Convert.ToBase64String(inputBytes).Replace("+", "-").Replace("/", "_").Replace("=", "");
}
var msg = new Google.Apis.Gmail.v1.Data.Message();
msg.Raw = Base64UrlEncode(rfc2822); // 一定要用 Base64 編碼過
-
建立一個 Gmail API 服務物件 (即 GmailService
實例)
這裡要帶入 credential
物件到 HttpClientInitializer
屬性中,他會在呼叫 RESTful API 時自動加入必要的 client_id
或 client_secret
資訊:
var service = new GmailService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = "Gmail Sender",
});
-
建立一個 GmailService
實例
var service = new GmailService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = "My App",
});
-
透過 Gmail API 發送郵件
// 這裡的 "me" 代表著通過 OAuth 驗證的那個使用者
service.Users.Messages.Send(msg, "me").Execute();
相關連結
- Google for Developers
- 範例程式
- MailKit
- 相關文章