Custom MVC Models and Controllers in Umbraco 7

By Stephen Garside on 1/9/2016

One of the great things about Umbraco 7 is the ability to create your own custom MVC controllers and models. In this article I am going to show you how to create a basic Contact form using MVC, C# and Umbraco 7.

First off you will need an MVC Controller to handle the incoming gets and posts.  In my example I have created a base controller to handle the calls to Umbraco so I can re-use the code for other controllers I may wish to create.  This base controller inherits PluginController - a base class provided by Umbraco.

using Examine;

using System;

using System.Linq;

using Umbraco.Core.Models;

using Umbraco.Web;

using Umbraco.Web.Mvc;

 

namespace Website.Controllers

{

    /// <summary>

    /// Base Controller

    /// </summary>

    public class BaseController : PluginController

    {

        /// <summary>

        /// Base Controller Constructor

        /// </summary>

        public BaseController()

            : this(UmbracoContext.Current)

        {

        }

 

        /// <summary>

        /// Base Controller Constructor

        /// </summary>

        /// <param name="umbracoContext">Umbraco Context</param>

        public BaseController(UmbracoContext umbracoContext)

            : base(umbracoContext)

        {

        }

 

        /// <summary>

        /// Get Published Content

        /// </summary>

        /// <param name="nodeName">Node Name</param>

        /// <returns>Published Content</returns>

        public IPublishedContent GetPublishedContent(string nodeName)

        {

            if (nodeName == null)

            {

                throw new ArgumentNullException("Node Name");

            }

 

            return Umbraco.TypedSearch(nodeName).First();

        }

    }

}

 

The next step is to create your Contact controller to handle the gets and posts. This new controller will inherit from your Base controller so you can utilise the GetPublishedContent method to search for Umbraco content. 

using System;

using System.Web.Mvc;

using Website.Models;

using Website.Models.Email;

 

namespace Website.Controllers

{

    /// <summary>

    /// Contact Controller

    /// </summary>

    public class ContactController : BaseController

    {

        /// <summary>

        /// Get Contact View

        /// </summary>

        /// <returns>Contact View</returns>

        [HttpGet]

        [Route("contact", Name = "Contact")]

        public ActionResult Contact()

        {

            return View("~/Views/Contact.cshtml",

                new ContactModel(GetPublishedContent("Contact")));

        }

 

        /// <summary>

        /// Post Contact View

        /// </summary>

        /// <param name="model">Contact Post Model</param>

        /// <returns>Redirect to Thankyou or Error</returns>

        [HttpPost]

        [Route("contact", Name = "ContactPost")]

        public ActionResult Contact(ContactPostModel model)

        {

            if (ModelState.IsValid)

            {

                try

                {

                    var emailModel = new ContactUsEmailModel()

                    {

                        EmailAddress = model.Email,

                        Name = model.Name,

                        Message = model.Message,

                        ContactDateTime = DateTime.Now

                    };

 

                    new EmailController().ContactUsEmail(emailModel).Send();

 

                    return RedirectToRoute("ContactThanks");

                }

                catch (Exception ex)

                {

                    // Somethings gone wrong

                    ModelState.AddModelError(

                        string.Empty, "Sorry, an error occurred, please retry");

                }               

            }

 

            return View("~/Views/Contact.cshtml",

                new ContactModel(GetPublishedContent("Contact")));

        }

 

        /// <summary>

        /// Get Contact Thanks View

        /// </summary>

        /// <returns>Contact View</returns>

        [HttpGet]

        [Route("contact/thankyou", Name = "ContactThanks")]

        public ActionResult ContactThanks()

        {

            return View("~/Views/ContactThanks.cshtml",

                new GeneralModel(GetPublishedContent("ContactThanks")));

        }

    }

}

So, lets have a look in a little more detail at our Get method first of all:-

        /// <summary>

        /// Get Contact View

        /// </summary>

        /// <returns>Contact View</returns>

        [HttpGet]

        [Route("contact", Name = "Contact")]

        public ActionResult Contact()

        {

            return View("~/Views/Contact.cshtml",

                new ContactModel(GetPublishedContent("Contact")));

        }

The first thing is to define your route - this is done by implementing MVC5 Attribute Routing for Umbraco 7.

We then initialise a new custom mvc model (called Contact model) that will be used to build and validate the view.  The constructor for the Contact model takes the Umbraco published content for the Contact page (which you need to create in your instance of Umbraco) e.g. 

contact page in umbraco

And below is the code for the Contact model. This model should inherit from the Umbraco Render Model object, and pass through the Published Content from Umbraco for the page to render correctly.  

The great thing now is that we can use MVC data annotation attributes in order to validate our model both server and client-side.

using System.ComponentModel.DataAnnotations;

using System.Globalization;

using Umbraco.Core.Models;

using Umbraco.Web.Models;

 

namespace Website.Models

