matomo

Sending E-Mails with asp.net core
Skip to main content

Development blog

This blog contains posts mostly regarding development in asp.net core. System Engineering and DevOps topics are also covered in this blog.
Most of the content published on this homepage is a practical approach to official documentations like docs.microsoft.com. I try to link the related parts of said documentations in every post.

If you feel that you are able to improve this blog by providing any kind of update or feedback, please do so via the comments section or the contact form.

Enjoy reading!

Sending E-Mails with asp.net core

Sending E-Mails with your application is often really usefull or even unavoidable. If you want to verify users or send statistics to yourself for example.

A pretty easy way to send emails is by using the SendGrid API. SendGrid allows you to send E-Mails from their servers and it is even for free if you stay below 100 E-Mails a day.

Also, and this is pretty important to me, SendGrid supports E-Mail Templates. This will allow you to send pretty E-Mails without having trouble with HTML content. Also you can update the templates without changing the code of your application.

In this Example, I will create a function to send a confirmation e-mail after a user registers and one that will send a password reset email. I manage the Users with Identity Framework Core and I also use some functions provided by it.

As prerequisites you need to create an account at sendgrid. With this account create an API-Key and a template. In my case I create a template for both of the functions.

EmailConfirmation

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html data-editor-version="2" class="sg-campaigns" xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1, maximum-scale=1" /><!--[if !mso]><!-->
<meta http-equiv="X-UA-Compatible" content="IE=Edge" /><!--<![endif]-->
<!--[if (gte mso 9)|(IE)]>
<xml>
<o:OfficeDocumentSettings>
<o:AllowPNG/>
<o:PixelsPerInch>96</o:PixelsPerInch>
</o:OfficeDocumentSettings>
</xml>
<![endif]-->
<!--[if (gte mso 9)|(IE)]>
<style type="text/css">
body {width: 600px;margin: 0 auto;}
table {border-collapse: collapse;}
table, td {mso-table-lspace: 0pt;mso-table-rspace: 0pt;}
img {-ms-interpolation-mode: bicubic;}
</style>
<![endif]--> <style type="text/css">
body, p, div {
font-family: arial;
font-size: 14px;
}
body {
color: #000000;
}
body a {
color: #1188E6;
text-decoration: none;
}
p { margin: 0; padding: 0; }
table.wrapper {
width:100% !important;
table-layout: fixed;
-webkit-font-smoothing: antialiased;
-webkit-text-size-adjust: 100%;
-moz-text-size-adjust: 100%;
-ms-text-size-adjust: 100%;
}
img.max-width {
max-width: 100% !important;
}
.column.of-2 {
width: 50%;
}
.column.of-3 {
width: 33.333%;
}
.column.of-4 {
width: 25%;
}
@media screen and (max-width:480px) {
.preheader .rightColumnContent,
.footer .rightColumnContent {
text-align: left !important;
}
.preheader .rightColumnContent div,
.preheader .rightColumnContent span,
.footer .rightColumnContent div,
.footer .rightColumnContent span {
text-align: left !important;
}
.preheader .rightColumnContent,
.preheader .leftColumnContent {
font-size: 80% !important;
padding: 5px 0;
}
table.wrapper-mobile {
width: 100% !important;
table-layout: fixed;
}
img.max-width {
height: auto !important;
max-width: 480px !important;
}
a.bulletproof-button {
display: block !important;
width: auto !important;
font-size: 80%;
padding-left: 0 !important;
padding-right: 0 !important;
}
.columns {
width: 100% !important;
}
.column {
display: block !important;
width: 100% !important;
padding-left: 0 !important;
padding-right: 0 !important;
margin-left: 0 !important;
margin-right: 0 !important;
}
}
</style>
<!--user entered Head Start-->

<!--End Head user entered-->
</head>
<body>
<center class="wrapper" data-link-color="#1188E6" data-body-style="font-size: 14px; font-family: arial; color: #000000; background-color: #ffffff;">
<div class="webkit">
<table cellpadding="0" cellspacing="0" border="0" width="100%" class="wrapper" bgcolor="#ffffff">
<tr>
<td valign="top" bgcolor="#ffffff" width="100%">
<table width="100%" role="content-container" class="outer" align="center" cellpadding="0" cellspacing="0" border="0">
<tr>
<td width="100%">
<table width="100%" cellpadding="0" cellspacing="0" border="0">
<tr>
<td>
<!--[if mso]>
<center>
<table><tr><td width="600">
<![endif]-->
<table width="100%" cellpadding="0" cellspacing="0" border="0" style="width: 100%; max-width:600px;" align="center">
<tr>
<td role="modules-container" style="padding: 0px 0px 0px 0px; color: #000000; text-align: left;" bgcolor="#ffffff" width="100%" align="left">

