Sending Emails with ActionMailer.Mvc in VB, Cannot Find View - asp.net-mvc

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();

Related

MVC email template engine .netcoreapp1.1

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);
}

Render string as MVC View

I have a simple Email Composer class where I get all Application Emails content from.
In this example, it sends the products to the email.
Now, I want to print them as well, and I'm trying to re-use the same method to grab the full html from the Email Composer and output it to a View.
the controller action
public ActionResult PrintRules()
{
var products = rep.ListAllProductsByCompanyId(currentCompany.company_id);
string body = mail.GetProductRules(products);
ViewBag.email = HttpUtility.HtmlEncode(body);
return View();
}
the view is:
#{
Layout = null;
string email = HttpUtility.HtmlDecode(ViewBag.email);
}
#Html.Raw(email)
<script>
window.print();
</script>
If I pass the body as a Model I do get errors on the parser, so I'm using the ViewBag instead.
as outputs:
#Html.Raw(email) will output nothing at all
#Html.Raw(email.Length) will output 17463
#email will output the code but the browser outputs it, does not parse it (image below)
What am I missing? I know it must be a really simple thing, but I'm totally blank...
browser output from using #email
Try this
#(new HtmlString(mystring))
or
#MvcHtmlString.Create(ViewBag.Stuff)

asp.net mvc validation must be a number custom error

