Extending the EmailSender to Include a Text View


Ken Haggerty
Created 02/09/2019 - Updated 02/21/2019 23:32

In the previous article ASP.NET Core 2.2 - SMTP EmailSender Implementation the EmailSender was sending just html message emails. This article will demonstrate how to add a text view.

I will add a second SendEmailAsync method with parameters for text and html messages. A good explanation of why and how to verify the text view can be found in Plain text versions of html emails. If you followed the previous article, you have a page configured to test the email settings. I will use the same example to demonstrate the added function.

Edit EmailSender.cs, add a new SendEmailAsync method.
public async Task SendEmailAsync(string email, string subject, string textMessage, string htmlMessage)
{
    try
    {
        var message = new MimeMessage();

        message.From.Add(new MailboxAddress(_emailSettings.SenderName, _emailSettings.Sender));

        message.To.Add(new MailboxAddress(email));

        message.Subject = subject;

        var builder = new BodyBuilder
        {
            TextBody = textMessage,
            HtmlBody = htmlMessage
        };

        // Now we just need to set the message body and we're done
        message.Body = builder.ToMessageBody();

        using (var client = new SmtpClient())
        {
            // For demo-purposes, accept all SSL certificates (in case the server supports STARTTLS)
            client.ServerCertificateValidationCallback = (s, c, h, e) => true;

            if (_env.IsDevelopment())
            {
                // The third parameter is useSSL (true if the client should make an SSL-wrapped
                // connection to the server; otherwise, false).
                await client.ConnectAsync(_emailSettings.MailServer, _emailSettings.MailPort, true);
            }
            else
            {
                await client.ConnectAsync(_emailSettings.MailServer);
            }

            // Note: only needed if the SMTP server requires authentication
            await client.AuthenticateAsync(_emailSettings.Sender, _emailSettings.Password);

            await client.SendAsync(message);

            await client.DisconnectAsync(true);
        }

    }
    catch (Exception ex)
    {
        // TODO: handle exception
        throw new InvalidOperationException(ex.Message);
    }
}

Notice the new method uses the MimeKit.BodyBuilder class to add a TextBody and a HtmlBody.

Add the new method signature to IEmailSender.
public interface IEmailSender
{
    Task SendEmailAsync(string email, string subject, string message);
    Task SendEmailAsync(string email, string subject, string textMessage, string htmlMessage);
}

In the previous article ASP.NET Core 2.2 - SMTP EmailSender Implementation we setup a test from a razor page.

Add this label and form to Index.cshtml.
<div class="row">
    <div class="col-6">
        <label class="alert alert-success">@Model.EmailStatusMessage</label>
    </div>
</div>
<div class="row">
    <div class="col-6">
        <form method="post">
            <div class="form-group">
                <label asp-for="Email"></label>
                <input asp-for="Email" class="form-control" />
                <span asp-validation-for="Email" class="text-danger"></span>
            </div>
            <button type="submit" class="btn btn-primary">Email Test</button>
        </form>
    </div>
</div>
Edit Index.cshtml.cs.
public class IndexModel : PageModel
{
    private readonly IEmailSender _emailSender;

    public IndexModel(IEmailSender emailSender)
    {
        _emailSender = emailSender;
    }

    public string EmailStatusMessage { get; set; }

    [Required]
    [BindProperty]
    public string Email { get; set; }

    public void OnGet()
    {
    }

    public async Task OnPostAsync()
    {
        if (!ModelState.IsValid)
        {
            return Page();
        }

        var email = Email;

        var subject = "Email Test";

        var message = "This is a test message.";

        await _emailSender.SendEmailAsync(email, subject, message);

        EmailStatusMessage = "Send test email was successful.";

        return Page();
    }
}

I will use this page to test the new method which also has a text formatted message.

Edit Index.cshtml.cs > OnPostAsync().
public async Task OnPostAsync()
{
    if (!ModelState.IsValid)
    {
        return Page();
    }

    var email = Email;

    var subject = "New Email Test";

    //var message = "This is a test message.";
    //await _emailSender.SendEmailAsync(email, subject, message);

    var textMessage = "This is the text message.";
    var htmlMessage = "This is the <strong>html</strong> message.";
    await _emailSender.SendEmailAsync(email, subject, textMessage, htmlMessage);

    EmailStatusMessage = "Send new test email was successful.";

    return Page();
}

If you search for SendEmailAsync you will find the default implementation in the Register.cshtml.cs and ForgotPassword.cshtml.cs pages in the Account folder and the Index.cshtml.cs page in the Account\Manage folder. I will add a text formatted message to each.

Edit Register.cshtml.cs > OnPostAsync().
Replace
await _emailSender.SendEmailAsync(Input.Email, "Confirm your email",
                        $"Please confirm your account by <a href='{HtmlEncoder.Default.Encode(callbackUrl)}'>clicking here</a>.");
With
var subject = "Confirm your email";

var htmlMessage = "Please confirm your account by " +
                                $"<a href='{HtmlEncoder.Default.Encode(callbackUrl)}'>clicking here</a>.";

var textMessage = "Please confirm your account by coping and pasting this link: \r\n \r\n" +
                               $"{callbackUrl} \r\n \r\nto the address bar of your browser.";

await _emailSender.SendEmailAsync(Input.Email, subject, textMessage, htmlMessage);
Edit ForgotPassword.cshtml.cs > OnPostAsync().
Replace
await _emailSender.SendEmailAsync(
                    Input.Email,
                    "Reset Password",
                    $"Please reset your password by <a href='{HtmlEncoder.Default.Encode(callbackUrl)}'>clicking here</a>.");
With
var subject = "Reset password";

var htmlMessage = "Please reset your password by " +
                                $"<a href='{HtmlEncoder.Default.Encode(callbackUrl)}'>clicking here</a>.";

var textMessage = "Please reset your password by coping and pasting this link: \r\n \r\n" +
                               $"{callbackUrl} \r\n \r\nto the address bar of your browser.";

await _emailSender.SendEmailAsync(Input.Email, subject, textMessage, htmlMessage);
Edit Account\Manage\Index.cshtml.cs > OnPostAsync().
Replace
await _emailSender.SendEmailAsync(
                email,
                "Confirm your email",
                $"Please confirm your account by <a href='{HtmlEncoder.Default.Encode(callbackUrl)}'>clicking here</a>.");
With
var subject = "Confirm your email";

var htmlMessage = "Please confirm your account by " +
                                $"<a href='{HtmlEncoder.Default.Encode(callbackUrl)}'>clicking here</a>.";

var textMessage = "Please confirm your account by coping and pasting this link: \r\n \r\n" +
                               $"{callbackUrl} \r\n \r\nto the address bar of your browser.";

await _emailSender.SendEmailAsync(Input.Email, subject, textMessage, htmlMessage);

If you have implemented SendEmailAsync anywhere else in your project, you can update those following this pattern. This is a good practice for all new implementations.

Look for my next couple of articles where I will demonstrate how to add a html template and an admin notification email.

Article references:

Article Tags:

Email Identity

Comment Count = 0

Please log in to comment or follow.

Login Register
Follow to get web notifications when new comments are posted to this article.
Logged in users receive web notifications for new articles, topics and assets.
Web Notifications