Styling Confirmation Email Identity 2.0 MVC 5 [duplicate] - c#

I have designed an Email Template from Razor Syntax. When I send this template as Email using C# code and SMTP protocol, I get bare Razor and HTML markups as Email Body. Am I wrong in this approach? Are Razor Pages allowed as Email Template?
Here is my Page
#inherits ViewPage
#{
Layout = "_Layout";
ViewBag.Title = "";
}
<div class="container w-420 p-15 bg-white mt-40">
<div style="border-top:3px solid #22BCE5"> </div>
<span style="font-family:Arial;font-size:10pt">
Hello <b>{UserName}</b>,<br /><br />
Thanks for Registering to XYZ Portal<br /><br />
<a style="color:#22BCE5" href="{Url}">Click to Confirm Email</a><br />
<br /><br />
Thanks<br />
Admin (XYZ)
</span>
Update..
using (StreamReader reader = new StreamReader(HttpContext.Current.Server.MapPath("~/ContentPages/EmailConfTemplate.cshtml")))
{
body = reader.ReadToEnd();
//Replace UserName and Other variables available in body Stream
body = body.Replace("{UserName}", FirstName);
}
Later On I am replacing the SMTP Code as ..
MailMessage message = new MailMessage(
ApplicationWideData.fromEmailId, // From field
ToEmailId, // Recipient field
"Click On HyperLink To Verify Email Id", // Subject of the email message
body
);

You do not need any special libraries to render a Razor view to a string in an ASP.NET MVC application.
Here is how you do it in MVC Core 3
public static class ViewToStringRenderer
{
public static async Task<string> RenderViewToStringAsync<TModel>(IServiceProvider requestServices, string viewName, TModel model)
{
var viewEngine = requestServices.GetRequiredService(typeof(IRazorViewEngine)) as IRazorViewEngine;
ViewEngineResult viewEngineResult = viewEngine.GetView(null, viewName, false);
if (viewEngineResult.View == null)
{
throw new Exception("Could not find the View file. Searched locations:\r\n" + string.Join("\r\n", viewEngineResult.SearchedLocations));
}
else
{
IView view = viewEngineResult.View;
var httpContextAccessor = (IHttpContextAccessor)requestServices.GetRequiredService(typeof(IHttpContextAccessor));
var actionContext = new ActionContext(httpContextAccessor.HttpContext, new RouteData(), new ActionDescriptor());
var tempDataProvider = requestServices.GetRequiredService(typeof(ITempDataProvider)) as ITempDataProvider;
using var outputStringWriter = new StringWriter();
var viewContext = new ViewContext(
actionContext,
view,
new ViewDataDictionary<TModel>(new EmptyModelMetadataProvider(), new ModelStateDictionary()) { Model = model },
new TempDataDictionary(actionContext.HttpContext, tempDataProvider),
outputStringWriter,
new HtmlHelperOptions());
await view.RenderAsync(viewContext);
return outputStringWriter.ToString();
}
}
}
In the controller
string str = await ViewToStringRenderer.RenderViewToStringAsync(HttpContext.RequestServices, $"~/Views/Emails/MyEmailTemplate.cshtml", new MyEmailModel { Prop1 = "Hello", Prop2 = 23 });
In ConfigureServices() in Startup.cs
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
Here is how you do it in MVC 5
public static class ViewToStringRenderer
{
public static string RenderViewToString<TModel>(ControllerContext controllerContext, string viewName, TModel model)
{
ViewEngineResult viewEngineResult = ViewEngines.Engines.FindView(controllerContext, viewName, null);
if (viewEngineResult.View == null)
{
throw new Exception("Could not find the View file. Searched locations:\r\n" + viewEngineResult.SearchedLocations);
}
else
{
IView view = viewEngineResult.View;
using (var stringWriter = new StringWriter())
{
var viewContext = new ViewContext(controllerContext, view, new ViewDataDictionary<TModel>(model), new TempDataDictionary(), stringWriter);
view.Render(viewContext, stringWriter);
return stringWriter.ToString();
}
}
}
}
Then, from the controller
ViewToStringRenderer.RenderViewToString(this.ControllerContext, "~/Views/Emails/MyEmailTemplate.cshtml", model);
After you have the email content, it is easy to send the email using MailMessage and SmtpClient.

