How to send emails in Umbraco 9 using IEmailSender

Posted written by Paul Seal on October 01, 2021 Umbraco

I released a new starter kit for Umbraco 9 called Portfolio Starter Kit. In that, there is a contact form and if you have your SMTP settings set up correctly in appSettings.json it will send the email for you.

Here is the Contact Controller Code:

using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Portfolio.Core.Models.ViewModels;
using System.Threading.Tasks;
using Umbraco.Cms.Core.Cache;
using Umbraco.Cms.Core.Configuration.Models;
using Umbraco.Cms.Core.Logging;
using Umbraco.Cms.Core.Mail;
using Umbraco.Cms.Core.Models.Email;
using Umbraco.Cms.Core.Routing;
using Umbraco.Cms.Core.Services;
using Umbraco.Cms.Core.Web;
using Umbraco.Cms.Infrastructure.Persistence;
using Umbraco.Cms.Web.Website.Controllers;


namespace Portfolio.Core.Controllers.Surface
{
    public class ContactSurfaceController : SurfaceController
    {
        private readonly IEmailSender _emailSender;
        private readonly ILogger<ContactSurfaceController> _logger;
        private readonly GlobalSettings _globalSettings;

        public ContactSurfaceController(
            IUmbracoContextAccessor umbracoContextAccessor, 
            IUmbracoDatabaseFactory databaseFactory, 
            ServiceContext services, 
            AppCaches appCaches, 
            IProfilingLogger profilingLogger, 
            IPublishedUrlProvider publishedUrlProvider, 
            IEmailSender emailSender, 
            ILogger<ContactSurfaceController> logger, 
            IOptions<GlobalSettings> globalSettings)
            : base(umbracoContextAccessor, databaseFactory, 
                  services, appCaches, profilingLogger, 
                  publishedUrlProvider)
        {
            _emailSender = emailSender;
            _logger = logger;
            _globalSettings = globalSettings.Value;
        }

        [HttpPost]
        public async Task<IActionResult> SubmitForm(ContactViewModel model)
        {
            if (!ModelState.IsValid) return CurrentUmbracoPage();

            TempData["Success"] = await SendEmail(model);

            return RedirectToCurrentUmbracoPage();
        }

        public async Task<bool> SendEmail(ContactViewModel model)
        {
            try
            {
                var fromAddress = _globalSettings.Smtp.From;

                var subject = string.Format("Enquiry from: {0} - {1}", model.Name, model.Email);
                EmailMessage message = new EmailMessage(fromAddress, model.Email, subject, model.Message, false);
                await _emailSender.SendAsync(message, emailType: "Contact");

                _logger.LogInformation("Contact Form Submitted Successfully");
                return true;
            }
            catch (System.Exception ex)
            {
                _logger.LogError(ex, "Error When Submitting Contact Form");
                return false;
            }
        }
    }
}

The first thing to notice is the amount of services you have to inject into your surface controller

public ContactSurfaceController(
    IUmbracoContextAccessor umbracoContextAccessor, 
    IUmbracoDatabaseFactory databaseFactory, 
    ServiceContext services, 
    AppCaches appCaches, 
    IProfilingLogger profilingLogger, 
    IPublishedUrlProvider publishedUrlProvider, 
    IEmailSender emailSender, 
    ILogger<ContactSurfaceController> logger, 
    IOptions<GlobalSettings> globalSettings)
    : base(umbracoContextAccessor, databaseFactory, 
          services, appCaches, profilingLogger, 
          publishedUrlProvider)
{
    _emailSender = emailSender;
    _logger = logger;
    _globalSettings = globalSettings.Value;
}

The first set of interfaces injected in are to pass to the base class, then we pass in 3 more.

    IEmailSender emailSender, 
    ILogger<ContactSurfaceController> logger, 
    IOptions<GlobalSettings> globalSettings

The IEmailSender is an interface to allow you to send emails using MailKit instead of System.Net.Mail that we are used to from .NET Framework.

ILogger logger is just set up the logger instance for this class

IOptions globalSettings allows us to read from the global settings from the appSettings file and get the sender email address.

The SubmitForm action is an async task and is pretty simple. If the form is valid then call the send email method and if not then show the form with the error messages.

[HttpPost]
public async Task<IActionResult> SubmitForm(ContactViewModel model)
{
    if (!ModelState.IsValid) return CurrentUmbracoPage();

    TempData["Success"] = await SendEmail(model);

    return RedirectToCurrentUmbracoPage();
}

Finally here is the SendEmail method.

public async Task<bool> SendEmail(ContactViewModel model)
{
    try
    {
        var fromAddress = _globalSettings.Smtp.From;

        var subject = string.Format("Enquiry from: {0} - {1}", model.Name, model.Email);
        EmailMessage message = new EmailMessage(fromAddress, model.Email, subject, model.Message, false);
        await _emailSender.SendAsync(message, emailType: "Contact");

        _logger.LogInformation("Contact Form Submitted Successfully");
        return true;
    }
    catch (System.Exception ex)
    {
        _logger.LogError(ex, "Error When Submitting Contact Form");
        return false;
    }
}

I believe the email sending doesn't work if you leave the from address as [email protected] so you need to change that.

If you want to test it out locally, you may need to run this command to add a valid dev SSL certificate to allow the sending of emails.

dotnet dev-certs https --trust

And if that doesn't work, you might need to add this to your appSettings.development.json Smtp section settings:

1