ASP.NETでapplication/x-www-form-urlencodedのメール送信ステータスWebhookを処理する

Customers Mail CloudのWebhookは2種類あります。

  1. メール受信時
  2. メール送信時

メール送信時は、送信したメールに対してステータスが変わったタイミングで通知が送られるものです。

その際、 application/json を指定しない設定ができます。この時のデータがどうなっているのか紹介します。

<!—more—>

受け取るWebhookの設定

管理画面にて、受け取るWebhookを設定できます。設定は以下が用意されています。

  • Bounces
    • bounced(エラーメールを受け取る)
  • Deliveries
    • queued(キューに入ったタイミング)
    • succeeded(送信完了)
    • failed(送信失敗)
    • deferred(送信延期)

この中で application/json を指定できます。本記事では指定しなかった場合を想定しています。

ASP.NETによる実装

今回はASP.NETでWebhookを受け取った場合について解説します。 application/json の場合、ASP.NETでは定義したクラスにJSON全体を変換して受け取れます。

それに対して、今回の application/json を外した場合には、 event 以下が文字列になって受け取られます。つまり以下のような形です。

{
  server_composition: 'sandbox',
  event_type: 'deliveries',
  event: '{"deliveries":[{"reason":"","sourceIp":"100.100.100.1","returnPath":"info@return.sandbox.smtps.jp","created":"2023-04-27 13:23:58","subject":"メールマガジンのテスト","apiData":"","messageId":"\\\\\\\\u003C1e928240-492e-af27-fcac-da2513ec7bc8@dxlabo.com\\\\\\\\u003E","from":"info@smtps.jp","to":"user@smtps.jp","senderIp":"","status":"queued"},{"reason":"Recipient address \\\\\\\\u003Cuser@smtps.jp\\\\\\\\u003E is suppressed to send mail. (in reply to RCPT TO)","sourceIp":"","returnPath":"info@return.sandbox.smtps.jp","created":"2023-04-27 13:23:58","subject":"メールマガジンのテスト","apiData":"","messageId":"\\\\\\\\u003C1e928240-492e-af27-fcac-da2513ec7bc8@dxlabo.com\\\\\\\\u003E","from":"info@smtps.jp","to":"user@smtps.jp","senderIp":"","status":"failed"}]}'
}

そこで、 event については別途変換が必要です。

var parameters = JsonSerializer.Deserialize<Dictionary<string, List<MailEvent>>>(data.@event);

クラスの定義

クラスは以下のように定義します。

namespace CMC.Models;

public class MailStatusAttachment
{
    public string event_type { get; set; }
    public string server_composition { get; set; }
    public string @event { get; set; }
    public List<MailEvent> deliveries { get; set; }
    public List<MailEvent> bounces { get; set; }
}

MailEventクラスは次のようになります。

namespace CMC.Models;

public class MailEvent
{
    public string reason { get; set; }
    public string sourceIp { get; set; }
    public string returnPath { get; set; }
    public string created { get; set; }
    public string subject { get; set; }
    public string apiData { get; set; }
    public string messageId { get; set; }
    public string from { get; set; }
    public string to { get; set; }
    public string senderIp { get; set; }
    public string status { get; set; }
}

受信時の処理

コントローラーは以下のようになります。

namespace CMC.Controllers;

using Microsoft.AspNetCore.Mvc;
using CMC.Models;
using System.Text.Json;
using System.Collections.Generic;

// [ApiController]
public class MailController : Controller
{
    [HttpPost("mails")]
    public async Task<IActionResult> UploadFile([FromForm] MailStatusAttachment data)
    {
        // eventキー以下の変換
        var parameters = JsonSerializer.Deserialize<Dictionary<string, List<MailEvent>>>(data.@event);
        if (data.event_type == "deliveries")
        {
            data.deliveries = parameters["deliveries"];
        }
        if (data.event_type == "bounces")
        {
            data.bounces = parameters["bounces"];
        }
        Console.WriteLine(JsonSerializer.Serialize(data));
        // 処理が成功した場合は200 OKを返す
        return new OkResult();

    }
}

メール送信した直後

メール送信を行うと、そのデータがキューに入ります。そして、以下のようなWebhookが送られてきます(データは一部マスキングしています)。

