ASP.NET MVC WebAPI Email Templating and Localization - CodeProject

:

Introduction

In most of the applications, we need to send emails to users. Instead of directly sending emails from various applications, we can write a webapi to do the same so that we can call this Email web API for sending emails from any of the applications.

It does two things in the current example.

  1. Uses the Razor Views as templates to format the email body
  2. Localizes the email based on User Preference

In the below example, I tried to use multiple templates to send list of errors to users in multiple languages. I have used VS2013 to develop this, the source files are attached to the article.

Using the Code

Step 1

Create a new MVC Web API application and in the Controller, add an API SendMail. The method actually takes JSON string as input and sends the email accordingly. The Json string contains a list of To,CC,BCC addresses, information to be sent in email body, subject. Along with these, it contains the Locale in which Email should be sent to user and the template name to be used for formatting the email.

[HttpPost]
       public void SendMail(JToken Payload) {
           try {
               var emailList = Payload.SelectToken("email").ToObject<List<EmailModel>>();

               foreach (var data in emailList) {

                   //Set Culture
                   Thread.CurrentThread.CurrentCulture = new System.Globalization.CultureInfo(
                               data.Locale);
                   Thread.CurrentThread.CurrentUICulture = Thread.CurrentThread.CurrentCulture;

                   //Localize the body
                   var model = JsonConvert.DeserializeObject<Errors>(data.EmailBody);
                   string template = File.ReadAllText(
                   HttpContext.Current.Server.MapPath("~/Templates/")+data.EmailTemplate+".cshtml");
                   data.EmailBody = Razor.Parse<Errors>(template, model);
                   data.EmailSubject = Razor.Parse("@MyWebAPI.Resources.Language.ErrorEmailTitle");

                   //Send Mail
                   var smtpClient = new SmtpClient("smtp.gmail.com", 587) {
                       Credentials = new NetworkCredential("a@b.com", "password"),
                       EnableSsl = true
                   };
                   MailMessage mail = new MailMessage();
                   mail.From = new MailAddress(data.FromAddress);
                   mail.Subject = data.EmailSubject;
                   mail.Body = data.EmailBody;
                   mail.IsBodyHtml = true;
                   if (!string.IsNullOrEmpty(data.EmailIds))
                       mail.To.Add(data.EmailIds);
                   if (!string.IsNullOrEmpty(data.EmailIdsCC))
                       mail.CC.Add(data.EmailIdsCC);
                   if (!string.IsNullOrEmpty(data.EmailIdsBCC))
                       mail.Bcc.Add(data.EmailIdsBCC);
                   smtpClient.Send(mail);
               }
           } catch (Exception e) {
               //Log Errors
           }
       }

In the above method, Razor.Parse plays a vital role. It takes full Razer View path and model object and returns the new HTML formatted string. RazorView(.cshtml) file acts as an Email Template to format the body.

Step 2

In order to localize the email template, i.e., RazorView we need resource files. So add as many resource files as number of languages required. Here, I have added two resource files (English,French) with all the required phrases.

Now add one more resource file (.resx) without any Locale extension, i.e., Language.resx. Now in Language.Designer.cs, add methods to return phrases depending on locale set.

public static string High
   {
       get
       {
           return ResourceManager.GetString("High", resourceCulture);
       }
   }

   public static string at {
       get {
           return ResourceManager.GetString("at", resourceCulture);
       }
   }

   public static string With {
       get {
           return ResourceManager.GetString("With", resourceCulture);
       }
   }

So each word that is required in template should have a method/property here. So by calling this in Razor View, we will display the data.

Step 3

Now design the razor view templates for email body. Depending upon user preference, we will pick the appropriate template. In the below code, we can observe that we are using properties/methods in Language class in order to get the localized phrase.

@using MyWebAPI.Resources;
@using System.Web.WebPages.Razor;

