In this article, we are going to learn how to send an email in ASP.NET Core. We are going to start with simple project creation and creating a basic email configuration. Once we are finished with that, we are going to install the required MailKit library and create the logic to send email messages from ASP.NET Core.

We are going to send email messages in ASP.NET Core synchronously and asynchronously and change the email body from plain text to pure HTML.

Finally, we are going to learn how to add attachments to the email body.


VIDEO: How to Send an Email with Attachments in ASP.NET Core.


So, let’s see the basic navigation for this article:

Configure an SMTP Server

We are going to start with a new ASP.NET Core 3.1 Web API project. After the project creation, we are going to add the .NET Core Class Library project with the name EmailService. Of course, we have to add a reference to the main project:

Project structure

Let’s add the EmailConfiguration class inside that project:

public class EmailConfiguration
{
    public string From { get; set; }
    public string SmtpServer { get; set; }
    public int Port { get; set; }
    public string UserName { get; set; }
    public string Password { get; set; }
}

This class contains properties needed to configure sending email messages from our application. We are going to use the appsettings.json file to populate these properties, and in order to do that, we have to modify that file:

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  },
  "EmailConfiguration": {
    "From": "[email protected]",
    "SmtpServer": "smtp.gmail.com",
    "Port": 465,
    "Username": "[email protected]",
    "Password": "our test password"
  },
    "AllowedHosts": "*"
}

To finish with the configuration process, we are going to modify the ConfigureServices method in the Startup.cs class:

public void ConfigureServices(IServiceCollection services)
{
    var emailConfig = Configuration
        .GetSection("EmailConfiguration")
        .Get<EmailConfiguration>();
    services.AddSingleton(emailConfig);

    services.AddControllers();
}

So, we extract configuration values from the appsettings file and register EmailConfiguration as a singleton. And that’s all we need to configure our email service.

Add the MailKit Library

Before starting any other operation in our project, we have to add the NETCore.MailKit library to the EmailService project:

MailKit

This library is going to help us send the email.

The imports we’ll be using are:

using MailKit.Net.Smtp;
using MimeKit;

Send a Test Email

Next, in the same project, we are going to create a Message class:

public class Message
{
    public List<MailboxAddress> To { get; set; }
    public string Subject { get; set; }
    public string Content { get; set; }

    public Message(IEnumerable<string> to, string subject, string content)
    {
        To = new List<MailboxAddress>();

        To.AddRange(to.Select(x => new MailboxAddress(x)));
        Subject = subject;
        Content = content;        
    }
}

We are going to use this class to set the data related to our email recipients, subject, and content.

Then, let’s create a new interface:

public interface IEmailSender
{
    void SendEmail(Message message);
}

And a class that implements this interface:

public class EmailSender : IEmailSender
{
    private readonly EmailConfiguration _emailConfig;

    public EmailSender(EmailConfiguration emailConfig)
    {
        _emailConfig = emailConfig;
    }

    public void SendEmail(Message message)
    {
        var emailMessage = CreateEmailMessage(message);

        Send(emailMessage);
    }
}

As you can see, we inject email configuration into EmailSender class and then we call two different methods to create an email message and to send the email respectively. Now, let’s implement those two missing methods:

private MimeMessage CreateEmailMessage(Message message)
{
    var emailMessage = new MimeMessage();
    emailMessage.From.Add(new MailboxAddress(_emailConfig.From));
    emailMessage.To.AddRange(message.To);
    emailMessage.Subject = message.Subject;
    emailMessage.Body = new TextPart(MimeKit.Text.TextFormat.Text) { Text = message.Content };

    return emailMessage;
}

private void Send(MimeMessage mailMessage)
{
    using (var client = new SmtpClient())
    {
        try
        {
            client.Connect(_emailConfig.SmtpServer, _emailConfig.Port, true);
            client.AuthenticationMechanisms.Remove("XOAUTH2");
            client.Authenticate(_emailConfig.UserName, _emailConfig.Password);

            client.Send(mailMessage);
        }
        catch
        {
            //log an error message or throw an exception or both.
            throw;
        }
        finally
        {
            client.Disconnect(true);
            client.Dispose();
        }
    }
}