<table class="module preheader preheader-hide" role="module" data-type="preheader" border="0" cellpadding="0" cellspacing="0" width="100%"
style="display: none !important; mso-hide: all; visibility: hidden; opacity: 0; color: transparent; height: 0; width: 0;">
<tr>
<td role="module-content">
<p></p>
</td>
</tr>
</table>

<table class="module" role="module" data-type="text" border="0" cellpadding="0" cellspacing="0" width="100%" style="table-layout: fixed;">
<tr>
<td style="padding:18px 0px 18px 0px;line-height:22px;text-align:inherit;"
height="100%"
valign="top"
bgcolor="">
<h1>Thanks for joining cointer!</h1> <div>there is only one thing left before you can start counting your coins.</div> <div>&nbsp;</div></td>
</tr>
</table>
<table border="0" cellPadding="0" cellSpacing="0" class="module" data-role="module-button" data-type="button" role="module" style="table-layout:fixed" width="100%"><tbody><tr><td align="center" bgcolor="" class="outer-td" style="padding:0px 0px 0px 0px"><table border="0" cellPadding="0" cellSpacing="0" class="button-css__deep-table___2OZyb wrapper-mobile" style="text-align:center"><tbody><tr><td align="center" bgcolor="#3f51b5" class="inner-td" style="border-radius:6px;font-size:16px;text-align:center;background-color:inherit"><a href="{{Link}}" style="background-color:#3f51b5;border:1px solid #333333;border-color:#3f51b5;border-radius:6px;border-width:1px;color:#ffffff;display:inline-block;font-family:arial,helvetica,sans-serif;font-size:16px;font-weight:normal;letter-spacing:0px;line-height:16px;padding:12px 18px 12px 18px;text-align:center;text-decoration:none" target="_blank">Confirm E-Mail</a></td></tr></tbody></table></td></tr></tbody></table><div data-role="module-unsubscribe" class="module unsubscribe-css__unsubscribe___2CDlR" role="module" data-type="unsubscribe" style="color:#444444;font-size:12px;line-height:20px;padding:16px 16px 16px 16px;text-align:center"><div class="Unsubscribe--addressLine"><p class="Unsubscribe--senderName" style="font-family:Arial, Helvetica, sans-serif;font-size:12px;line-height:20px">cointer.steinbach.io</p><p style="font-family:Arial, Helvetica, sans-serif;font-size:12px;line-height:20px"><span class="Unsubscribe--senderAddress">Altgasse 3 / 14</span>, <span class="Unsubscribe--senderZip">1130</span> <span class="Unsubscribe--senderCity">Vienna</span>, <span class="Unsubscribe--senderState">Austria</span> </p></div><p style="font-family:Arial, Helvetica, sans-serif;font-size:12px;line-height:20px"><a class="Unsubscribe--unsubscribeLink" href="<%asm_group_unsubscribe_raw_url%>" style="color:#3f51b5">Unsubscribe</a> - <a class="Unsubscribe--unsubscribePreferences" href="<%asm_preferences_raw_url%>" style="color:#3f51b5">Unsubscribe Preferences</a></p></div>
</td>
</tr>
</table>
<!--[if mso]>
</td></tr></table>
</center>
<![endif]-->
</td>
</tr>
</table>
</td>
</tr>
</table>
</td>
</tr>
</table>
</div>
</center>
</body>
</html>

 and PasswordReset


