tekitoumemo’s diary

C#、.NET系の技術ブログを書いています。みんなの洋楽ランキングを運営しています。

【ASP.NET MVC Core】.NET CoreでSystem.Net.Mail.SmtpClientが使えなくなったのでMimeKitを使う

f:id:tekitoumemo:20180510224948p:plain

みんなの洋楽ランキングを.NET Coreに絶賛移行中でMacBook Airのみで開発しています。.Net Core3.0ではWindows Formも対応するみたいでなかなか最強感出てきましたね!ASP.NET CoreはScalaの1.7倍のパフォーマンスを出すそうです。
www.ageofascent.com

タイトル通り「.NET CoreでSystem.Net.Mail.SmtpClientが使えなくなった」です。

.NET CoreからSystem.Net.Mail.SmtpClientでなくMimeKitが推奨されましたのでそのメモ。結構この記事は多いのですが、デバッグ時にSSL推奨の影響でエラーになっちゃったりするんでその対応を追記します。

MimeKitとは?

github.com
オープンソースのメールライブラリです。公式がこう発表しています。

「SmtpClientとそのタイプのネットワークは設計が不十分であり、代わりに MailKitを使うことを強く推奨する」

という感じです。

MimeKitをインストール

バージョンは以下で確認してくださいGithubの最新は2.02だった。なぜ?
www.nuget.org

以下のコマンドで追加

dotnet add package MailKit --version 2.0.3

csprojに

<PackageReference Include="MailKit" Version="2.0.3" />

を追加して

dotnet restore

でも良いです。

使い方

usingと生成

using MimeKit;
var emailMessage = new MimeMessage ();

送信元とユーザー名を設定

emailMessage.From.Add (new MailboxAddress ("ユーザー名", "送信元"));

送信先の設定

emailMessage.To.Add (new MailboxAddress ( "送信先"));

件名と本文を設定

emailMessage.Subject = "件名";
emailMessage.Body = new TextPart ("plain") { Text = "本文" };

SMTPサーバに接続

var client = new SmtpClient();
await client.ConnectAsync("smtp.test.net", 587, SecureSocketOptions.SslOnConnect);

SMTPサーバ認証IDとパスワード)

await client.AuthenticateAsync("SMTPサーバ認証ID", "パスワード");

メールを送信

await client.SendAsync(emailMessage);

SMTPサーバ接続を切る

await client.DisconnectAsync(true);

処理はこれで終了です。ですが、開発中にSSLにしていないとエラーになります。
エラーになるコードは以下です。

var emailMessage = new MimeMessage ();
//と
await client.ConnectAsync("smtp.test.net", 587, SecureSocketOptions.SslOnConnect);

で以下のエラーが発生します。

SslHandshakeException: An error occurred while attempting to establish an SSL or TLS connection.

SSL接続が出来ないってことですね。なので対策のため、生成前に証明書検証用コールバックを設定します。

ServicePointManager.ServerCertificateValidationCallback +=
                (sender, cert, chain, sslPolicyErrors) => true;
var emailMessage = new MimeMessage ();

SecureSocketOptionsはStartTlsを設定します。StartTlsは受信側がTLS/SSLに対応していれば暗号化して送り、されていなければ平文で送るオプションです。

await client.ConnectAsync("smtp.test.net", 587, SecureSocketOptions.StartTls);

ちなみにGmailは以下のコードにメールアドレスとパスワードを設定すればよいです。

await client.AuthenticateAsync("SMTPサーバ認証ID", "パスワード");

ざっと通しで作ったので確認してみてください。

using System.Linq;
using System.Net;
using System.Threading.Tasks;
using MailKit.Net.Smtp;
using MailKit.Security;
using MimeKit;

namespace Mail {
    public class MailManager {

        public string UserName { get; set; }
        public string PassWord { get; set; }
        public string From { get; set; }
        public string To { get; set; }
        public string Subject { get; set; }
        public string Body { get; set; }
        public string Host { get; set; }
        public int Port { get; set; }

        public async Task SendEmailAsync () {

#if DEBUG
            ServicePointManager.ServerCertificateValidationCallback +=
                (sender, cert, chain, sslPolicyErrors) => true;
#endif
            var emailMessage = new MimeMessage ();

            emailMessage.From.Add (new MailboxAddress (this.UserName, this.From));

            emailMessage.To.Add (new MailboxAddress (this.To));

            emailMessage.Subject = this.Subject;

            emailMessage.Body = new TextPart ("plain") { Text = this.Body };

            using (var client = new SmtpClient ()) {
#if DEBUG
                await client.ConnectAsync (this.Host, this.Port, SecureSocketOptions.StartTls);
#else
                await client.ConnectAsync (this.Host, this.Port, SecureSocketOptions.SslOnConnect);
#endif
                await client.AuthenticateAsync (this.UserName, this.PassWord);
                await client.SendAsync (emailMessage);
                await client.DisconnectAsync (true);
            }
        }
    }
}

最低限必要なものはプロパティで用意しているので、設定すれば動くと思います。

async、awaitが標準になり使い勝手も変わらないので結構楽でよいですね!