Friday, March 05, 2010

Contact Form with ASP.NET MVC, Castle Validation, & fluentHtml




Here’s another little tutorial for those of you new to MVC &/or fluentHtml. I figure I’ll do these little tutorials until I find a new project to work on. I do have a couple of ideas…so we’ll see how those turn out.

Let’s get started.

We’ll start with the view model class again like last week. By the way, if you’re not familiar with the way I setup my MVC projects, see this post.

public class ContactView
{
[
ValidateNonEmpty("Name")]
public string Name { get; set; }
[
ValidateNonEmpty("Email"), ValidateEmail("Valid Email")]
public string Email { get; set; }
public string Subject { get; set; }
[
ValidateNonEmpty("Message")]
public string Message { get; set; }
}

The ValidateNonEmpty and ValidateEmail are attributes from the Castle project and I’m passing in the error message I want displayed if invalid. Alrighty, let’s setup our controller action and the view (html).

Controller Action:

public class ContactController : Controller
{
public ActionResult Index()
{
return View(new ContactView());
}
}

View/HTML Markup

<%@ Page Title="Contact Us" Language="C#"
MasterPageFile="~/Views/Shared/Site.Master"
Inherits="MvcContrib.FluentHtml.ModelViewPage<ContactView>" %>

<asp:Content ContentPlaceHolderID="MainContent" runat="server">
<
h2>Contact Us</h2>
<% using(Html.BeginForm("Index", "Contact", FormMethod.Post)) {%>
<%
=Html.DivSuccessMessage("Message sent successfully!")%>
<%
=Html.DivValidationSummary("Please see the items below:")%>
<fieldset>
<
legend>Proposal Form</legend>
<%=this.TextBox(f => f.Name).Label("Name:")%>
<%
=this.TextBox(f => f.Email).Label("Email:")%>
<%
=this.TextBox(f => f.Subject).Label("Subject:")%>
<%
=this.TextArea(f => f.Message).Label("Message:")%>
<%
=Html.AntiForgeryToken() %>
<%
=this.SubmitButton("Send").Attr("accesskey", "S").Class("sb") %> All fields are required.
</
fieldset>
<% } %>
</asp:Content>

This is a simple form with fluentHtml. If you need a tutorial on fluentHtml, here’s a post on it. You should notice that this page inherits from the MvcContrib ModelViewPage with our ContactView from above being passed in. I typically create my own base page that inhertis from the MvcContrib one, but to make this post easier, I just inherit straight from their’s.

Okay, let’s get to the meet where we accept the post in the controller.

[AcceptVerbs(HttpVerbs.Post), ValidateModel(typeof(ContactView)), ValidateAntiForgeryToken]
public ActionResult Index(ContactView form)
{
if (!ModelState.IsValid)
return View(form);

try
{
var notificationService = DI.EmailNotificationService(new EmailNotification(form));
notificationService.Notify();

Success();
}
catch (NotificationException)
{
ModelState.AddModelError(
"notifyerror"
, "Unfortunately, something went wrong with the message delivery. Please try again.");
}

return View(form);
}

Okay, so we’re only accepting a post, we’re validating our model with a validation filter, and validating the anti-forgery token. I’m also accepting my ContactView model. If the model state isn’t valid, I return the model back to the view. Otherwise, I try to create a new instance of EmailNotificationService (which returns an INotificationService) then I call the Notify(). If I don’t receive a NotificationException, then I call Success() on my SmartController (which is what this controller inherits from) Success(); simply does this: ViewData["success"] = ""; You can read more about it in this post. If the Notify() bombs, we add a model error and then finally we return the form to the view. If you wanted to clear out the form, you’d put return View(new ContactView()) right after the Success() call.

So what does the INotificationService look like? Well, this:

public interface INotificationService
{
void Notify();
}

The implementation looks like this:

public class EmailNotificationService : INotificationService
{
private readonly IEmailNotification _emailNotification;

public EmailNotificationService(IEmailNotification emailNotification)
{
_emailNotification = emailNotification;
}

public void Notify()
{
using (var mail = new MailMessage())
{
mail.To.Add(_emailNotification.To);
mail.From =
new MailAddress(_emailNotification.From);
mail.Subject = _emailNotification.Subject;
mail.Body = _emailNotification.Body;
mail.IsBodyHtml =
true;

try
{
mail.Send();
}
catch (SmtpException)
{
throw new NotificationException();
}
}
}
}

NOTE: You can also try using AutoMapper to map to the MailMessage, but for whatever reason, it was not working out for me. I send Jimmy and email about it, but he’s busy enough and it’s not that big a deal to do the left=right thing every once in a while :)

My IEmailNotification looks like this:

public interface IEmailNotification
{
string To { get; }
string From { get; }
string Subject { get; }
string Body { get; }
}

I’ll show the implementation in a sec. First I wanted to show what the NotificationException looks like…

public class NotificationException : Exception
{
}

That’s it…I can adjust as needed, but for this…there’s no reason really. I just want to be able to catch the type in my controller. Yes you could do catch(SmtpException) in the controller, but what if your next implementation of INotificationService doesn’t use smtp and you have catch(SmtpException) spread all through your other code? It wouldn’t be a good thing. Let’s keep truckin…

Here’s the implementation of IEmailNotification:

public class EmailNotification : IEmailNotification
{
private readonly ContactView _view;

public EmailNotification(ContactView view)
{
_view = view;
}

public string To
{
get { return "email@email.com"; }
}

public string From
{
get { return _view.Email; }
}

public string Subject
{
get { return _view.Subject; }
}

public string Body
{
get { return _view.Message; }
}
}

You should pull email@email.com from some type of config file since it could potentially change often. What’s cool about this is that now I could have different versions for nighttime daytime or certain months or whatever…I don’t know why you’d ever do that, but it’s getting late and I can’t think of better examples :)

Anyhow…you can also customize the body here and not have to worry about formatting in your controller. You could do something like this for the Body:

public string Body
{
get
{
var body = new StringBuilder();
body.AppendFormat(
"Date Message Sent: {0}", DateTime.Now);
body.AppendFormat(
"Sent from IP: {0}", HttpContext.Current.Request.UserHostAddress);
body.AppendFormat(
"Browser Used: {0} v{1}", HttpContext.Current.Request.Browser.Browser,
HttpContext.Current.Request.Browser.Version);
return _view.Message;
}
}

I hope I didn’t leave anything out…if I did, please let me know!

Thanks for reading!


Shout it

kick it on DotNetKicks.com

blog comments powered by Disqus
Related Posts Plugin for WordPress, Blogger...