Email messages only understand two formats: plain text and HTML. Since Razor is neither, it will need to be processed by some engine, so that it gives you back the generated HTML.
That's exactly what happens when you use Razor in ASP.NET MVC, behind the scenes. The Razor file is compiled into a internal C# class, that gets executed, and the result of the execution is the string content of the HTML, that gets sent to the client.
Your problem is that you want and need that processing to run, only to get the HTML back as a string, instead of being sent to the browser. After that you can do whatever you want with the HTML string, including sending it as an e-mail.
There are several packages that include this power, and I've used Westwind.RazorHosting successfully, but you can also use RazorEngine with similar results. I would prefer RazorHosting for standalone non-web applications, and RazorEngine for web applications
Here is a (sanitized) version of some of my code - I'm using Westwind.RazorHosting to send razor-formatted emails from a windows service, using a strongly typed view.
RazorFolderHostContainer host = = new RazorFolderHostContainer();
host.ReferencedAssemblies.Add("NotificationsManagement.dll");
host.TemplatePath = templatePath;
host.Start();
string output = host.RenderTemplate(template.Filename, model);
MailMessage mm = new MailMessage { Subject = subject, IsBodyHtml = true };
mm.Body = output;
mm.To.Add(email);
var smtpClient = new SmtpClient();
await smtpClient.SendMailAsync(mm);

Have you took a look at MVC Mailer?
It's a free package available from GitHub (https://github.com/smsohan/MvcMailer)
There is a step by step guide for it too https://github.com/smsohan/MvcMailer/wiki/MvcMailer-Step-by-Step-Guide
It's also on Nuget too. https://www.nuget.org/packages/MvcMailer
Essentially it will parse your razor view into html.

Check out a razor processor like RazorEngine (https://razorengine.codeplex.com/) which is available on NuGet. It processes razor to create an output, which is what you'd then use as the body of your email.

The Mailzory project is a valuable and convenient choice for sending emails which have Razor templates.
// template path
var viewPath = Path.Combine("Views/Emails", "hello.cshtml");
// read the content of template and pass it to the Email constructor
var template = File.ReadAllText(viewPath);
var email = new Email(template);
// set ViewBag properties
email.ViewBag.Name = "Johnny";
email.ViewBag.Content = "Mailzory Is Funny";
// send email
var task = email.SendAsync("mailzory#outlook.com", "subject");
task.Wait()
this project is hosted at Github. Also there is a nuget package available for Mailzory.

Related

assign HttpPost async to view ASP .NET Core6 MVC

pls help me to get it solved. I am making a ASP .NET Core 6 MVC Application where I have to make two screens login and after validate login show profile details via WEB API call. login is successful after that I am trying to use HTTPPOST to show details into view named home/Account . here is my code in home controller :-
[HttpPost]
public async Task<IActionResult> Account(ReceivedToken token)
{
var companyForCreation = new ReceivedToken
{
email = "myemailid"
};
GetCompanyDetailsViewModel details = new GetCompanyDetailsViewModel();
using (var httpClient = new HttpClient())
{
StringContent content = new StringContent(JsonConvert.SerializeObject(companyForCreation), Encoding.UTF8, "application/json");
using (var response = await httpClient.PostAsync("webAPIURL", content))
{
if (response.StatusCode == System.Net.HttpStatusCode.OK)
{
string apiResponse = await response.Content.ReadAsStringAsync();
details = JsonConvert.DeserializeObject<GetCompanyDetailsViewModel>(apiResponse);
}
else
{
//ViewBag.StatusCode = response.StatusCode;
TempData["msg"] = response.StatusCode;
}
}
}
return View(details);
}
how can i Assign this HTTPPOST method directly to my view
#model GetCompanyDetailsViewModel
#{
ViewBag.Title = "Account Page";
}
<html>
<head>
<style type="text/css">
header{display:none;}
body {
background-color: darkseagreen;
}
</style>
</head>
</html>
#if (Model != null)
{
<h2>Welcome to the web Application</h2>
#Model.bankAccount
#Model.bankName
#Model.email
}
Your view does not belong to your POST method. It belongs to GET method called Account(). So after your POST method finishes its work send data to GET method with RedirectToAction(nameOf(Account))

razor html to string using Quartz

