Tag Archives: SendGrid

SendGrid の Event Webhook から POST されたデータを Azure DocumentDB に登録する

SendGrid の Event Webhook を利用すると、メールを送信する際に発生したイベントを指定した URL に POST することができます。POST されるデータは、delivered や bounce などのイベントタイプによってスキーマが異なる JSON 配列なので、スキーマフリーの NoSQL ストアである Azure DocumentDB に登録してみました。

Event Webhook から POST される Web API の作成

まず、Azure Preview ポータルから Azure DocumentDB を作成しておきます。10分ほどかかるので、Visual Studio から ASP.NET Web API のプロジェクトテンプレートを作成します。NuGet から、DocumentDB の Client Library をインストールします。

  • Install-Package Microsoft.Azure.Documents.Client -Pre

SendGridController を追加し、Post メソッドを実装します。

[RoutePrefix("api/sendgrid")]
public class SendGridController : ApiController
{
    private static string EndpointUrl = "https://xxx.documents.azure.com:443/";
    private static string AuthorizationKey = "xxx";
    private static string DatabaseID = "SendGridDb";
    private static string CollectionID = "SendGridCollection";

    [Route]
    public async Task Post()
    {
        // Event Webhook から POST された JSON を 取得
        var json = "";
        using (var reader = new StreamReader(await Request.Content.ReadAsStreamAsync()))
        {
            json = reader.ReadToEnd();
        }

        // Azure DocumentDB に登録
        using (var client = new DocumentClient(new Uri(EndpointUrl), AuthorizationKey))
        {
            var database = client.CreateDatabaseQuery()
                            .Where(x => x.Id == DatabaseID).ToArray().FirstOrDefault();
            if (database == null)
            {
                database = await client.CreateDatabaseAsync(new Database { Id = DatabaseID });
            }
            var documentCollection = client.CreateDocumentCollectionQuery(database.CollectionsLink)
                                        .Where(x => x.Id == CollectionID).ToArray().FirstOrDefault();
            if (documentCollection == null)
            {
                documentCollection = await client.CreateDocumentCollectionAsync(database.SelfLink, new DocumentCollection { Id = CollectionID });
            }
            foreach (var doc in JsonConvert.DeserializeObject<List<dynamic>>(json))
            {
                await client.CreateDocumentAsync(documentCollection.DocumentsLink, doc);
            }
        }
    }
}

EndpointUrl と AuthorizationKey には、Azure Preview ポータルから取得できる値を設定してください。スキーマを定義することなく、dynamic 型をそのまま DocumentDB に登録できるところがいい感じです。実装できたら、Azure Websites にデプロイします。

Event Webhook から POST する URL を設定する

SendGrid のダッシュボードで、Apps メニューから Event Notification を Enabled します。

EventWebhook