<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html data-editor-version="2" class="sg-campaigns" xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1, maximum-scale=1" /><!--[if !mso]><!-->
<meta http-equiv="X-UA-Compatible" content="IE=Edge" /><!--<![endif]-->
<!--[if (gte mso 9)|(IE)]>
<xml>
<o:OfficeDocumentSettings>
<o:AllowPNG/>
<o:PixelsPerInch>96</o:PixelsPerInch>
</o:OfficeDocumentSettings>
</xml>
<![endif]-->
<!--[if (gte mso 9)|(IE)]>
<style type="text/css">
body {width: 600px;margin: 0 auto;}
table {border-collapse: collapse;}
table, td {mso-table-lspace: 0pt;mso-table-rspace: 0pt;}
img {-ms-interpolation-mode: bicubic;}
</style>
<![endif]--><style type="text/css">
body, p, div {
font-family: arial;
font-size: 14px;
}
body {
color: #000000;
}
body a {
color: #1188E6;
text-decoration: none;
}
p { margin: 0; padding: 0; }
table.wrapper {
width:100% !important;
table-layout: fixed;
-webkit-font-smoothing: antialiased;
-webkit-text-size-adjust: 100%;
-moz-text-size-adjust: 100%;
-ms-text-size-adjust: 100%;
}
img.max-width {
max-width: 100% !important;
}
.column.of-2 {
width: 50%;
}
.column.of-3 {
width: 33.333%;
}
.column.of-4 {
width: 25%;
}
@media screen and (max-width:480px) {
.preheader .rightColumnContent,
.footer .rightColumnContent {
text-align: left !important;
}
.preheader .rightColumnContent div,
.preheader .rightColumnContent span,
.footer .rightColumnContent div,
.footer .rightColumnContent span {
text-align: left !important;
}
.preheader .rightColumnContent,
.preheader .leftColumnContent {
font-size: 80% !important;
padding: 5px 0;
}
table.wrapper-mobile {
width: 100% !important;
table-layout: fixed;
}
img.max-width {
height: auto !important;
max-width: 480px !important;
}
a.bulletproof-button {
display: block !important;
width: auto !important;
font-size: 80%;
padding-left: 0 !important;
padding-right: 0 !important;
}
.columns {
width: 100% !important;
}
.column {
display: block !important;
width: 100% !important;
padding-left: 0 !important;
padding-right: 0 !important;
margin-left: 0 !important;
margin-right: 0 !important;
}
}
</style>
<!--user entered Head Start-->

<!--End Head user entered-->
</head>
<body>
<center class="wrapper" data-link-color="#1188E6" data-body-style="font-size: 14px; font-family: arial; color: #000000; background-color: #ffffff;">
<div class="webkit">
<table cellpadding="0" cellspacing="0" border="0" width="100%" class="wrapper" bgcolor="#ffffff">
<tr>
<td valign="top" bgcolor="#ffffff" width="100%">
<table width="100%" role="content-container" class="outer" align="center" cellpadding="0" cellspacing="0" border="0">
<tr>
<td width="100%">
<table width="100%" cellpadding="0" cellspacing="0" border="0">
<tr>
<td>
<!--[if mso]>
<center>
<table><tr><td width="600">
<![endif]-->
<table width="100%" cellpadding="0" cellspacing="0" border="0" style="width: 100%; max-width:600px;" align="center">
<tr>
<td role="modules-container" style="padding: 0px 0px 0px 0px; color: #000000; text-align: left;" bgcolor="#ffffff" width="100%" align="left">

<table class="module preheader preheader-hide" role="module" data-type="preheader" border="0" cellpadding="0" cellspacing="0" width="100%"
style="display: none !important; mso-hide: all; visibility: hidden; opacity: 0; color: transparent; height: 0; width: 0;">
<tr>
<td role="module-content">
<p></p>
</td>
</tr>
</table>

