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

2 Following


Comment Count = 0

Please log in to comment or follow.

Login Register