blastengineを使ってC#でメールを送信する

[blastengine](https://blastengine.jp/)はシンプルに使える開発者向けメールサービスを提供しています。何かと面倒なメール配信をシンプルに、かつ確実に送信先に届くようになります。

今回はblastengineを使ってC#でメール送信を行うまでの流れを解説します。

## ユーザ登録する

blastengineにユーザ登録します。管理画面に入るためのユーザID、パスワードが手に入るので、ログインします（ユーザIDは後で使います）。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/197026/5ca99ca7-9114-4a64-c51e-67ba88dfcb76.png)


## 送信元ドメインのSPFを設定する

送信元として利用するドメイン（自分で持っているもの）の設定をします。これは任意のドメイン管理サービスで設定できますが、TXTレコードに以下のSPFを追加します。

```
txt @ v=spf1 include:spf.besender.jp ~all
```

## API経由で配信する

まずはAPIを使ってHTMLメールを配信する流れを紹介します。今回は標準ライブラリのみで実装します。

### APIキーを取得する

ログイン後、管理画面の右上にある設定メニューに移動します。

![getting-started-6.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/197026/78a4eeb3-4649-149f-8246-44f83d03a0cf.jpeg)


そして設定の中で、APIキーを取得します。

![getting-started-5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/197026/d21fc4e3-36f0-6f5c-23e3-8c675e16f538.png)


### ライブラリを読み込む

まず必要なライブラリを読み込みます。上記の `superagent` に加えて、ハッシュを作る際に利用する `crypto` も読み込みます。

```csharp
using System.Net.Http;
using System.Security.Cryptography;
using System.Text;
using System.Text.Encodings.Web;
using System.Text.Json;
using System.Text.Unicode;

using static System.Net.Mime.MediaTypeNames;
```

### 必要な変数を取得

先ほど取得したAPIキーなど、必要な情報を設定します。

```csharp
string apiUser = "YOUR_USER_ID";
string apiKey = "YOUR_API_KEY";
string fromMail = "info@example.com";
string fromName = "管理者";
string toAddress = "user@example.jp";
string subject = "C#でのメール送信（API版）";
string body = "メールの本文です";
```

### トークンを生成する

APIリクエストするためのトークンを生成します。手順としては次のとおりです。

1. ユーザIDとAPIキーを連結する
2. SHA256のハッシュを生成する
3. ハッシュを全て小文字にする
4. 3の文字列をBASE64エンコードする

実際のコードで言うと、次のようになります。 `token` がトークンです。

```csharp
// 入力文字列をバイト配列に変換
byte[] inputBytes = Encoding.UTF8.GetBytes(apiUser + apiKey);

// SHA256を使用してハッシュ化
SHA256 sha256 = SHA256.Create();
byte[] hashBytes = sha256.ComputeHash(inputBytes);

// ハッシュを16進数表現の小文字文字列に変換
StringBuilder sb = new StringBuilder();
foreach (byte b in hashBytes)
{
    sb.Append(b.ToString("x2"));
}
string lowerCaseHash = sb.ToString();

// 小文字のハッシュをバイト配列に再変換
byte[] hashBytesLower = Encoding.UTF8.GetBytes(lowerCaseHash);

// Base64エンコードして結果を返す
string token = Convert.ToBase64String(hashBytesLower);
```

### APIリクエストする

では実際にメールを送信します。APIのエンドポイントは `https://app.engn.jp/api/v1/deliveries/transaction` になります。生成したトークンは `Authorization` ヘッダーに適用します。fromのemailやtoなど、メールアドレスは利用されるものに書き換えてください。

```csharp
string url = "https://app.engn.jp/api/v1/deliveries/transaction";
// リクエスト内容の生成
var jsonObject = new
{
    to = toAddress,
    from = new
    {
        name = fromName,
        email = fromMail
    },
    encode = "UTF-8",
    subject = subject,
    text_part = body
};
var options = new JsonSerializerOptions
{
    Encoder = JavaScriptEncoder.Create(UnicodeRanges.All),
    WriteIndented = true
};
string requestBody = JsonSerializer.Serialize(jsonObject, options);
```

HTTPリクエストを送信します。ヘッダーに `Authorization` を追加しています。 `Content-Type` は StrinContentの時点で指定されます。

```csharp
HttpClient client = new HttpClient();
HttpContent content = new StringContent(requestBody, Encoding.UTF8, "application/json");
client.DefaultRequestHeaders.Add("Authorization", $@"Bearer {token}");

try
{
    HttpResponseMessage response = await client.PostAsync(url, content);
    string responseBody = await response.Content.ReadAsStringAsync();

    // ステータスコードのチェック（成功しているかどうか）
    if (response.StatusCode != System.Net.HttpStatusCode.Created)
    {
        throw new Exception(responseBody);
    }
    Console.WriteLine(responseBody);
}
catch (HttpRequestException e)
{
    // エラーが発生した場合の処理
    Console.WriteLine("Request error: " + e.Message);
}
```

### 結果

レスポンスを出力します。配信ID（delivery_id）は照会、変更および削除操作に必要です。

```csharp
Console.WriteLine(responseBody);
// { delivery_id: 9 }
```


### 全体のコード

今回のサンプルコードは次のようになります。実装時の参考にしてください。

