Umbraco MVC: What on earth is a surface controller?

Continuing the Umbraco MVC blog post series we will look at Surface controllers in this post and find out what the heck they are why they are so important when building Umbraco MVC websites.

Read on to find out how to implement your very own surface controllers.

So what the heck is a surface controller?

A surface controller is an Umbraco specific version of a normal MVC controller with the added benefits of the surface controller knowing about your Umbraco site.

So why should I care about SurfaceControllers?

Without surface controllers, we would be unable to have any form of interactivity on our website. For example such as contact forms, login form, edit profile form and many other various types of forms where we accept input from a user. Without a surface controller none of this would be possible. A surface controller is a bit more powerful than a normal MVC controller as it knows a lot more about Umbraco, the current page and other contexts than a regular old ASP.NET MVC controller would not know.

Show me the code!

As always with these blog posts I try not to ramble on too much and rather just show you how to do things with heavily commented code examples rather than a ton of theory that no one can ever remember!

In this example the form we will use in our surface controller example is a simple contact form. Posting a name, email address and a message which we will send out as a message.
So lets cover the basics.

A surface controller comprises of three things, a model or to be more precise a view model for our form data/fields, a controller in our case a surface controller and finally a view to display the form and success message.

So lets start off with the very basics and create a view model for our form. You will need a form of Visual Studio for this be it the free express edition or any other addition as this code will need to be compiled into a dll for the website. So right click and create a new folder called Models and in there create a new class called ContactFormViewModel.cs

In this file we can add the fields or properties to this class that we want to show up in our contact form.

public class ContactFormViewModel
{
   public string Name { get; set; }

   public string Email { get; set; }

   public string Message { get; set; }
}

As you can see the class is very simple with three string properties. We can make our lives easier by adding validation by using a native ASP.NET MVC data attributes for validation.

So lets take a look at the updated model with our data attributes added in.

public class ContactFormViewModel
{
   [Required]
   public string Name { get; set; }

   [Required]
   [EmailAddress]
   public string Email { get; set; }

   [Required]
   public string Message { get; set; }
}

Step two, lets create our actual surface controller. Create a folder called Controllers and in there create a new class called ContactFormSurfaceController.cs for this to automagically work and wire into Umbraco, the file needs to always end in SurfaceController.cs.This class needs to inherit from SurfaceController (Umbraco.Web.Mvc.SurfaceController)

public class ContactFormSurfaceController : SurfaceController
{
   //Our ActionResult code will go here (to handle the view & one to handle the form POST)
}

We next need to add an ActionResult into our controller. Which will handle the form rendering on our page. So lets add the following to our controller.

/// <summary>
/// Renders the Contact Form
/// @Html.Action("RenderContactForm","ContactFormSurface");
/// </summary>
/// <returns></returns>
public ActionResult RenderContactForm()
{
    //Return a partial view ContactForm.cshtml in /views/ContactFormSurface/ContactForm.cshtml
    //With an empty/new ContactFormViewModel
    return PartialView("ContactForm", new ContactFormViewModel());
}

With this ActionResult in place, we need a second one to deal with the contact form HTTP POST method.

