MVC email template engine .netcoreapp1.1 - asp.net-mvc

I'm new with ASP.NET Core and I am trying to make emails, based on templates.
I want to include tags, which are then replaced with specific data from the controller.
I found a engine called razorengine that was exectly what I was looking for, but it is not compatible with .netcoreapp1.1. Are there other alternatives that I could use?
I am using Mailkit btw.
UPDATE:
I found a alternative called DotLiquid, Thanks anyways!

You may also use RazorLight which allows the same using the built-in Razor engine and runs with .Net Core.

You don't need to include an extra library for that.
Just create an html file that lies in your project scope and mark your placeholders e.g. via {{ ... }}
For example create a file wwwroot/emails/welcome.html:
Dear {{user}},
<p>Have fun with this page.</p>
<p>Please click here to activate your account.</p>
Best wishes
Now in your controller action:
public async Task<IActionResult> SendSomeEmailTo(userId)
{
string body;
using (var file = System.IO.File.OpenText("wwwroot/emails/welcome.html"))
{
body = file.ReadToEnd();
}
var user = this.userManager.findById(userId);
// replace your placeholders here
body = body
.Replace("{{user}}", user.getFullName())
.Replace("{{link}}", "www.foo.bar");
// use the email body in your email service of your choice
await sender.SendEmailAsync(user.Email, "Welcome", body);
}

Related

Swagger documentation: Swashbuckle (hide methods/properties)

I am using Swagger Swashbuckle to generate documentation. There are some methods in my controller and some properties in my models that I don't want to document.
Is there any arrtibute or the property to leave or ignore specific methods from documentation?
For the method, you have couple of option:
Use Obsolete attribute. Then, you have to set the action - c.IgnoreObsoleteActions(); within the swagger configuration
Create a custom attribute and a swagger document filter. The document filter should iterate through each method and remove the method documentation if the method is having the custom attribute
For the properties, you can use JsonIgnoreAttribute
In addition to c.IgnoreObsoleteActions(), there is also c.IgnoreObsoleteProperties(), which hides the property from the documentation.
JsonIgnoreAttribute will stop the property deserializing when being received as part of a POST request body, which may not be what you want if you only wish to change the documentation and not the functionality.
In more recent version of Swashbuckle (Core2/3) XmlIgnore/JsonIgnore don't seem to work for properties.
Alternatively you can change the property access modifier to internal. This should prevent serialization and generated documentation.
I'm not sure about hiding whole controllers, you will probably need to add filters in your Swagger setup. I do have an example of hiding certain endpoints (for convenience I have prefixed routes for running locally):
public void ConfigureServices(IServiceCollection services)
{
...
services.AddSwaggerGen(config => {
config.SwaggerDoc("v1",
new OpenApiInfo {
Version = "v1",
Title = "Foo API",
Description = "Does foo things.",
Contact = new OpenApiContact {
Name = "nope",
Email = "mail#example.org",
},
});
// Include XML comments in Swagger docs
var xmlFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml";
var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile);
config.IncludeXmlComments(xmlPath);
// Filter out prefixed routes
config.DocInclusionPredicate(
(name, desc) => !desc.RelativePath.ToLower().StartsWith("MyDevPrefix"));
});
}
Just a note since I was also trying to figure out the JsonIgnore for properties not working...
The issue seems to be that newer versions of Swashbuckle for .Net Core do not support NewtonSoft out of the box.
Install from NuGet
Package Manager : Install-Package Swashbuckle.AspNetCore.Newtonsoft -Version 5.6.2
CLI : dotnet add package --version 5.6.2 Swashbuckle.AspNetCore.Newtonsoft
Add code to startup.cs
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo { Title = "My API", Version = "v1" });
});
services.AddSwaggerGenNewtonsoftSupport(); // explicit opt-in - needs to be placed after AddSwaggerGen()
This worked for me, hope this helps someone else.
Here's a bit newer answer:
As other's mentioned - to ignore properties (both docs and real response) use attribute: [JsonIgnore]
To hide controller/actions from docs (the controller/action still exists, it is just hidden from docs) use attribute: [ApiExplorerSettings(IgnoreApi = true)]

MultiLingual Email template with grails