We use the first method to create an object of type MimeMessage and to configure the required properties. Then, we pass that object to the second method and use the SmtpClient class to connect to the email server, authenticate and send the email.

Just pay attention that the SmtpClient class comes from the MailKit.Net.Smtp namespace. So, you should use that one instead of System.Net.Mail.

Now, we have to register this service in the Startup.cs class:

services.AddScoped<IEmailSender, EmailSender>();

We have one more step to do. Let’s modify the Get action in the WeatherForecastController:

[HttpGet]
public IEnumerable<WeatherForecast> Get()
{
    var rng = new Random();

    var message = new Message(new string[] { "[email protected]" }, "Test email", "This is the content from our email.");
    _emailSender.SendEmail(message);

    return Enumerable.Range(1, 5).Select(index => new WeatherForecast
    {
        Date = DateTime.Now.AddDays(index),
        TemperatureC = rng.Next(-20, 55),
        Summary = Summaries[rng.Next(Summaries.Length)]
    })
    .ToArray();
}

That’s it. Let’s test this now:

Postman request sync

We get a 200 OK response. So, let’s check the source email server:

Email server source

And let’s check the destination server:

Email server destination - Send Email

Excellent. Everything works as expected.

Just one note here. If you are using the Gmail server as a source server and you get an error about less secure apps, all you have to do is to sign in to your Gmail account, follow this link and turn on allow option.

Using HTML in the Email Body

In the previous example, we have been using Plain Text as a body format. But we can use HTML as well with a few modifications to our code.

So, all we have to do is to modify the CreateEmailMessage method:

private MimeMessage CreateEmailMessage(Message message)
{
    var emailMessage = new MimeMessage();
    emailMessage.From.Add(new MailboxAddress(_emailConfig.From));
    emailMessage.To.AddRange(message.To);
    emailMessage.Subject = message.Subject;
    emailMessage.Body = new TextPart(MimeKit.Text.TextFormat.Html) { Text = string.Format("<h2 style='color:red;'>{0}</h2>", message.Content) };

    return emailMessage;
}

And that’s all it takes. A simple change of the body text format and the content itself.

We can try sending the same request one more time from Postman:

Html body for the email message

It doesn’t get any easier than that.

Sending an Email in ASP.NET Core Asynchronously

If we want to send email messages asynchronously, we have to make some changes to our project. If you are not familiar with the asynchronous programming in ASP.NET Core Web API, we strongly recommend reading our article about Asynchronous Repository Pattern in ASP.NET Core Web API.

Let’s start with the interface modification:

public interface IEmailSender
{
    void SendEmail(Message message);
    Task SendEmailAsync(Message message);
}

Next, let’s modify the EmalSender class:

public async Task SendEmailAsync(Message message)
{
    var mailMessage = CreateEmailMessage(message);

    await SendAsync(mailMessage);
}

As you can see, we use the same method to create an email message but a different method to send that email. So, we have to implement that one:

private async Task SendAsync(MimeMessage mailMessage)
{
    using (var client = new SmtpClient())
    {
        try
        {
            await client.ConnectAsync(_emailConfig.SmtpServer, _emailConfig.Port, true);
            client.AuthenticationMechanisms.Remove("XOAUTH2");
            await client.AuthenticateAsync(_emailConfig.UserName, _emailConfig.Password);

            await client.SendAsync(mailMessage);
        }
        catch
        {
            //log an error message or throw an exception, or both.
            throw;
        }
        finally
        {
            await client.DisconnectAsync(true);
            client.Dispose();
        }
    }
}

Finally, we have to modify the Get action:

[HttpGet]
public async Task<IEnumerable<WeatherForecast>> Get()
{
    var rng = new Random();

    var message = new Message(new string[] { "[email protected]" }, "Test email async", "This is the content from our async email.");
    await _emailSender.SendEmailAsync(message);

    return Enumerable.Range(1, 5).Select(index => new WeatherForecast
    {
        Date = DateTime.Now.AddDays(index),
        TemperatureC = rng.Next(-20, 55),
        Summary = Summaries[rng.Next(Summaries.Length)]
    })
    .ToArray();
}

Awesome. Now let’s try this out with the same request from Postman:

Async email message

Adding File Attachments