[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult HandleContactForm(ContactFormViewModel model)
{
    //Check if the dat posted is valid (All required's & email set in email field)
    if (!ModelState.IsValid)
    {
        //Not valid - so lets return the user back to the view with the data they entered still prepopulated
        return CurrentUmbracoPage();
    }

    //Generate an email message object to send
    MailMessage email   = new MailMessage(model.Email, "you@yoursite.co.uk");
    email.Subject       = "Contact Form Request";
    email.Body          = model.Message;

    try
    {
        //Connect to SMTP credentials set in web.config
        SmtpClient smtp = new SmtpClient();

        //Try & send the email with the SMTP settings
        smtp.Send(email);
    }
    catch (Exception ex)
    {
        //Throw an exception if there is a problem sending the email
        throw ex;
    }

    //Update success flag (in a TempData key)
    TempData["IsSuccessful"] = true;

    //All done - lets redirect to the current page & show our thanks/success message
    return RedirectToCurrentUmbracoPage();
}

Hopefully the code is heavily commented enough to understand what is going on.

The final piece of our puzzle is that we need a partial view to render our contact form to allow our users to view and post a message via our contact form.

@using ExampleSite.Controllers
@using ExampleSite.Models
@model ContactFormViewModel

@{
    Html.EnableClientValidation(true);
    Html.EnableUnobtrusiveJavaScript(true);
}

@if (Convert.ToBoolean(TempData["IsSuccessful"]))
{
    <h1>YAY!</h1>
    <p>Thanks for sending your message, we will get back to you shortly.</p>
}
else
{
    using (Html.BeginUmbracoForm<ContactFormSurfaceController>("HandleContactForm"))
    {
        @Html.ValidationSummary(true)
        @Html.AntiForgeryToken()

        @Html.TextBoxFor(model => model.Name, new { placeholder = "Your Name" })
        @Html.ValidationMessageFor(model => model.Name)

        @Html.TextBoxFor(model => model.Email, new { placeholder = "Email Address" })
        @Html.ValidationMessageFor(model => model.Email)

        @Html.TextBoxFor(model => model.Message, new { placeholder = "Your Message" })
        @Html.ValidationMessageFor(model => model.Message)

        <input type="submit" value="Send" />
    }
}

We could easily expand on this example by using the knowledge learnt from the PetaPoco tutorial blog post I wrote earlier to create a DB table to store the contact form entries as a log, and then create a simple dashboard control to view these items in the Umbraco back office.

In the future with Umbraco belle aka V7 coming up in the future we could use the notification service and signalR to give a live notification that the contact form has been filled in when a user to the Umbraco back-office is logged in.

Next time I will be looking into other things we can do with surface controllers such as membership, with registration, login, forgotten password and reset password options.

27 thoughts on “Umbraco MVC: What on earth is a surface controller?

  1. Warren,
    I have written some custom data annotations where you can pass in umbraco dictionary key so your model labels validation messages can be localised. Will release at some point

      1. Hey Warren,

        It is pretty easy to make some new validation (just inherit from ValidationAttribute), have a bunce of them custom made for “validating forms in cms context” Most of them are ModelAware properties so lets say u have a model with a string prop called Named and bool prop called IsNameReq. then u can add a attribute to Name that points to IsNameReq, and based on that value the Name prop is either req or not. winwin

        But yesterday i had a model with over 40 props on and 4 modelaware props for each equals blowed model 🙂 so i deepdived into DataAnnotationsModelValidatorProvider and now i just need to define my fields in umbraco and the validation context knows about the currentpage in umbraco and gets values from there. so no more blowed data models just plain umbraco fields and attributes. it is allready on bitbucket so i can share it with u if u wanner take a closer look.

        usage:
        [UmbracoRequiredField(“isNameRequired”, ErrorMessageField = “nameErrorMessage”)]
        public string Name { get; set; }

      2. Hey Troel,
        Package looks good, but is OK to point me to the source code instead please. As would be interested in how you achieved this.

        Thanks,
        Warren 🙂

  2. Good write up Warren. I’m writing a post a the moment that might complement this on keeping controllers skinny. “Damn business logic get out of my controller”. As Umbraco devs I find we can be way to lazy and put too much “logic” in the controllers but it ultimately makes the app more difficult to manage. Anyway will let you know when I have finished writing it. 🙂

    1. Look forward to reading it mate.
      I agree with your approach to try and make them lightweight and move things like email stuff into a helper class. Common node collections into a retrieval class or something.

      But for the purpose of this tutorial and for an absolute beginner its probably best to put all logic in the controller. Rather than explain in a beginners tutorial that I have abstracted things away into our helper or extension based classes.

      But like I said before I totally agree with your approach on a day to day basis

  3. Nice blog post :). I don’t think that a surface controller name needs to end with SurfaceController. Just let it inherit from SurfaceController should be good enough. These days I’m using a SurfaceController for both forms and route hijacking so all logic can happen in 1 controller. More info here: http://issues.umbraco.org/issue/U4-2342

  4. Thanks Warren, very helpful. Useful to realise that convention allows you to have a subfolder in Views for the controller-specific view, rather than just putting in Views/Partials.

    I got the following error however:

    The required anti-forgery form field “__RequestVerificationToken” is not present

    Which was solved by moving the @Html.AntiForgeryToken() line to be inside the form.

    1. Thanks Lotte.
      Yes I noticed the same mistake last night in my code. Will update it shortly but also thanks for pointing it out to anyone else reading this in the meantime.

    1. Ok so I wasn’t in MVC mode for default render (from the umbraco.config) which was basically causing my confusion. Also bit of trial and error on where exactly to upload the view.

  5. Great article! This really helped me out. However, I think you should add how to include the custom partial view into another view, since it’s using a custom model. I used this way and it seems to work, not sure if it’s best practice though.
    @{ Html.RenderPartial(“ContactFormSurface/ContactForm”, new ContactFormViewModel()); }

    Thanks,
    Daniel

  6. Great post, thanks for sharing.
    I followed the instructions, but as radavies mentioned, I cannot get the page to load. I have break points at the controller, but they never get triggered, so I guess there is some configuration I’m missing. I checked in umbracoSettings.config and the render method states: “Mvc”. The url I’m using is “http://localhost:21290/contactform”, “http://localhost:21290/contactformsurface”, but none of them work. Is there anything else I should try?
    Best regards, Ariel

  7. Hi, I was doing Umbraco starting from last week. I was finding email implementation for my code. However, I was not sure about the last picture of code where store to and how to render the contact form. Can you able to show me the full code as reference. Thanks for help.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s