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.

Support Code Maze on Patreon to get rid of ads and get the best discounts on our products!
Become a patron at Patreon!

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


To download the source code for this article, you can visit our GitHub repository.

Let’s start.

Configure an SMTP Server

We are going to start with a new ASP.NET Core Web API project. After the project creation, we are going to add the .NET 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();
}

In .NET 6, we have to modify the Program class:

var emailConfig = builder.Configuration
        .GetSection("EmailConfiguration")
        .Get<EmailConfiguration>();
builder.Services.AddSingleton(emailConfig);
    
builder.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.

How to Enable Less Secure Apps with Gmail

Starting on May 30, 2022, ​​Google no longer supports the use of third-party apps or devices which ask you to sign in to your Google Account using only your username and password. So, we have to use a different solution for our application. To do this, we need to enable 2-step verification for our Gmail account first and then we can use the App Password feature to overcome this issue.

So, to enable 2-step verification, we have to:

  • Navigate to our Google Account – the account you will send the emails from (https://myaccount.google.com/)
  • In the menu on the left, we should select Security
  • Then under the “Signing in to Google” section, we can see that 2-Step Verification is off – so we have to click on it
  • Click Get Started, provide your password, and confirm the code by providing a mobile number
  • If everything goes well, you should see the Turn On option, so just click on it

At this point, we have enabled our 2-Step verification and we can return to the Security page. There, under the same “Signing in to Google” section, we can find the App passwords option set to None. 

So, we have to:

  • Click on it
  • Provide a password
  • Click the Select app menu and choose the Other (Custom Name) option
  • Now, all we have to do is to provide any name we like for our app and click the Generate button

This will generate a new password for us, which we should use in our appsettings.json file instead of our personal password. 

With this in place, we are enabled again to send emails with our third-party apps.

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();
        }
    }
}

Note: If you are using the newest Mailkit package, you may want to modify the .From.Add line to:

emailMessage.From.Add(new MailboxAddress("email", _emailConfig.From));

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 if you are using .NET 5 or some of the previous versions:

services.AddScoped<IEmailSender, EmailSender>();

Or for .NET 6, we have to modify the Program class:

builder.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.

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 a 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();
}

In .NET 6, we have to use the builder variable in the Program class:

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

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 using C#.

So to sum up, we’ve learned:

  • How to create a basic SMTP configuration
  • The way to send email messages with plain text or an HTML body
  • How to send email messages asynchronously
  • How to include attachments in an email message
Liked it? Take a second to support Code Maze on Patreon and get the ad free reading experience!
Become a patron at Patreon!