In order to include attachments to our email messages, we have to provide the way for our app to process the attached files.

So, let’s start by adding a new POST action to our controller:

[HttpPost]
public async Task<IEnumerable<WeatherForecast>> Post()
{
    var rng = new Random();

    var files = Request.Form.Files.Any() ? Request.Form.Files : new FormFileCollection();

    var message = new Message(new string[] { "[email protected]" }, "Test mail with Attachments", "This is the content from our mail with attachments.", files);
    await _emailSender.SendEmailAsync(message);

    return Enumerable.Range(1, 5).Select(index => new WeatherForecast
    {
        Date = DateTime.Now.AddDays(index),
        TemperatureC = rng.Next(-20, 55),
        Summary = Summaries[rng.Next(Summaries.Length)]
    })
    .ToArray();
}

This action is almost the same as the previous one, but it has some important modifications. The first thing to notice is that this is a POST action. The second change is the part where we extract files from the request, if any, or return an empty file collection. The third change is related to the Message constructor, it now accepts an additional parameter.

Of course, we have to modify the Message class:

public class Message
{
    public List<MailboxAddress> To { get; set; }
    public string Subject { get; set; }
    public string Content { get; set; }

    public IFormFileCollection Attachments { get; set; }

    public Message(IEnumerable<string> to, string subject, string content, IFormFileCollection attachments)
    {
        To = new List<MailboxAddress>();

        To.AddRange(to.Select(x => new MailboxAddress(x)));
        Subject = subject;
        Content = content;
        Attachments = attachments;
    }
}

As you can see, we’ve added the additional IFormCollection parameter. Now, let’s move on to the EmailSender class:

private MimeMessage CreateEmailMessage(Message message)
{
    var emailMessage = new MimeMessage();
    emailMessage.From.Add(new MailboxAddress(_emailConfig.From));
    emailMessage.To.AddRange(message.To);
    emailMessage.Subject = message.Subject;

    var bodyBuilder = new BodyBuilder { HtmlBody = string.Format("<h2 style='color:red;'>{0}</h2>", message.Content) };

    if (message.Attachments != null && message.Attachments.Any())
    {
        byte[] fileBytes;
        foreach (var attachment in message.Attachments)
        {
            using (var ms = new MemoryStream())
            {
                attachment.CopyTo(ms);
                fileBytes = ms.ToArray();
            }

            bodyBuilder.Attachments.Add(attachment.FileName, fileBytes, ContentType.Parse(attachment.ContentType));
        }
    }

    emailMessage.Body = bodyBuilder.ToMessageBody();
    return emailMessage;
}

We now use the BodyBuilder class to create a body for the email message. Additionally, we check for the attachment files and if they exist, we convert each of them to the byte array and add it to the Attachments part from the bodyBuilder object. Finally, we convert the bodyBuilder object to the message body and return that message.

Now, before we use Postman to send a request, we are going to add the FormOptions configuration to the ConfigureServices method:

public void ConfigureServices(IServiceCollection services)
{
    var emailConfig = Configuration
        .GetSection("EmailConfiguration")
        .Get<EmailConfiguration>();
    services.AddSingleton(emailConfig);
    services.AddScoped<IEmailSender, EmailSender>();

    services.Configure<FormOptions>(o => {
        o.ValueLengthLimit = int.MaxValue;
        o.MultipartBodyLengthLimit = int.MaxValue;
        o.MemoryBufferThreshold = int.MaxValue;
    });

    services.AddControllers();
}

With the FormOptions configuration, we set the limit for its different properties to the maximum value.

One more thing. We have to modify the call to the Message constructor in the Get action because now it accepts four parameters.

var message = new Message(new string[] { "[email protected]" }, "Test email async", "This is the content from our async email.", null);

To prepare a request in Postman, we have to choose the POST method option and use the form-data options to send files with the request:

Send Email with attachment Postman

And let’s inspect our email:

Attachments email

Excellent work.

Conclusion

We did a great job here. Now we have a fully functional Web API service that we can use in our projects to send email messages.

So to sum up, we have learned:

  • How to create a basic configuration
  • The way to send email messages with the plain text or HTML body
  • How to send email messages asynchronously
  • And how to include attachments in an email message