{

    /// <summary>

    /// Contact Model

    /// </summary>

    public class ContactModel : RenderModel

    {

        /// <summary>

        /// Contact Model Constructor

        /// </summary>

        /// <param name="content">Umbraco Published Content</param>

        public ContactModel(IPublishedContent content)

            : base(content, CultureInfo.CurrentUICulture) { }

 

        /// <summary>

        /// Gets or sets Name

        /// </summary>

        [Required(ErrorMessage = "Please provide your Full Name")]

        public string Name { get; set; }

 

        /// <summary>

        /// Gets or sets Email

        /// </summary>

        [Required(ErrorMessage = "Email Address is required")]

        public string Email { get; set; }

 

        /// <summary>

        /// Gets or sets Message

        /// </summary>

        [Required(ErrorMessage = "At least give me a message ;)")]

        public string Message { get; set; }

    }

}

The last piece of the jigsaw is the MVC Razor View.  This view inherits from UmbracoViewPage (as opposed to UmbracoTemplatePage) because our model inherits from the Umbraco Render Model.  The rest of the view is just 'standard' Razor.  I have chosen not to use MVC Html helpers to create the input controls - that is just a personal choice, however I have used the Html.ValidationSummary helper to spit out clientside model validation.  

Thats pretty much it for the Http Get side of things!

@using Website.Models

@inherits UmbracoViewPage<ContactModel>

 

@{

    Layout = "Master.cshtml";

}

 

<div class="container">

    <div class="shadow-box icon-shadow">

        <div class="contact-header clearfix">

            <div class="title">

                <h1>@Umbraco.Field("pageTitle")</h1>

                <p class="last">Get in touch with me by completing the following form. I always try and reply within 48 hours.</p>

            </div>

            <img class="stamp" src="~/images/website/contact-stephen-garside-stamp.jpg" alt="contact Stephen Garside" />

        </div>

        <form class="validate solid" method="post" action="~/contact/" novalidate="novalidate">

            <div class="form-group">

                <input type="text" id="Name" name="Name" class="form-control inp-bck" placeholder="Your Name" required />

            </div>

            <div class="form-group">

                <input type="email" id="Email" name="Email" class="form-control inp-bck" placeholder="Email Address" required />

            </div>

            <div class="form-group">

                <textarea id="Message" name="Message" class="form-control" placeholder="Your Message" required></textarea>

            </div>

            <div>

                @Html.ValidationSummary()

            </div>

            <button type="submit" class="btn btn-cta">Send</button>

        </form>

    </div>

</div>

To handle the postback of the Contact form we create a Contact Post model. This model does not inherit from the Umbraco Render Model and contains only the information we expect to be posted back together with the Data Annotations for model validation.

using System.ComponentModel.DataAnnotations;

 

namespace Website.Models

{

    /// <summary>

    /// Contact Post Model

    /// </summary>

    public class ContactPostModel

    {

        /// <summary>

        /// Gets or sets Name

        /// </summary>

        [Required(ErrorMessage="Please provide your Full Name")]

        public string Name { get; set; }

 

        /// <summary>

        /// Gets or sets Email

        /// </summary>

        [Required(ErrorMessage = "Email Address is required")]

        public string Email { get; set; }

 

        /// <summary>

        /// Gets or sets Message

        /// </summary>

        [Required(ErrorMessage = "At least give me a message ;)")]

        public string Message { get; set; }

    }

}

You may have noticed in the code for the Contact Controller that we have a method that accepts the form post:-

        /// <summary>

        /// Post Contact View

        /// </summary>

        /// <param name="model">Contact Post Model</param>

        /// <returns>Redirect to Thankyou or Error</returns>

        [HttpPost]

        [Route("contact", Name = "ContactPost")]

        public ActionResult Contact(ContactPostModel model)

        {

            if (ModelState.IsValid)

            {

                try

                {

                    var emailModel = new ContactUsEmailModel()

                    {

                        EmailAddress = model.Email,

                        Name = model.Name,

                        Message = model.Message,

                        ContactDateTime = DateTime.Now

                    };

 

                    new EmailController().ContactUsEmail(emailModel).Send();

 

                    return RedirectToRoute("ContactThanks");

                }

                catch (Exception ex)

                {

                    // Somethings gone wrong

                    ModelState.AddModelError(

                        string.Empty, "Sorry, an error occurred, please retry");

                }               

            }

 

            return View("~/Views/Contact.cshtml",

                new ContactModel(GetPublishedContent("Contact")));

        }

This really is the final piece of the jigsaw. This method accepts type ContactPostModel and simply validates the incoming model and redirects the user to a custom thank you page, or returns the contact view / model to highlight model validation errors.

And that's all there is to creating Umbraco Custom MVC Model and Controllers :) - I hope you find this information useful and feel free to leave me your comments and thoughts!

Share this article...
Join the Discussion...