I have been trying to obtain a html and translate the razor code in a string to send and email to multiple users. The emails are scheduled by Quartz and send to the users.
The mail is generating a link via #Url.Action. I notice that I don't have a Controller nor HttpContext at this point on my application. I have been trying a way to translate the razor code (RazorEngine and MvcMailer) to a string and sending in the email, but with no use because I cannot translate the #Url.Action and can't find a working package MvcMailer for Visual Studio 2017
Is there a way to possible do this?
Here is the template of the Email:
Hi #ViewBag.RecipientName,
Client that buy this item is #Model.ClientName
<p> <a href='#Url.Action("Index", "Item", new { ItemId = Model.ItemId}, Request.Url.Scheme)'>Click here to check the Item</a> </p>
#ViewBag.RecipientEmailAddress
Here is the email generator
public MailMessage GetMessage(EmailModel notification)
{
string BodyTemplate = ReplaceEmailBody(notification);
return new MailMessage
{
To = { "testuser#testdomain.com" },
Subject = DateTime.Now.ToString(),
IsBodyHtml = true,
Body = BodyTemplate
};
}
Here is my rubbish attempt to replace the razor:
private string ReplaceEmailBody(EmailModel notification)
{
string notificationBody = "";
notificationBody = Templates.Resources.MailTemplate;
notificationBody = notificationBody.Replace("#ViewBag.RecipientName", notification.RecipientName);
notificationBody = notificationBody.Replace("#ViewBag.RecipientEmailAddress", notification.RecipientEmailAddress);
notificationBody = notificationBody.Replace("#Model.CLIENT_NAME", notification.ClientName);
//Need to replace the Url.Action
}
All this code is running in a execute job of Quartz
I'm using Visual Studio 2017
As #Stanislav Nedeljkovic suggested, I put the Url on a config file and manage to create and HttpContext with it. Then I could translate the rest with RazorEngine.
var request = new HttpRequest("/", ConfigFile.Url, "");
var response = new HttpResponse(new StringWriter());
httpContext = new HttpContext(request, response);
var httpContextBase = new HttpContextWrapper(httpContext);
var routeData = new RouteData();
var requestContext = new RequestContext(httpContextBase, routeData);
var urlHelper = new UrlHelper(requestContext);
var url = urlHelper.Action("Index", "Item", new { ItemId = model.itemId});

MVC Email Issue

Here are my two actions for sending emails via MVC:
[HttpGet]
// GET: EmailForms
public ActionResult EmailForm(int id)
{
EmailFormModel emailModel = new EmailFormModel();
ChosenWT cwt = new ChosenWT();
OInfo person= new OInfo();
using (var db = new OWTEntities())
{
cwt = db.ChosenWTs.Find(id);
person= db.OInfoes.Find(cwt.OID);
}
emailModel.Message = "This is paragraph 1.\n\nThis is paragraph 2.\n\nThis is paragraph 3." // issue deals with this property
emailModel.FromEmail = User.Identity.Name.Split('\\')[1] + "#domain.com";
string Logon = Common.GetLogon(person.IBM);
emailModel.ToEmail = Logon + "#domain.com";
return View(emailModel);
}
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> EmailForm(EmailFormModel model)
{
if (ModelState.IsValid)
{
var body = "<p>Email From: {0} ({1})</p><p>Message:</p><p>{2}</p>";
var message = new MailMessage();
message.To.Add(new MailAddress(model.ToEmail));
message.From = new MailAddress(model.FromEmail);
message.Subject = "Test Program";
message.Body = string.Format(body, model.FromName, model.FromEmail, model.Message); // This doesn't keep the paragraph formatting like in the [HttpGet] Action.. Instead it just combines it all into 1 big paragraph..
message.IsBodyHtml = true;
using (var smtp = new SmtpClient())
{
smtp.Host = "smtp.test.server";
await smtp.SendMailAsync(message);
return RedirectToAction("Index", "OInfoes");
}
}
return View(model);
}
If you read the comments I put in the code above, that is the issue with this code.
I can format the Message so that when the user first comes to the page, the string is formatted into paragraph form.. but when I click Send, the HttpPost Action takes the message and ignores the \n\n and combines it all into one giant paragraph.
So I have tried to include <p></p> symbols into the string, but when I hit submit, I get an error stating that those characters are potentially dangerous.
Is there a way for the formatting to stay the same when i submit?
Any help is appreciated.
The reason why you are getting the error is, when a form is submitted, the asp.met mvc framework will inspect the request body to see whether it has any potentially dangerous content as HTML markup(Think about script injection). If it detects any dangerous content,the Request Validation module will throw an error. This is by design
You can apply AllowHtml attribute to the property which holds the html markup in your view model class so that the request validation framework won't reject the data submitted.
public class EmailFormModel
{
[AllowHtml]
public string Message { set;get;}
public string FromEmail {set;get;}
//Other properties of view model goes here
}