I am trying to send emails from grails and the mail template should be multilingual.
I found that we can render GSP as a string or even in the grails mail plugin we can render the GSP.
In the GSP now I read the static messages from messages.properties assuming that I would define for each languages and my emails would go multi lingual.
Now here is the problem that I am facing
In the template the language is always set to en_US. I am using the below API to get the string of the template. I am not using mail plugin directly as I need to store the send message as string into the database as well
def contents = groovyPageRenderer.render(template:"/layouts/emailparse", model:[mailObj: mailObj])
I also read on other post on forum about setting the language using lang parameter but still the language is set to en_US only.
Would the above method call support specifying language?
Is there an option to use velocity template to do this kind of multilingual mails?
If you're sending the mail from within a request handling thread (e.g. from a controller action) then it should pick up the right locale from the request automatically. If you're sending from a background thread then it won't know what locale to use because there's no "current request" context.
If you have another way to know the correct language to use (e.g. if you store each user's preferred language in the database) then you could reset the LocaleContextHolder
def savedContext = LocaleContextHolder.getLocaleContext()
LocaleContextHolder.setLocale(correctLocaleForThisUser)
try {
def contents = groovyPageRenderer.render(template:"/layouts/emailparse", model:[mailObj: mailObj])
// etc. etc.
} finally {
LocaleContextHolder.setLocaleContext(savedContext)
}
Exactly how you determine the correctLocaleForThisUser depends on your application. You could store each user's preferred language as a property of the User domain object in the database, or if you're using something like the executor plugin's runAsync from a controller action then you could save the request locale while you have access to it and then re-use that in the async task:
// SomeController.groovy
def sendEmail() {
// get locale from the thread-local request and save it in a local variable
// that the runAsync closure can see
Locale localeFromRequest = LocaleContextHolder.getLocale()
runAsync {
def savedContext = LocaleContextHolder.getLocaleContext()
// inject the locale extracted from the request
LocaleContextHolder.setLocale(localeFromRequest)
try {
def contents = groovyPageRenderer.render(template:"/layouts/emailparse", model:[mailObj: mailObj])
// etc. etc.
} finally {
LocaleContextHolder.setLocaleContext(savedContext)
}
}
}
Can you work around this by creating a model containing a list with the correct translations?
For example:
def messages = [:]
messages['hello.world'] = messageSource.getMessage(
"hello.world",
null,
new Locale("nb")
)
def template = groovyPageRenderer.render(
template: '/mail/email',
model:[messages:messages]
)
And then in the view you just write:
<html>
<head>
<title>${messages['hello.world']}</title>
</head>
<body>
</body>
</html>

Sending Emails with ActionMailer.Mvc in VB, Cannot Find View

The error I get when I try to send an email is:
NoViewsFoundException
You must provide a view for this email. Views should be named
~/Views/Email/VerificationEmail.html.vbhtml.txt.cshtml or
~/Views/Email/VerificationEmail.html.vbhtml.html.cshtml (or aspx for
WebFormsViewEngine) depending on the format you wish to render.
Error on line:
Return Email("~/Views/Email/VerificationEmail.html.vbhtml", model)
Can emails not be sent in .vbhtml, must they be sent in .cshtml? How can this work for VB?
Here is my code controller:
Imports ActionMailer.Net.Mvc
Public Class EmailController
Inherits MailerBase
Public Function VerificationEmail(ByVal model As RegisterModel) As EmailResult
[To].Add(model.Email)
From = "me#my.org"
Subject = "Thanks for registering with us!"
Return Email("~/Views/Email/VerificationEmail.html.vbhtml", model)
End Function
End Class
Here is my view:
#modelType MyBlog.RegisterModel
#Code
Layout = Nothing
End code
Welcome to My Cool Site, #Model.UserName
We need you to verify your email. Click this nifty link to get verified!
#Html.ActionLink("Verify", "Account", New With {.code = Model.Email})
Thanks!
After reading a couple of issues and answer, it could get it to work with this:
public override string ViewPath {
get { return AppDomain.CurrentDomain.BaseDirectory + #"\EmailTemplates\"; }
}
Of course you can have vbhtml email templates you just need to be careful with the naming (the .cshtmls exception message are hardcoded so don't be confused on it)
Your view is named correctly as VerificationEmail.html.vbhtml you just need remove all the prefixes from the view name in the Email call:
Return Email("VerificationEmail", model)
Because ActionMailer will be automatically add the prefixes and select the correct template for you.
Note that currently you cannot use relative viewnames like which start with ~ e.g. "~/Views/..." (I don't know wether this is a bug or feature).
So you need put your mail template to the regular view folders e.g.
/Views/{MailControllerName}/
/View/Shared/
Had the same issue as Chad Richardson. To solve the issue which happens when trying to send email from other area just add this code to Application_Start method:
var razorEngine = ViewEngines.Engines.OfType<RazorViewEngine>().First();
razorEngine.ViewLocationFormats = razorEngine.ViewLocationFormats.Concat(new string[]
{
"~/Areas/.../{0}.cshtml"
}).ToArray();

Best way to structure the code for an ASP.NET MVC REST API that is decoupled from the data formats?

I am creating a REST API in ASP.NET MVC. I want the format of the request and response to be JSON or XML, however I also want to make it easy to add another data format and easy to create just XML first and add JSON later.
Basically I want to specify all of the inner workings of my API GET/POST/PUT/DELETE requests without having to think about what format the data came in as or what it will leave as and I could easily specify the format later or change it per client. So one guy could use JSON, one guy could use XML, one guy could use XHTML. Then later I could add another format too without having to rewrite a ton of code.
I do NOT want to have to add a bunch of if/then statements to the end of all my Actions and have that determine the data format, I'm guessing there is some way I can do this using interfaces or inheritance or the like, just not sure the best approach.
Serialization
The ASP.NET pipeline is designed for this. Your controller actions don't return the result to the client, but rather a result object (ActionResult) which is then processed in further steps in the ASP.NET pipeline. You can override the ActionResult class. Note that FileResult, JsonResult, ContentResult and FileContentResult are built-in as of MVC3.
In your case, it's probably best to return something like a RestResult object. That object is now responsible to format the data according to the user request (or whatever additional rules you may have):
public class RestResult<T> : ActionResult
{
public override void ExecuteResult(ControllerContext context)
{
string resultString = string.Empty;
string resultContentType = string.Empty;
var acceptTypes = context.RequestContext.HttpContext.Request.AcceptTypes;
if (acceptTypes == null)
{
resultString = SerializeToJsonFormatted();
resultContentType = "application/json";
}
else if (acceptTypes.Contains("application/xml") || acceptTypes.Contains("text/xml"))
{
resultString = SerializeToXml();
resultContentType = "text/xml";
}
context.RequestContext.HttpContext.Response.Write(resultString);
context.RequestContext.HttpContext.Response.ContentType = resultContentType;
}
}
Deserialization
This is a bit more tricky. We're using a Deserialize<T> method on the base controller class. Please note that this code is not production ready, because reading the entire response can overflow your server:
protected T Deserialize<T>()
{
Request.InputStream.Seek(0, SeekOrigin.Begin);
StreamReader sr = new StreamReader(Request.InputStream);
var rawData = sr.ReadToEnd(); // DON'T DO THIS IN PROD!
string contentType = Request.ContentType;
// Content-Type can have the format: application/json; charset=utf-8
// Hence, we need to do some substringing:
int index = contentType.IndexOf(';');
if(index > 0)
contentType = contentType.Substring(0, index);
contentType = contentType.Trim();
// Now you can call your custom deserializers.
if (contentType == "application/json")
{
T result = ServiceStack.Text.JsonSerializer.DeserializeFromString<T>(rawData);
return result;
}
else if (contentType == "text/xml" || contentType == "application/xml")
{
throw new HttpException(501, "XML is not yet implemented!");
}
}
Just wanted to put this on here for the sake of reference, but I have discovered that using ASP.NET MVC may not be the best way to do this:
Windows Communication Foundation (WCF)
provides a unified programming model
for rapidly building service-oriented
applications that communicate across
the web and the enterprise
Web application developers today are
facing new challenges around how to
expose data and services. The cloud,
move to devices, and shift toward
browser-based frameworks such as
jQuery are all placing increasing
demands on surfacing such
functionality in a web-friendly way.
WCF's Web API offering is focused on
providing developers the tools to
compose simple yet powerful
applications that play in this new
world. For developers that want to go
further than just exposing over HTTP,
our API will allow you to access all
the richness of HTTP and to apply
RESTful constraints in your
application development. This work is
an evolution of the HTTP/ASP.NET AJAX
features already shipped in .Net 4.0.
http://wcf.codeplex.com/
However I will not select this as the answer because it doesn't actually answer the question despite the fact that this is the route I am going to take. I just wanted to put it here to be helpful for future researchers.

Is it possible to implement X-HTTP-Method-Override in ASP.NET MVC?

I'm implementing a prototype of a RESTful API using ASP.NET MVC and apart from the odd bug here and there I've achieve all the requirements I set out at the start, apart from callers being able to use the X-HTTP-Method-Override custom header to override the HTTP method.
What I'd like is that the following request...
GET /someresource/123 HTTP/1.1
X-HTTP-Method-Override: DELETE
...would be dispatched to my controller method that implements the DELETE functionality rather than the GET functionality for that action (assuming that there are multiple methods implementing the action, and that they are marked with different [AcceptVerbs] attributes). So, given the following two methods, I would like the above request to be dispatched to the second one:
[ActionName("someresource")]
[AcceptVerbs(HttpVerbs.Get)]
public ActionResult GetSomeResource(int id) { /* ... */ }
[ActionName("someresource")]
[AcceptVerbs(HttpVerbs.Delete)]
public ActionResult DeleteSomeResource(int id) { /* ... */ }
Does anybody know if this is possible? And how much work would it be to do so...?
You won't be able to use the [AcceptVerbs] attribute as-is since it's tied to the request's actual HTTP verb. Fortunately the [AcceptVerbs] attribute is very simple; you can see the source for yourself at http://www.codeplex.com/aspnet/SourceControl/changeset/view/21528#266431.
In short, subclass AcceptsVerbsAttribute and override the IsValidForRequest() method. The implementation would be something like the following:
string incomingVerb = controllerContext.HttpContext.Request.Headers["X-HTTP-Method-Override"] ?? controllerContext.HttpContext.Request.Method;
return Verbs.Contains(incomingVerb, StringComparer.OrdinalIgnoreCase);
Levi's answer is great. Additionally, I added a check in the custom AcceptsVerbsAttribute that also examines the FORM collection, so you can simply put a hidden input to trigger the DELETE (similar to MVC 2's Html.HttpMethodOverride(HttpVerbs.Delete)).
<input name="X-HTTP-Method-Override" type="hidden" value="DELETE" />
Change the incomingVerb assignment to:
string incomingVerb = controllerContext.HttpContext.Request.Headers["X-HTTP-Method-Override"] ?? controllerContext.HttpContext.Request.Form["X-HTTP-Method-Override"] ??controllerContext.HttpContext.Request.HttpMethod;
Be careful with this approach! See a related post by Stephen Walther.
Hopefully this helps someone.
Insert to Form:
<%= Html.HttpMethodOverride(HttpVerbs.Delete) %>
This conversation is a bit old, but I wanted to share what I have found using mvc 2:
Browsers support two HTTP verbs: GET and POST, but ASP.NET MVC 2 allows you to simulate Put, Get, and Delete using Html.HttpMethodOverride helper method. Internally, this works by sending the verb in an X-HTTP-Method-Override form field. The behavior of HttpMethodOverride is used by the [AcceptVerbs] attribute as well as the new shorter verb attributes:
For example, the action declaration:
[ActionName("someresource")]
[HttpDelete]
public ActionResult DeleteSomeResource()
should take responsibility for your get request that has the X-HTTP-Method-Override set to Delete.
I'm surprised that this hasn't been mentioned yet, but ASP.NET MVC natively supports X-HTTP-Method-Override and has been doing so from at least version 2. There's no need to write custom code to handle this.
It work in the following way:
Inside AcceptVerbsAttribute (also proxied by [HttpPut], [HttpPost], etc), there's an IsValidForRequest method. Inside that method, it checks with Request.GetHttpMethodOverride(), which returns the proper overriden HTTP method with the following conditions:
Overriding is only supported in POST requests. All others are ignored.
If the X-HTTP-Method-Override value is GET or POST, it's ignored. This makes sense, as you'd never need to override with these values.
It looks for X-HTTP-Method-Override in the following places in this priority:
1) HTTP Header
2) Form Body
3) Query String
If you're really curious, here's how GetHttpMethodOverride() looks (from MVC 3's source code):
public static class HttpRequestExtensions {
internal const string XHttpMethodOverrideKey = "X-HTTP-Method-Override";
public static string GetHttpMethodOverride(this HttpRequestBase request) {
if (request == null) {
throw new ArgumentNullException("request");
}
string incomingVerb = request.HttpMethod;
if (!String.Equals(incomingVerb, "POST", StringComparison.OrdinalIgnoreCase)) {
return incomingVerb;
}
string verbOverride = null;
string headerOverrideValue = request.Headers[XHttpMethodOverrideKey];
if (!String.IsNullOrEmpty(headerOverrideValue)) {
verbOverride = headerOverrideValue;
}
else {
string formOverrideValue = request.Form[XHttpMethodOverrideKey];
if (!String.IsNullOrEmpty(formOverrideValue)) {
verbOverride = formOverrideValue;
}
else {
string queryStringOverrideValue = request.QueryString[XHttpMethodOverrideKey];
if (!String.IsNullOrEmpty(queryStringOverrideValue)) {
verbOverride = queryStringOverrideValue;
}
}
}
if (verbOverride != null) {
if (!String.Equals(verbOverride, "GET", StringComparison.OrdinalIgnoreCase) &&
!String.Equals(verbOverride, "POST", StringComparison.OrdinalIgnoreCase)) {
incomingVerb = verbOverride;
}
}
return incomingVerb;
}
}
Have you looked at Simply Restful Routing? It already does this.
Edited Feb 2010 to add: Method overrides are built into MVC 2.
The X-HTTP-Method-Override is a custom header and most likely isn't supported by your web container.
Are you calling this from a web page? If so, you should probably use XmlHttpRequest with DELETE (or whatever verb you want). Better yet, use a JS framework to do the heavy lifting for you.
You could create an ActionFilter that implements OnActionExecuting, which fires before the controller action is invoked. You could then interrogate the request headers, and redirect based on the value of the X-HTTP-Method-Override header, when present.

Resources