We use cookies to provide you with a great user experience, analyze traffic and serve targeted promotions.   Learn More   Accept
Card image cap

Template based Emails - Adding and Embedding Images to Content using MimeKit

ASP.NET Core  • Posted 6 months ago

This is the third article in the series for understanding how we can implement email functionality in an asp.net core application. In the first article we have setup the context and seen how we can use a Razor View for generating dynamic html content for our emails and then in the second article, we looked at how we can use the html content generated to send out email to the recipient users by means of MailKit library. In this article, we shall top it off by looking at how we can add images to our html content and push them into the mail body. For this we make use of the class BodyBuilder from the MimeKit namespace, which is quite helpful in setting up the email body one step at a time.

To begin with, We are now required to add a header image and a footer image in our mail design. For this let's begin by creating models which hold the image contents and the body content for us. The models are designed as follows:


namespace ReadersApi.Controllers
{
    public class MailViewModel
    {
        public LinkedResource HeaderImage { get; set; }
        public string Content { get; set; }
        public LinkedResource FooterImage { get; set; }
    }

    public class LinkedResource
    {
        public string ContentId { get; set; }
        public string ContentPath { get; set; }
        public string ContentType { get; set; }
    }
}

The LinkedResource class is a metadata class which contains a resource's contentId: which is a unique identifier for the content resource we gonna attach along with the contentPath which holds the filepath of the file to be added and a contentType which in our case is not used. These LinkedResource types are used in the MailViewModel class which has a property each for the header image, footer image and the actual content: the html we obtain from the razor view.

We setup the MailController API method which is the starting point for our testing as shown below:


namespace ReadersApi.Controllers
{
    [Route("api/[controller]")]
    public class LogicController : ControllerBase
    {
        private ITemplateHelper _templateHelper;
        private IMailHelper _mail;
        private IHostingEnvironment _environment;

        public LogicController(ITemplateHelper helper, IMailHelper mail, IHostingEnvironment environment)
        {
            _templateHelper = helper;
            _mail = mail;
            _environment = environment;
        }

        [HttpGet]
        [Route("template")]
        public async Task<string> GetTemplate()
        {
            var model = new MailViewModel();

            var response = await _templateHelper.GetTemplateHtmlAsStringAsync<List<Reader>>("Templates/Content", ReaderStore.Readers);

            model.Content = response;

            var headerImagePath = string.Format("{0}/{1}", _environment.ContentRootPath, "Images/banner-image.png");

            model.HeaderImage = new LinkedResource
            {
                ContentId = "header",
                ContentPath = headerImagePath,
                ContentType = "image/png"
            };

            _mail.SendMail("bro@aka.com", "Reader Test Data", model);

            return response;
        }
    }
}

The header image is available under the folder Images in the root directory of the application.

Now that we are done here, we further make changes to the helper interface and the implementation to suit the changes here: to pass in a MailViewModel instead of the content.


namespace ReadersApi.Providers
{
    public interface IMailHelper
    {
        void SendMail(string to, string subject, MailViewModel model);
    }

    public class MailHelper : IMailHelper
    {
        IConfiguration configuration;

        public MailHelper(IConfiguration configuration)
        {
            this.configuration = configuration;
        }

        public void SendMail(string to, string subject, MailViewModel model)
        {
            string fromAddress = configuration["SmtpConfig:FromAddress"];
            string serverAddress = configuration["SmtpConfig:ServerAddress"];
            string username = configuration["SmtpConfig:Username"];
            string password = configuration["SmtpConfig:Password"];
            int port = Convert.ToInt32(configuration["SmtpConfig:Port"]);
            bool isSsl = Convert.ToBoolean(configuration["SmtpConfig:IsUseSsl"]);

                try
                {
                    SendSmtpMail(fromAddress, serverAddress, username, password, to, subject, model, port, isSsl);
                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex);
                }
        }

        private void SendSmtpMail(
            string from,
            string serverAddress,
            string username,
            string password,
            string to,
            string subject,
            MailViewModel messageContent,
            int port,
            bool isUseSsl)
        {
            try
            {

                var message = new MimeMessage();
                message.From.Add(new MailboxAddress(from, from));
                message.To.Add(new MailboxAddress(to, to));
                message.Subject = subject;
                
                .... Message Body is Built here ...

                using (var client = new MailKit.Net.Smtp.SmtpClient())
                {
                    client.Connect(serverAddress, port, isUseSsl);
                    client.Authenticate(username, password);
                    client.Send(message);
                    client.Disconnect(true);
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex);
            }
        }
    }
}

This is pretty much the same code, we saw in the previous article; the only changes being that we pass in the MailViewModel in place of the actual content string. Now we need to build the message body for to pass the header and footer images.


		// create a new instance of the BodyBuilder class
		var builder = new BodyBuilder();
		
		// add a header image linked resource to the builder
                var header = builder.LinkedResources.Add(messageContent.HeaderImage.ContentPath);
		
		// set the contentId for the resource added to the builder to the contentId passed
                header.ContentId = messageContent.HeaderImage.ContentId;

		// set the html body (since we are passing html content to render) to the body of the builder
                builder.HtmlBody = messageContent.Content;
		
		// convert all the configuration made into a message content which is assigned to the message body
                message.Body = builder.ToMessageBody();

The above is a step wise implementation of the body building logic. This makes the image passed to the message content get attached to the mail content and gets it embedded into the mail. There is one more change needed to the razor view html we have designed earlier.


@model List<ReadersApi.Providers.Reader>

<!DOCTYPE html>
<html>
    <head>
        <style>
            .no-border {
                border: 1px #dddddd;
            }

            .thead {
                background-color: #efefef;
            }

            .header-content {
                background: url('cid:header');
                padding: 5px;
                margin: 5px;
                text-align: center;
            }
        </style>
    </head>
    <body>
        <div class="header-content">
            <h3>Referbruv! For all things programming.</h3>
        </div>
        <div>
            <p>Hi,</p>
        </div>
        <div>
            <p>Please find the below list of readers currently available.</p>
        </div>
        <div>
            <table class="no-border">
                <tr class="thead">
                    <td>Id</td>
                    <td>User Name</td>
                    <td>Email Address</td>
                </tr>
                @foreach(var reader in @Model) {
                    <tr>
                        <td>@reader.Id</td>
                        <td>@reader.UserName</td>
                        <td>@reader.EmailAddress</td>
                    </tr>
                }
            </table>
        </div>
    </body>
</html>

In the above code, observe the class header-content contains a background style tag which receives a value for url as cid:header. Which is the contentId which we are passing for the header image at the builder stage. The MimeKit library embeds the linked resources at the places specified in the email body specified by the contentId for the specified resource.

Once we run this implementation, we get the below result for a mail received.

data/Admin/2019/12/Mail-2.PNG

In this way, we can achieve image embedding on a mail content by means of MimeKit and MailKit libraries.

Also Read:

Sending Template based Emails using MailKit

Extracting HTML from a Rendered Razor View using Razor View Engine

mailkit send html email mailkit html body mailkit html mailkit mailkit embed image mailkit .net core mailkit example mailkit async asp.net core mailkit mailkit .net mailkit tutorial mailkit dotnet core