<!DOCTYPE html>
<html>
<head>   
</head>
<body>
    <div>
        <h3>@Model.DisplayCount @Language.New.ToLower() @Language.Errors</h3>
    </div>
    <div class="pmargin">

        <table>
            <tr>
                <th>@Language.Severity</th>
                <th>@Language.Description</th>
                <th>@Language.Timestamp</th>
            </tr>

        @foreach (var error in @Model.ErrorList) {
            <tr>
                <td>@Language.GetSeverityValue(error.Severity)</td>
                <td>@Language.GetDescription(error.Description)</td>
                <td>@error.LastUpdate </td>
            </tr>
        }
    </table>
    <br />
</div>
</body>
</html>

Step 4

Finally, define a model to deserialize user input. Here, I have used JSON as payload to the WebApi and designed the model accordingly.

public class EmailModel {
       [JsonProperty("fromAddress")]
       public string FromAddress { get; set; }

       [JsonProperty("toAddress")]
       public string EmailIds { get; set; }

       [JsonProperty("subject")]
       public string EmailSubject { get; set; }

       [JsonProperty("body")]
       public string EmailBody { get; set; }

       [JsonProperty("toAddressCC")]
       public string EmailIdsCC { get; set; }

       [JsonProperty("toAddressBCC")]
       public string EmailIdsBCC { get; set; }

       [JsonProperty("emailTemplate")]
       public string EmailTemplate { get; set; }

       [JsonProperty("locale")]
       public string Locale { get; set; }
   }

Email Model is to deserialize the user input. And in that input, Email body again contains JSON string for list of errors. Hence, multiple models are required.

public class Errors {
      [JsonProperty("errors")]
      public List<Error> ErrorList { get; set; }

      [JsonProperty("count")]
      public int DisplayCount { get; set; }
  }

  [Serializable]
  public class Error {
      [JsonProperty("description")]
      public string Description { get; set; }

      [JsonProperty("severity")]
      public string Severity { get; set; }

      [JsonProperty("lastUpdate")]
      public DateTime LastUpdate { get; set; }
  }

How to Test this WebAPI?

  1. Create a Website in IIS
  2. In the SendMail API, I have used Gmail SMTP, so please use your gmail credentials in that method.
  3. In fiddler, select POST, Request URL: http://localhost:98/api/Email (use the port number which you have given in IIS) and use the below json as payload (replace the invalid emailids with some valid emailids to test it)
{
    "email": [
        {
            "fromAddress": "mamatha.neni@gmail.com",
            "toAddress": "a@b.com",
            "subject": "My First WebAPI",
            "body": "{\"errors\":[{\"description\":\"Disconnected\",
            \"severity\":\"high\",\"lastUpdate\":\"2015-04-09T06:30:15.244693+05:30\"},
            {\"description\":\"Disconnected\",\"severity\":\"low\",
            \"lastUpdate\":\"2015-04-08T06:30:15.247193+05:30\"}],\"count\":0}",
            "toAddressCC": "abc@gmail.com",
            "toAddressBCC": "",
            "emailTemplate": "ErrorsTable",
            "locale": "fr-CA"
        },
        {
            "fromAddress": "mamatha.neni@gmail.com",
            "toAddress": "a@b.com",
            "subject": "My First WebAPI",
            "body": "{\"errors\":[{\"description\":\"Disconnected\",
            \"severity\":\"high\",\"lastUpdate\":\"2015-04-09T06:30:15.244693+05:30\"},
            {\"description\":\"Disconnected\",\"severity\":\"low\",
            \"lastUpdate\":\"2015-04-08T06:30:15.247193+05:30\"}],\"count\":0}",
            "toAddressCC": "xy@gmail.com",
            "toAddressBCC": "",
            "emailTemplate": "ErrorsList",
            "locale": "en-US"
        }
    ]
}

Points of Interest

Till now, I have learnt so many things by going through the CodeProject articles. This the first time I am trying to write something. So have a glance and revert to me for any questions/comments.