{
    "event_type": "deliveries",
    "server_composition": "pro",
    "event": '{"deliveries":[{"reason":"","sourceIp":"100.100.100.1","returnPath":"info@return.pro.smtps.jp","created":"2023-01-25 14:03:06","subject":"メールマガジンのテスト","apiData":"","messageId":"<031a32d4-06cd-b1ae-9526-011c0b9f1296@example.com>","from":"info@example.com","to":"user@example.jp","senderIp":"","status":"queued"}]}'
}

メール送信完了時

Customers Mail Cloudからメール送信処理が行われると、ステータスが succeeded になったWebhookが送られてきます。

{
    "event_type": "deliveries",
    "server_composition": "pro",
    "event": '{"deliveries":[{"reason":"","sourceIp":"","returnPath":"info@return.pro.smtps.jp","created":"2023-01-25 14:03:09","subject":"メールマガジンのテスト","apiData":"","messageId":"<031a32d4-06cd-b1ae-9526-011c0b9f1296@example.com>","from":"info@example.com","to":"user@example.jp","senderIp":"100.100.100.3","status":"succeeded"}]}'
}

メール送信失敗時(メールアドレス形式に問題がある場合)

メールアドレスの形式に問題があるなど、送信処理が失敗した場合には以下のようなWebhookが送られてきます。

{
    "event_type": "bounces",
    "server_composition": "pro",
    "event": '{"bounces":[{"reason":"host unknown","returnPath":"info@return.pro.smtps.jp","created":"2023-01-25 14:05:15","subject":"メールマガジンのテスト","apiData":"","messageId":"<8f902ee7-ae65-8711-48a8-2f708cb14205@example.com>","from":"info@example.com","to":"user@example","status":"1"}]}'
}

メール送信失敗時(送信先サーバーからエラーが返ってくる場合)

ユーザーが存在しない、メールボックスがいっぱいなど送信先サーバーからエラーが返ってきた場合には、以下のようなJSONが返ってきます。

{
    "event_type": "deliveries",
    "server_composition": "pro",
    "event": '{"deliveries":[{"reason":"550 5.1.1 The email account that you tried to reach does not exist. Please try 5.1.1 double-checking the recipient's email address for typos or 5.1.1 unnecessary spaces. Learn more at 5.1.1  <https://support.google.com/mail/?p=NoSuchUser> b197-20020a621bce000000b0058b80756b07si311029pfb.3 - gsmtp (in reply to RCPT TO)","sourceIp":"","returnPath":"info@return.pro.smtps.jp","created":"2023-01-25 14:06:06","subject":"メールマガジンのテスト","apiData":"","messageId":"<9e7e564c-ac83-8cd8-2cb4-b9ff2a9f168d@example.com>","from":"info@example.com","to":"no-user@example.jp","senderIp":"100.100.100.3","status":"failed"}]}'
}

エラーとしてのWebhookも送られてきます。上記のものと event_type が異なるので注意してください。

{
    "event_type": "bounces",
    "server_composition": "pro",
    "event": '{"bounces":[{"reason":"550 5.1.1 The email account that you tried to reach does not exist. Please try 5.1.1 double-checking the recipient's email address for typos or 5.1.1 unnecessary spaces. Learn more at 5.1.1  <https://support.google.com/mail/?p=NoSuchUser> b197-20020a621bce000000b0058b80756b07si311029pfb.3 - gsmtp (in reply to RCPT TO)","returnPath":"info@return.pro.smtps.jp","created":"2023-01-25 14:06:07","subject":"メールマガジンのテスト","apiData":"","messageId":"<9e7e564c-ac83-8cd8-2cb4-b9ff2a9f168d@example.com>","from":"info@example.com","to":"no-user@example.jp","status":"2"}]}'
}

まとめ

Webhookを使うことで、メール送信ステータスの変化に応じて通知を受け取れるようになります。メールと連携したシステムを開発する際に役立つでしょう。

ASP.NETの場合は application/json を指定した方が全体として、すべて指定したクラスとして受け取れるので、扱いやすいでしょう。ぜひお試しください。なお、このWebhookはSMTP経由の場合、利用できます。