Razor View Page as Email Template

I have designed an Email Template from Razor Syntax. When I send this template as Email using C# code and SMTP protocol, I get bare Razor and HTML markups as Email Body. Am I wrong in this approach? Are Razor Pages allowed as Email Template?
Here is my Page
#inherits ViewPage
#{
Layout = "_Layout";
ViewBag.Title = "";
}
<div class="container w-420 p-15 bg-white mt-40">
<div style="border-top:3px solid #22BCE5"> </div>
<span style="font-family:Arial;font-size:10pt">
Hello <b>{UserName}</b>,<br /><br />
Thanks for Registering to XYZ Portal<br /><br />
<a style="color:#22BCE5" href="{Url}">Click to Confirm Email</a><br />
<br /><br />
Thanks<br />
Admin (XYZ)
</span>
Update..
using (StreamReader reader = new StreamReader(HttpContext.Current.Server.MapPath("~/ContentPages/EmailConfTemplate.cshtml")))
{
body = reader.ReadToEnd();
//Replace UserName and Other variables available in body Stream
body = body.Replace("{UserName}", FirstName);
}
Later On I am replacing the SMTP Code as ..
MailMessage message = new MailMessage(
ApplicationWideData.fromEmailId, // From field
ToEmailId, // Recipient field
"Click On HyperLink To Verify Email Id", // Subject of the email message
body
);
You do not need any special libraries to render a Razor view to a string in an ASP.NET MVC application.
Here is how you do it in MVC Core 3
public static class ViewToStringRenderer
{
public static async Task<string> RenderViewToStringAsync<TModel>(IServiceProvider requestServices, string viewName, TModel model)
{
var viewEngine = requestServices.GetRequiredService(typeof(IRazorViewEngine)) as IRazorViewEngine;
ViewEngineResult viewEngineResult = viewEngine.GetView(null, viewName, false);
if (viewEngineResult.View == null)
{
throw new Exception("Could not find the View file. Searched locations:\r\n" + string.Join("\r\n", viewEngineResult.SearchedLocations));
}
else
{
IView view = viewEngineResult.View;
var httpContextAccessor = (IHttpContextAccessor)requestServices.GetRequiredService(typeof(IHttpContextAccessor));
var actionContext = new ActionContext(httpContextAccessor.HttpContext, new RouteData(), new ActionDescriptor());
var tempDataProvider = requestServices.GetRequiredService(typeof(ITempDataProvider)) as ITempDataProvider;
using var outputStringWriter = new StringWriter();
var viewContext = new ViewContext(
actionContext,
view,
new ViewDataDictionary<TModel>(new EmptyModelMetadataProvider(), new ModelStateDictionary()) { Model = model },
new TempDataDictionary(actionContext.HttpContext, tempDataProvider),
outputStringWriter,
new HtmlHelperOptions());
await view.RenderAsync(viewContext);
return outputStringWriter.ToString();
}
}
}
In the controller
string str = await ViewToStringRenderer.RenderViewToStringAsync(HttpContext.RequestServices, $"~/Views/Emails/MyEmailTemplate.cshtml", new MyEmailModel { Prop1 = "Hello", Prop2 = 23 });
In ConfigureServices() in Startup.cs
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
Here is how you do it in MVC 5
public static class ViewToStringRenderer
{
public static string RenderViewToString<TModel>(ControllerContext controllerContext, string viewName, TModel model)
{
ViewEngineResult viewEngineResult = ViewEngines.Engines.FindView(controllerContext, viewName, null);
if (viewEngineResult.View == null)
{
throw new Exception("Could not find the View file. Searched locations:\r\n" + viewEngineResult.SearchedLocations);
}
else
{
IView view = viewEngineResult.View;
using (var stringWriter = new StringWriter())
{
var viewContext = new ViewContext(controllerContext, view, new ViewDataDictionary<TModel>(model), new TempDataDictionary(), stringWriter);
view.Render(viewContext, stringWriter);
return stringWriter.ToString();
}
}
}
}
Then, from the controller
ViewToStringRenderer.RenderViewToString(this.ControllerContext, "~/Views/Emails/MyEmailTemplate.cshtml", model);
After you have the email content, it is easy to send the email using MailMessage and SmtpClient.
Email messages only understand two formats: plain text and HTML. Since Razor is neither, it will need to be processed by some engine, so that it gives you back the generated HTML.
That's exactly what happens when you use Razor in ASP.NET MVC, behind the scenes. The Razor file is compiled into a internal C# class, that gets executed, and the result of the execution is the string content of the HTML, that gets sent to the client.
Your problem is that you want and need that processing to run, only to get the HTML back as a string, instead of being sent to the browser. After that you can do whatever you want with the HTML string, including sending it as an e-mail.
There are several packages that include this power, and I've used Westwind.RazorHosting successfully, but you can also use RazorEngine with similar results. I would prefer RazorHosting for standalone non-web applications, and RazorEngine for web applications
Here is a (sanitized) version of some of my code - I'm using Westwind.RazorHosting to send razor-formatted emails from a windows service, using a strongly typed view.
RazorFolderHostContainer host = = new RazorFolderHostContainer();
host.ReferencedAssemblies.Add("NotificationsManagement.dll");
host.TemplatePath = templatePath;
host.Start();
string output = host.RenderTemplate(template.Filename, model);
MailMessage mm = new MailMessage { Subject = subject, IsBodyHtml = true };
mm.Body = output;
mm.To.Add(email);
var smtpClient = new SmtpClient();
await smtpClient.SendMailAsync(mm);
Have you took a look at MVC Mailer?
It's a free package available from GitHub (https://github.com/smsohan/MvcMailer)
There is a step by step guide for it too https://github.com/smsohan/MvcMailer/wiki/MvcMailer-Step-by-Step-Guide
It's also on Nuget too. https://www.nuget.org/packages/MvcMailer
Essentially it will parse your razor view into html.
Check out a razor processor like RazorEngine (https://razorengine.codeplex.com/) which is available on NuGet. It processes razor to create an output, which is what you'd then use as the body of your email.
The Mailzory project is a valuable and convenient choice for sending emails which have Razor templates.
// template path
var viewPath = Path.Combine("Views/Emails", "hello.cshtml");
// read the content of template and pass it to the Email constructor
var template = File.ReadAllText(viewPath);
var email = new Email(template);
// set ViewBag properties
email.ViewBag.Name = "Johnny";
email.ViewBag.Content = "Mailzory Is Funny";
// send email
var task = email.SendAsync("mailzory#outlook.com", "subject");
task.Wait()
this project is hosted at Github. Also there is a nuget package available for Mailzory.

How do you convert a Razor view to a string?

I would like to use my Razor view as some kind of template for sending emails,
so I would like to "save" my template in a view, read it into controller as a string, do some necessary replacements, and then send it.
I have solution that works: my template is hosted somewhere as an HTML page, but I would like to put it into my application (i.e. in my view). I don't know how to read a view as a string in my controller.
I use the following. Put it on your base controller if you have one, that way you can access it in all controllers.
public static string RenderPartialToString(Controller controller, string viewName, object model)
{
controller.ViewData.Model = model;
using (StringWriter sw = new StringWriter())
{
ViewEngineResult viewResult = ViewEngines.Engines.FindPartialView(controller.ControllerContext, viewName);
ViewContext viewContext = new ViewContext(controller.ControllerContext, viewResult.View, controller.ViewData, controller.TempData, sw);
viewResult.View.Render(viewContext, sw);
return sw.GetStringBuilder().ToString();
}
}
Take a look at the RazorEngine library, which does exactly what you want. I've used it before for email templates, and it works great.
You can just do something like this:
// Read in your template from anywhere (database, file system, etc.)
var bodyTemplate = GetEmailBodyTemplate();
// Substitute variables using Razor
var model = new { Name = "John Doe", OtherVar = "Hello!" };
var emailBody = Razor.Parse(bodytemplate, model);
// Send email
SendEmail(address, emailBody);

Categories