I am new to asp.net and I have a problem. When the users insert in a editor for a decimal field something other than numbers, they get an error "Field name" is not a number. But I don't want them to receive this message I want them to receive another message. I have no problem with this with required and range validators.
Is there any way for me to do this?
I am not refering necessarily to changing the culture just displaying another message.
Thanks.
Hope I understand your, to change RangeValidator ErrorMessage just initialize ErrorMessage parameter:
[Range(0, 100, ErrorMessage = "Some another error message insert here!")]
[RegularExpression("\d", ErrorMessage = "!!!")]
public decimal DecimalField { get; set; }
This is the actual answer:
Create a class CustomClientDataTypeModelValidatorProvider. Copy the code from the MVC sources. Change the method MakeErrorString to output the appropiate message like this:
private static string MakeErrorString(string displayName)
{
return string.Format(
CultureInfo.CurrentCulture,
Core.Resources.Errors.EroareNuENr,
displayName);
}
I couldn't find a way not to copy the code just extend it as it uses this static method.
If anyone knows this please tell me.
Then, in global.asax, I wrote this:
var cdProvider = ModelValidatorProviders.Providers.SingleOrDefault(p => p.GetType().Equals(typeof(ClientDataTypeModelValidatorProvider)));
if(cdProvider != null)
{
ModelValidatorProviders.Providers.Remove(cdProvider);
ModelValidatorProviders.Providers.Add(
new CustomClientDataTypeModelValidatorProvider());
}
so that the flow would actually be routed to my class and not the class in the asp.net MVC dll
I got the idea from here:
Unfortunately this is is not a trivial task. However you can try the following hack...
Better to do this only on essential fields, as this is more code to maintain.
In the controller's action method
if(ModelState.IsValid)
{
// code
}
else
{
if (ModelState["YourField"].Errors.Count > 0)
{
ModelState["YourField"].Errors.Clear();
ModelState.AddModelError("YourField", "Your custom message here");
}
// code
}
You can set ResourceClassKey of ClientDataTypeModelValidatorProvider class to name of a global resource that contains FieldMustBeNumeric key to replace mvc validation error message of number with your custom message. Also key of date validation error message is FieldMustBeDate.
ClientDataTypeModelValidatorProvider.ResourceClassKey="MyResources"; // MyResource is my global resource
See here for more details on how to add the MyResources.resx file to your project:
The field must be a number. How to change this message to another language?
To change the error message you get after server side validation you need to change 'PropertyValueInvalid' key in your resource file and assign the resource file name to DefaultModelBinder.ResourceClassKey. See this question for details: localize default model validation in mvc 2
Look for solution at the end of this page:
http://jwwishart.wordpress.com/2010/03/22/custom-server-and-client-side-required-validator-in-mvc-2-using-jquery-validate/
I checked this in my MVC 3 RTM project and it works well.
... or use jQuery to change to message on the client.
A quick and simple hack for Customize RangeValidator ErrorMessage --"'Field name' is not a number"-- is using RegularExpression
[Range(0.5, 1000, ErrorMessage = "Amount should be in range {1} to {2}.")]
[DataType(DataType.Currency)]
[RegularExpression(#"\d", ErrorMessage = "Amount is not valid.")]
public decimal Amount{ get; set; }
You could implement your own custom validation attribute: http://haacked.com/archive/2009/11/19/aspnetmvc2-custom-validation.aspx
It seems that since Para's answer MVC evolved and now the ClientDataTypeModelValidatorProvider accepts a ResourceClassKey property. It uses the FieldMustBeNumeric and FieldMustBeNumeric messages specified in your resource class.

ASP.Net MVC routing legacy URLs passing querystring Ids to controller actions

We're currently running on IIS6, but hoping to move to IIS 7 soon.
We're moving an existing web forms site over to ASP.Net MVC. We have quite a few legacy pages which we need to redirect to the new controllers. I came across this article which looked interesting:
http://blog.eworldui.net/post/2008/04/ASPNET-MVC---Legacy-Url-Routing.aspx
So I guess I could either write my own route handler, or do my redirect in the controller. The latter smells slightly.
However, I'm not quite sure how to handle the query string values from the legacy urls which ideally I need to pass to my controller's Show() method. For example:
Legacy URL:
/Artists/ViewArtist.aspx?Id=4589
I want this to map to:
ArtistsController Show action
Actually my Show action takes the artist name, so I do want the user to be redirected from the Legacy URL to /artists/Madonna
Thanks!
depending on the article you mentioned, these are the steps to accomplish this:
1-Your LegacyHandler must extract the routes values from the query string(in this case it is the artist's id)
here is the code to do that:
public class LegacyHandler:MvcHandler
{
private RequestContext requestContext;
public LegacyHandler(RequestContext requestContext) : base(requestContext)
{
this.requestContext = requestContext;
}
protected override void ProcessRequest(HttpContextBase httpContext)
{
string redirectActionName = ((LegacyRoute) RequestContext.RouteData.Route).RedirectActionName;
var queryString = requestContext.HttpContext.Request.QueryString;
foreach (var key in queryString.AllKeys)
{
requestContext.RouteData.Values.Add(key, queryString[key]);
}
VirtualPathData path = RouteTable.Routes.GetVirtualPath(requestContext, redirectActionName,
requestContext.RouteData.Values);
httpContext.Response.Status = "301 Moved Permanently";
httpContext.Response.AppendHeader("Location", path.VirtualPath);
}
}
2- you have to add these two routes to the RouteTable where you have an ArtistController with ViewArtist action that accept an id parameter of int type
routes.Add("Legacy", new LegacyRoute("Artists/ViewArtist.aspx", "Artist", new LegacyRouteHandler()));
routes.MapRoute("Artist", "Artist/ViewArtist/{id}", new
{
controller = "Artist",
action = "ViewArtist",
});
Now you can navigate to a url like : /Artists/ViewArtist.aspx?id=123
and you will be redirected to : /Artist/ViewArtist/123
I was struggling a bit with this until I got my head around it. It was a lot easier to do this in a Controller like Perhentian did then directly in the route config, at least in my situation since our new URLs don't have id in them. The reason is that in the Controller I had access to all my repositories and domain objects. To help others this is what I did:
routes.MapRoute(null,
"product_list.aspx", // Matches legacy product_list.aspx
new { controller = "Products", action = "Legacy" }
);
public ActionResult Legacy(int catid)
{
MenuItem menuItem = menu.GetMenuItem(catid);
return RedirectPermanent(menuItem.Path);
}
menu is an object where I've stored information related to menu entries, like the Path which is the URL for the menu entry.
This redirects from for instance
/product_list.aspx?catid=50
to
/pc-tillbehor/kylning-flaktar/flaktar/170-mm
Note that RedirectPermanent is MVC3+. If you're using an older version you need to create the 301 manually.

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