HTTP Post URL には、デプロイした Web API の URL(http://xxx.azurewebsites.net/api/sendgrid/)を設定します。送信に失敗したメールアドレスの情報を取得したいので、3つのアクションを設定しました。

  • Dropped・・・無効なメールアドレス、バウンスされたメールアドレスなど
  • Deferred・・・受信側メールサーバーから一時的に拒否された
  • Bounced・・・受信側メールサーバーが受信できない

結果確認

存在しないメールアドレス(test@example.com)を宛先としてメール送信すると、DocumentDB に JSON 形式のデータが登録されます。Azure Preview ポータルのクエリ エクスプローラで、DocumentDB に登録されたデータを確認できます。

doc

まとめ

クラウドで展開されるアプリケーションは、パスワードリマインダや登録完了のお知らせなどのトランザクションメールの機能が必要となるケースが多く、メール送信の信頼性は重要なポイントになります。Event Webhook なら、送信に失敗したメールアドレスを通知できるので、対応を自動化することも可能です。

今回は、Azure DocumentDB に登録しましたが、同じような NoSQL ストアである Azure Search に登録してもいいと思います。Azure DocumentDB は、まだ日本にデプロイされていないので、来月 GA する際にはデプロイされてほしいです。

 

SendGrid の Web API v3 で統計情報を取得する

久しぶりに、SendGrid の Web API のドキュメントを読んだら、Web API v3 が公開されていました。Web API v3  は、Basic 認証を使うようになり、API のリクエストの送信先ホストも変更されています。

以前の投稿で、統計情報を取得する General Statistics の Web API を使って、今月のメール送信通数を取得しました。従来の General Statistics が非推奨となり、今後は v3 の Stats を利用することを推奨しています。そこで、統計情報の取得方法を Web API v3 に置き換えてみました。

統計情報を取得する

今回も、今月のメール送信通数を通知してくれる Azure Webjobs を発行することにします。

static void Main()
{
    try
    {
        var sendGridUserName = "xxx";
        var sendGridPassword = "xxx";

        // Basic 認証
        var parameter = Convert.ToBase64String(UTF8Encoding.UTF8.GetBytes(String.Format("{0}:{1}", sendGridUserName, sendGridPassword)));
        var authHeader = new AuthenticationHeaderValue("Basic", parameter);

        // 今月の統計情報を取得
        var startDate = DateTime.UtcNow.AddHours(9).AddDays(-1).ToString("yyyy-MM-01");
        var queryCollection = new Dictionary<string, string>();
        queryCollection.Add("start_date", startDate);
        queryCollection.Add("aggregated_by", "month");
        var queryString = "?" + String.Join("&", queryCollection.Select(x => String.Format("{0}={1}", x.Key, x.Value)));
        var url = "https://api.sendgrid.com/v3/stats" + queryString;

        int delivered = 0;
        using (var client = new HttpClient())
        {
            client.DefaultRequestHeaders.Authorization = authHeader;
            client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
            var response = client.GetStringAsync(url).Result;
            var statistics = JsonConvert.DeserializeObject<List<MySendGridStatisticsV3>>(response);
            delivered = statistics[0].stats[0].metrics.delivered;
        }
        var message = String.Format("今月の送信件数は、{0} 件です。", delivered);

        // メール送信
        var from = "admin@example.com";

        var to = new List<string>();
        to.Add("tony@example.com");
        var smtpapi = new Header();
        smtpapi.SetTo(to);

        var email = new SendGridMessage();
        email.AddTo(from);	// SmtpapiのSetTo()を使用しているため、実際にはこのアドレスにはメールは送信されない
        email.From = new MailAddress(from, "Web Services Adminstrator");
        email.Subject = "SendGrid Statistics";
        email.Text = message;
        email.Headers.Add("X-Smtpapi", smtpapi.JsonString());

        var credentials = new NetworkCredential(sendGridUserName, sendGridPassword);
        new Web(credentials).Deliver(email);
    }
    catch (Exception ex)
    {
        Console.WriteLine(ex.Message);
    }
}

パラメーターとして、Start_date に月初の日付(2015-03-01)、aggregated_by に month を指定しています。MySendGridStatisticsV3 クラスは、Visual Studio の形式を選択して貼り付けで、JSON をクラスとして貼り付けて作りました。

public class MySendGridStatisticsV3
{
    public string date { get; set; }
    public Stat[] stats { get; set; }
}

public class Stat
{
    public Metrics metrics { get; set; }
}

public class Metrics
{
    public int blocks { get; set; }
    public int bounce_drops { get; set; }
    public int bounces { get; set; }
    public int clicks { get; set; }
    public int deferred { get; set; }
    public int delivered { get; set; }
    public int invalid_emails { get; set; }
    public int opens { get; set; }
    public int processed { get; set; }
    public int requests { get; set; }
    public int spam_report_drops { get; set; }
    public int spam_reports { get; set; }
    public int unique_clicks { get; set; }
    public int unique_opens { get; set; }
    public int unsubscribe_drops { get; set; }
    public int unsubscribes { get; set; }
}

あとは、このコンソールアプリを定期的に実行するスケジュールで WebJobs として発行するだけです。

まとめ

SendGrid の Web API のドキュメントを読んでみると、メールを送信できるだけでなく、いろいろな機能が充実していて面白いです。次回は、Event Webhook にチャレンジしてみたいと思います。

SendGrid の Web API でメール送信通数を取得する

SendGrid を使ってメールを送信する Web サービスを開発するにあたり、日々どのくらい利用されているのかを把握したかったので、ふとツイートしてみると SendGrid のサポートの方からレスを頂けました。

統計情報を取得できる Web API があるとのことで、早速試してみました。

統計情報を取得する Web API のリファレンス

Retrieve Statistics

Try It ボタンを押すと、その場で簡単に API を試すことができて、とても素敵です。2014年9月は、24件のメールを送信していることが分かります。

sendgrid01

 

Azure WebJobs として発行する

SendGrid を使った Web サービスは、Azure Web Sites にデプロイしているので、今月のメール送信通数を通知してくれる WebJobs を発行することにしました。

static void Main()
{
    try
    {
        var sendGridUserName = "azure_xxx@azure.com";
        var sendGridPassword = "xxx";

        // 今月の統計情報を取得
        var startDate = DateTime.UtcNow.AddHours(9).AddDays(-1).ToString("yyyy-MM-01");
        var content = new FormUrlEncodedContent(new Dictionary<string, string>
				    {
					    { "api_user", sendGridUserName },
					    { "api_key", sendGridPassword },
					    { "start_date", startDate },
					    { "aggregate", "1" },
				    });
        var responseMessage = new HttpClient().PostAsync("https://sendgrid.com/api/stats.get.json", content).Result;
	    responseMessage.EnsureSuccessStatusCode();
	    var response = responseMessage.Content.ReadAsStringAsync().Result;
        var statistics = JsonConvert.DeserializeObject<MySendGridStatistics>(response);
        var message = String.Format("今月の送信件数は、{0} 件です。", statistics.delivered);

        // メール送信
        var from = "admin@example.com";

        var to = new List<string>();
        to.Add("tony@example.com");
        var smtpapi = new Header();
        smtpapi.SetTo(to);

        var email = new SendGridMessage();
        email.AddTo(from);
        email.From = new MailAddress(from, "Web Services Adminstrator");
        email.Subject = "SendGrid Statistics";
        email.Text = message;
        email.Headers.Add("X-Smtpapi", smtpapi.JsonString());

        var credentials = new NetworkCredential(sendGridUserName, sendGridPassword);
        new Web(credentials).Deliver(email);
    }
    catch (Exception ex)
    {
        Console.WriteLine(ex.Message);
    }
}

パラメーターとして、Start_date に月初の日付(2014-09-01)、aggregate に 1(統計情報をまとめる)を指定しています。MySendGridStatistics クラスは、Visual Studio の形式を選択して貼り付けで、JSON をクラスとして貼り付けると簡単に作れます。 あとは、このコンソールアプリを定期的に実行するスケジュールで WebJobs として発行するだけです。

アラート機能

メールの送信通数が上限の◯%を超えたらメール通知するというアラート機能は、こちらで設定されています。

sendgrid02

まとめ

今回は、メール送信通数(delivered)しか使っていませんが、他にも情報が取得できるので活用できそうです。回答を頂いた SendGrid のサポートの方、ありがとうございました。

 

Visual Studio から Azure WebJobs に発行する

Visual Studio から Console Projects を Azure WebJobs に発行できるようになったので、試してみました。Visual Studio 2013 Update 3 + Azure SDK 2.4 をインストールする必要があります。

以前の記事と同様に、WebJobs と SendGrid の SDK を使って、発生した例外のメッセージを Queue ストレージに追加すると、メールでアラートが送信され、Table ストレージにログを記録するシナリオで確認します。

Azure Web Jobs SDK を使って SendGrid からメール送信する

WebJobs SDK のバージョンが 0.3.1-beta に更新されており、その間に名前空間やクラス名が変更される大きな変更がはいっているので、コードを書き直しました。新旧バージョンの変更点については、こちらのサイトが参考になります。

Console Projects の作成

Visual Studio のテンプレートから「Microsoft Azure WebJob」を選択し、コンソールアプリを作成します。NuGet から WebJobs と SendGrid の SDK をインストールします。

  • Install-Package Microsoft.Azure.Jobs -Pre
  • Install-Package Sendgrid

ジョブの作成

最新の WebJobs SDK では、SendAlert メソッドの引数に QueueTrigger と CloudTable を使います。キューから受け取ったメッセージを SendGrid でメール送信し、テーブルストレージにログを書き込みます。

public class ErrorLog : TableEntity
{
    public string Message { get; set; }
}

class Program
{
    static void Main()
    {
        new JobHost().RunAndBlock();
    }

    public static void SendAlert([QueueTrigger("error")] string message, [Table("ErrorLog")] CloudTable table)
    {
        // SendGridを利用して、メールでアラート通知
        var sendGridUserName = "username";
        var sendGridPassword = "password";
        var to = new List<string>();
        to.Add("tony@gmail.com");
        var from = "admin@gooner.com";

        var smtpapi = new Header();
        smtpapi.SetTo(to);

        var email = new SendGridMessage();
        email.AddTo(from);
        email.From = new MailAddress(from, "システム管理者");
        email.Subject = "アラート";
        email.Text = message;
        email.Headers.Add("X-Smtpapi", smtpapi.JsonString());

        var credentials = new NetworkCredential(sendGridUserName, sendGridPassword);
        var web = new Web(credentials);
        web.Deliver(email);

        // テーブルストレージにエラーログを書く
        var partitionKey = "error";
        var rowKey = String.Format("{0:D19}", DateTime.MaxValue.Ticks - DateTime.UtcNow.Ticks);
        var operation = TableOperation.Insert(new ErrorLog { PartitionKey = partitionKey, RowKey = rowKey, Message = message });
        table.Execute(operation);
    }
}

接続文字列の名称も、AzureJobsDashboard と AzureJobsStorage に変更されていますので、注意してください。

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <connectionStrings>
    <add name="AzureJobsDashboard" connectionString="DefaultEndpointsProtocol=https;AccountName=xxx;AccountKey=xxx"/>
    <add name="AzureJobsStorage" connectionString="DefaultEndpointsProtocol=https;AccountName=xxx;AccountKey=xxx"/>
  </connectionStrings>
  <startup>
    <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.1" />
  </startup>
</configuration>

Azure WebJobs として発行する

プロジェクトを右クリックして、Azure WebJobs として発行します。WebJob 実行モードは、「継続的に実行」を選択します。

webjobs00

次に、Azure Web Sites のサイト名とリージョンを指定して、新規にサイトを立ち上げます。

 webjobs03

上記の手順で Azure WebJobs が動作しますが、ひとつ注意点があります。Azure 管理ポータルから WebJobs の実行ログを見るためには、接続文字列に「AzureJobsDashboard」を追加する必要があります。

webjobs02

まとめ

Visual Studio からダイレクトに Azure WebJobs として発行できるので、コンソールアプリをビルドして zip ファイルに変換してポータルにアップロードする手間がなくなりました。Visual Studio と Azure SDK が統合されて、ますます便利になっていきますね。

Windows Azure Web Jobs SDK を使って SendGrid からメール送信する

// 20140811 追記 ————————— ↓

WebJobs SDK の 0.3.1-beta に対応したコードに変更しました。

// 20140811 追記 ————————— ↑

Windows Azure – 技術者でつなぐ日めくりカレンダー の 3/7 の記事です。Web Jobs SDK と SendGrid を使って、サンプルアプリを作りました。エラーが発生したら、例外のメッセージを Queue ストレージに追加すると、メールでアラートが送信され、Table ストレージにログを記録するシナリオです。

Web Jobs SDK については、しばやんさんのブログが大変分かりやすかったので、参考にさせて頂きました。

Windows Azure WebJobs SDK の属性が面白そうだったので一通り試してみた

SendGrid については、先日 SendGrid Night Episode 2 に参加してきました。Azure Store から契約すると、マーケティングメールが使えなかったり、プランの上限を超えると送信できなかったりしますが、開発中の動作確認であれば、25,000通/月のFreeプランで十分だと思います。Azure Storeからの契約でも、構造計画研究所が日本語でサポートしてくれるのは助かります。

サンプルアプリの作成

コンソールアプリを作成し、NuGet から Web Jobs と SendGrid の SDK をインストールします。

  • Install-Package Microsoft.WindowsAzure.Jobs.Host -Pre
  • Install-Package Sendgrid -Version 1.0.1

NetworkCredential には、SendGrid と契約した際の username と password を設定します。Azure ストレージの connectionString は、ポータルから Web サイトの方にも、忘れずに設定しておきます。

static void Main(string[] args)
{
    new JobHost().RunAndBlock();
}

public static void SendAlert([QueueInput("error")] string message, [Table("ErrorLog")] IDictionary<Tuple<string, string>, object> table)
{
    // SendGridを利用して、メールでアラート通知
    var myMessage = SendGrid.GenerateInstance();
    myMessage.AddTo("tony@gmail.com");
    myMessage.From = new MailAddress("admin@gooner.com", "システム管理者");
    myMessage.Subject = "アラート";
    myMessage.Text = message;
    var credentials = new NetworkCredential("username", "password");
    var transportSMTP = SMTP.GenerateInstance(credentials);
    transportSMTP.Deliver(myMessage);

    // テーブルストレージにエラーログを書く
    var partitionKey = "error";
    var rowKey = String.Format("{0:D19}", DateTime.MaxValue.Ticks - DateTime.UtcNow.Ticks);
    table.Add(Tuple.Create(partitionKey, rowKey), new { Message = message });
}
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <connectionStrings>
    <add name="AzureJobsRuntime" connectionString="DefaultEndpointsProtocol=https;AccountName=test;AccountKey=xxx"/>
    <add name="AzureJobsData" connectionString="DefaultEndpointsProtocol=https;AccountName=test;AccountKey=xxx"/>
  </connectionStrings>
</configuration>

あとは、ビルドして作成された Debug フォルダごと zip ファイルに変換して、ポータルから Web サイトの Web Jobs にアップロードして完了です。

webjobs

結果確認

Queue ストレージに「エラーが発生しました!」というメッセージを追加すると、メールが送信され、Table ストレージにログが記録されました。

errormail

errorlog

まとめ

Web Jobs SDK を利用するメリットは、アプリ本体の実装からログやアラートなどを切り離せることです。Queue ストレージのメッセージには 8KB のサイズ制限があるので、Table ストレージをトリガーにしたかったのですが、難しいようです。エラーログのアラートなら、Blob ストレージを使ってメールに添付する方法もありですが、リアルタイムではないので注意が必要です。