#4 Sending Email Using Mailkit
Friday, November 16, 2018
In this article we will create the ability to send emails from our web application using the Mailkit nuget package. In addition to sending basic emails our message service will also be able to deal with the cases when we want to send along attachments as well.
Parts
- Part 29: Offset Pager Urls
- Part 28: Offset Pager Start
- Part 27: Mock Context Builder
- Part 26: Mock Repository
- Part 25: Mock Async
- Part 24: Picture Tag Helper
- Part 23: Img DPR Tag Helper
- Part 22: Img Responsive Tag Helper
- Part 21: Img Optimized Display
- Part 20: Img Optimization
- Part 19: Img Lazy Loading
- Part 18: Img Responsive
- Part 17: Bottom Nav
- Part 16: Main Nav Cookie
- Part 15: Main Nav Mobile
- Part 14: Main Nav Search
- Part 13: Main Nav Auth
- Part 12: Main Nav Anchors
- Part 11: Main Nav Logo
- Part 10: Search Results
- Part 9: Search Manager
- Part 8: Search Start
- Part 7: Seeding the Database
- Part 6: Domain Database
- Part 5: Emailing Exceptions
- Part 4: Mailkit
- Part 3: View Renderer
- Part 2: Upgrade to 2.1
- Part 1: Quick Start
Installing Mailkit
We will start by installing the Mailkit nuget package that we will enable us to send emails from our application (a).
Sometimes we want to send an attachment
- WebUi
- Features
- Messaging
- Attachment.cs
- Messaging
- Features
Although strictly speaking now required to start with we will go ahead and deal with the case where we want to
send an attachment with an email. To do this we start by defining an attachment class (b).
In addition to simply holding the information that we want to send in the Content
property we also have the ability to convert the content to a memory stream using the
ContentToStreamAsync
method (b).
Attachment.cs
using System.IO;
using System.Text;
using System.Threading.Tasks;
namespace WebUi.Features.Messaging
{
public class Attachment
{
public Attachment(string fileName, object content)
{
Content = content;
FileName = fileName;
Type = AttachmentType.Text;
}
public enum AttachmentType
{
Json,
Text
}
public object Content { get; set; }
public string FileName { get; set; }
public AttachmentType Type { get; set; }
public async Task<MemoryStream> ContentToStreamAsync()
{
string text;
switch (Type)
{
case AttachmentType.Json:
text = Newtonsoft.Json.JsonConvert.SerializeObject(Content);
break;
case AttachmentType.Text:
text = Content.ToString();
break;
default:
throw new ArgumentOutOfRangeException();
}
// Would create a string extension method to be able to reuse the following.
var stream = new MemoryStream();
var writer = new StreamWriter(stream, Encoding.UTF8);
await writer.WriteAsync(text);
await writer.FlushAsync();
stream.Position = 0;
return stream;
}
}
}
Time for our message service and its interface
- WebUi
- Features
- Messaging
- IMessageService.cs
- Messaging
- Features
We will start by defining an interface that our message service will implement. The interface, at least for right now, will contain a single method signature shown in (c).
IMessageService.cs
using System.Threading.Tasks;
namespace WebUi.Features.Messaging
{
public interface IMessageService
{
Task SendEmailAsync(
string fromDisplayName,
string fromEmailAddress,
string toName,
string toEmailAddress,
string subject,
string message,
params Attachment[] attachments);
}
}
- WebUi
- Features
- Messaging
- MessageService.cs
- Messaging
- Features
Of course an interface is not very useful without a class that implements it which brings us to our message service (d). Most of the code below is of course not specific to any situation except for the area marked as specific to a particular provider. You will need to supply the information for your particular situation.
MessageService.cs
using System.Threading.Tasks;
using MailKit.Net.Smtp;
using MimeKit;
namespace WebUi.Features.Messaging
{
public class MessageService : IMessageService
{
public async Task SendEmailAsync(
string fromDisplayName,
string fromEmailAddress,
string toName,
string toEmailAddress,
string subject,
string message,
params Attachment[] attachments)
{
var email = new MimeMessage();
email.From.Add(new MailboxAddress(fromDisplayName, fromEmailAddress));
email.To.Add(new MailboxAddress(toName, toEmailAddress));
email.Subject = subject;
var body = new BodyBuilder
{
HtmlBody = message
};
foreach (var attachment in attachments)
{
using (var stream = await attachment.ContentToStreamAsync())
{
body.Attachments.Add(attachment.FileName, stream);
}
}
email.Body = body.ToMessageBody();
using (var client = new SmtpClient())
{
client.ServerCertificateValidationCallback = (sender, certificate, chain, errors) => true;
client.AuthenticationMechanisms.Remove("XOAUTH2");
// Start of provider specific settings
await client.ConnectAsync("smtp.host", 587, false).ConfigureAwait(false);
await client.AuthenticateAsync("username", "password").ConfigureAwait(false);
// End of provider specific settings
await client.SendAsync(email).ConfigureAwait(false);
await client.DisconnectAsync(true).ConfigureAwait(false);
}
}
}
}
The final touch is setting up dependency injection
- WebUi
- Startup.cs
At some point we might get around to doing what we are supposed to be doing and that is testing everything and we all know that to support that we should be injecting our services into the classes that need the functionality. To do this we of course just need to add a reference to both our message service interface and the concrete implementation within the configure services method of our startup class (e).
Startup.cs
...
using WebUi.Features.Messaging;
...
namespace WebUi
{
public class Startup
{
...
public void ConfigureServices(IServiceCollection services)
{
...
services.AddTransient<IMessageService, MessageService>();
}
}
}