<table class="module" role="module" data-type="text" border="0" cellpadding="0" cellspacing="0" width="100%" style="table-layout: fixed;">
<tr>
<td style="padding:18px 0px 18px 0px;line-height:22px;text-align:inherit;"
height="100%"
valign="top"
bgcolor="">
<h1>You forgot your password?</h1> <div>If so, just click the button below to reset it.</div></td>
</tr>
</table>
<table border="0" cellPadding="0" cellSpacing="0" class="module" data-role="module-button" data-type="button" role="module" style="table-layout:fixed" width="100%"><tbody><tr><td align="center" class="outer-td" style="padding:0px 0px 0px 0px"><table border="0" cellPadding="0" cellSpacing="0" class="button-css__deep-table___2OZyb wrapper-mobile" style="text-align:center"><tbody><tr><td align="center" bgcolor="#3f51b5" class="inner-td" style="border-radius:6px;font-size:16px;text-align:center;background-color:inherit"><a style="background-color:#3f51b5;border:1px solid #333333;border-color:#3f51b5;border-radius:6px;border-width:1px;color:#ffffff;display:inline-block;font-family:arial,helvetica,sans-serif;font-size:16px;font-weight:normal;letter-spacing:0px;line-height:16px;padding:12px 18px 12px 18px;text-align:center;text-decoration:none" href="{{Link}}" target="_blank">Reset password</a></td></tr></tbody></table></td></tr></tbody></table>
<table class="module" role="module" data-type="text" border="0" cellpadding="0" cellspacing="0" width="100%" style="table-layout: fixed;">
<tr>
<td style="padding:18px 0px 18px 0px;line-height:22px;text-align:inherit;"
height="100%"
valign="top"
bgcolor="">
<div>If you did not request this, you can just ignore this E-Mail.</div> </td>
</tr>
</table>
<div data-role="module-unsubscribe" class="module unsubscribe-css__unsubscribe___2CDlR" role="module" data-type="unsubscribe" style="color:#444444;font-size:12px;line-height:20px;padding:16px 16px 16px 16px;text-align:center"><div class="Unsubscribe--addressLine"><p class="Unsubscribe--senderName" style="font-family:Arial,Helvetica, sans-serif;font-size:12px;line-height:20px">cointer.steinbach.io</p><p style="font-family:Arial,Helvetica, sans-serif;font-size:12px;line-height:20px"><span class="Unsubscribe--senderAddress">Altgasse 3 / 14</span>, <span class="Unsubscribe--senderZip">1130</span> <span class="Unsubscribe--senderCity">Vienna</span>, <span class="Unsubscribe--senderState">Austria</span> </p></div><p style="font-family:Arial,Helvetica, sans-serif;font-size:12px;line-height:20px"><a class="Unsubscribe--unsubscribeLink" href="<%asm_group_unsubscribe_raw_url%>" style="color:#3f51b5">Unsubscribe</a> - <a class="Unsubscribe--unsubscribePreferences" href="<%asm_preferences_raw_url%>" style="color:#3f51b5">Unsubscribe Preferences</a></p></div>
</td>
</tr>
</table>
<!--[if mso]>
</td></tr></table>
</center>
<![endif]-->
</td>
</tr>
</table>
</td>
</tr>
</table>
</td>
</tr>
</table>
</div>
</center>
</body>
</html>

Note the template-ids (format like x-123456789abcdefghijklmnopqrstuvwx) and insert them into your appsettings.json like this

{
...
"SendGridKey": "YOUR APIKEY",
"SendGridFrom": "test@steinbach.io",
"SendGridEmailConfirmationTemplateID": "x-123456789abcdefghijklmnopqrstuvwx",
"SendGridPasswordResetTemplateID": "x-123456789abcdefghijklmnopqrstuvwx",
...
}

Now to the coding part.

First add a way to access the SendGrid values of appsettings.json. Let's create a class, that represents the Values below the Data folder in the project.

namespace test.Data
{
public class SendGridOptions
{
public static string SendGridKey { get; set; }
public static string SendGridFrom { get; set; }
public static string SendGridEmailConfirmationTemplateID { get; set; }
public static string SendGridPasswordResetTemplateID { get; set; }
}

Then register this class in StartUp.cs, so that the program knows that it should look for these values.

 

First I install SendGrid from NuGet (v9.11.0 at the moment).

To keep things in a good structure, I create a Services folder in my Project. Inside the Services Folder I create an EmailSender.cs class and an IEmailSender.cs Interface.

The interface will expose the functions like this

using test.Models;
using SendGrid;
using System.Threading.Tasks; namespace test.Services
{
public interface IEmailSender
{
Task<Response> SendEmailConfirmation(User user, string Link);
Task<Response> SendPasswordReset(User user, string Link);
}
}

 in EmailSender.cs the functions look like this

using test.Models;
using test.Data;
using Microsoft.Extensions.Options;
using SendGrid;
using SendGrid.Helpers.Mail;
using System.Threading.Tasks; namespace test.Services
{
public class EmailSender : IEmailSender
{
Task<Response> SendEmailConfirmation(User user, string Link);
Task<Response> SendPasswordReset(User user, string Link);
}
}


DevOps Engineer at TIMEWARP IT Consulting GmbH

Read more posts by Marius Steinbach