```csharp
using System.Net.Http;
using System.Security.Cryptography;
using System.Text;
using System.Text.Encodings.Web;
using System.Text.Json;
using System.Text.Unicode;

using static System.Net.Mime.MediaTypeNames;

string apiUser = "YOUR_USER_ID";
string apiKey = "YOUR_API_KEY";
string fromMail = "info@example.com";
string fromName = "管理者";
string toAddress = "user@example.jp";
string subject = "C#でのメール送信（API版）";
string body = "メールの本文です";

// 入力文字列をバイト配列に変換
byte[] inputBytes = Encoding.UTF8.GetBytes(apiUser + apiKey);

// SHA256を使用してハッシュ化
SHA256 sha256 = SHA256.Create();
byte[] hashBytes = sha256.ComputeHash(inputBytes);

// ハッシュを16進数表現の小文字文字列に変換
StringBuilder sb = new StringBuilder();
foreach (byte b in hashBytes)
{
    sb.Append(b.ToString("x2"));
}
string lowerCaseHash = sb.ToString();

// 小文字のハッシュをバイト配列に再変換
byte[] hashBytesLower = Encoding.UTF8.GetBytes(lowerCaseHash);

// Base64エンコードして結果を返す
string token = Convert.ToBase64String(hashBytesLower);

string url = "https://app.engn.jp/api/v1/deliveries/transaction";
// リクエスト内容の生成
var jsonObject = new
{
    to = toAddress,
    from = new
    {
        name = fromName,
        email = fromMail
    },
    encode = "UTF-8",
    subject = subject,
    text_part = body
};
var options = new JsonSerializerOptions
{
    Encoder = JavaScriptEncoder.Create(UnicodeRanges.All),
    WriteIndented = true
};
string requestBody = JsonSerializer.Serialize(jsonObject, options);

HttpClient client = new HttpClient();
HttpContent content = new StringContent(requestBody, Encoding.UTF8, "application/json");
client.DefaultRequestHeaders.Add("Authorization", $@"Bearer {token}");

try
{
    HttpResponseMessage response = await client.PostAsync(url, content);
    string responseBody = await response.Content.ReadAsStringAsync();

    // ステータスコードのチェック（成功しているかどうか）
    if (response.StatusCode != System.Net.HttpStatusCode.Created)
    {
        throw new Exception(responseBody);
    }
    Console.WriteLine(responseBody);
}
catch (HttpRequestException e)
{
    // エラーが発生した場合の処理
    Console.WriteLine("Request error: " + e.Message);
}
```

## SMTPリレーでの配信

次にSMTPリレーを使った方式です。こちらも標準ライブラリだけで送信します。

### SMTPリレー用のパスワードを取得する

SMTPリレーの際に用いるパスワードは、APIキーとは別に取得します。管理画面の設定メニューから取得できます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/197026/6125fd13-24f4-43cf-1ddd-6e4612a9449f.png)


### 変数の設定

配信に必要な変数を設定します。blastengineのSMTPサーバアドレスは `smtp.engn.jp` 固定になります。ポート番号は25/587/2525より選択できます。

```csharp
string apiUser = "USER_ID";
string password = "SMTP_PASSWORD";
string senderMail = "info@example.com";
string recipientMail = "user@example.jp";
string smtpHost = "smtp.engn.jp";
int smtpPort = 587;

string subject = "C#でのメール送信（SMTP版）";
string body = "メールの本文です";
```

### SMTPクライアントの設定

今回は標準ライブラリのSmtpClientを使います。

```csharp
// 標準ライブラリのSmtpClientを使います
System.Net.Mail.SmtpClient sc = new System.Net.Mail.SmtpClient();
sc.Host = smtpHost;
sc.Port = smtpPort;
sc.EnableSsl = true;
```

### 認証設定

SMTPリレーの際には認証が必要です。SMTPサーバアドレス、ユーザID、パスワードを設定します。

```csharp
// SMTPの認証設定です
sc.Credentials = new System.Net.NetworkCredential(apiUser, password);
```

### 配信する

メールの送信処理です。API経由とは異なり、デリバリーIDは取得できませんので注意してください。

```csharp
// メールを送信します
sc.DeliveryMethod = System.Net.Mail.SmtpDeliveryMethod.Network;
sc.Send(senderMail, recipientMail, subject, body);

// 後片付けです
sc.Dispose();
```

### 全体のコード

今回のサンプルコードは次のようになります。実装時の参考にしてください。

```csharp
string apiUser = "USER_ID";
string password = "SMTP_PASSWORD";
string senderMail = "info@example.com";
string recipientMail = "user@example.jp";
string smtpHost = "smtp.engn.jp";
int smtpPort = 587;

string subject = "C#でのメール送信（SMTP版）";
string body = "メールの本文です";

// 標準ライブラリのSmtpClientを使います
System.Net.Mail.SmtpClient sc = new System.Net.Mail.SmtpClient();
sc.Host = smtpHost;
sc.Port = smtpPort;
sc.EnableSsl = true;

// SMTPの認証設定です
sc.Credentials = new System.Net.NetworkCredential(apiUser, password);

// メールを送信します
sc.DeliveryMethod = System.Net.Mail.SmtpDeliveryMethod.Network;
sc.Send(senderMail, recipientMail, subject, body);

// 後片付けです
sc.Dispose();
```

## まとめ

クラウドサービスではSMTPポートが塞がれている場合があるので、そうした時にはAPI経由を利用してください。SMTPリレーを使えば、より信頼性高く、安定した配信が実現できるでしょう。

APIとSMTPリレー、それぞれの要件に合わせて最適な方を選択してください。

[エンジニア向けメール配信システム「ブラストエンジン」](https://blastengine.jp/)