Form validation with ASP.NET MVC preview 5

In earlier ASP.NET MVC previews, form validation was something that should be implemented "by hand". Since the new ASP.NET MVC preview 5, form validation has become more handy. Let me show you how you can add validation in such a ridiculously easy manner.

Here's an example controller:

[code:c#]

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Mvc.Ajax;

namespace ValidationExample.Controllers
{
    [HandleError]
    public class HomeController : Controller
    {
        // ... some other action methods ...

        [AcceptVerbs("GET")]
        public ActionResult Contact()
        {
            return View();
        }

        [AcceptVerbs("POST")]
        public ActionResult Contact(string name, string email, string message)
        {
            // Add data to view
            ViewData["name"] = name;
            ViewData["email"] = email;
            ViewData["message"] = message;

            // Validation
            if (string.IsNullOrEmpty(name))
                ViewData.ModelState.AddModelError("name", name, "Please enter your name!");
            if (string.IsNullOrEmpty(email))
                ViewData.ModelState.AddModelError("email", email, "Please enter your e-mail!");
            if (!string.IsNullOrEmpty(email) && !email.Contains("@"))
                ViewData.ModelState.AddModelError("email", email, "Please enter a valid e-mail!");
            if (string.IsNullOrEmpty(message))
                ViewData.ModelState.AddModelError("message", message, "Please enter a message!");

            // Send e-mail?
            if (ViewData.ModelState.IsValid)
            {
                // send email...
                return RedirectToAction("Index");
            }
            else
            {
                return View();
            }
        }
    }
}

[/code]

You may notice an starnge thing here... Why is Contact defined twice, and why is it with this strange AcceptVerbs attribute? The AcceptVerbs attribute determines which action method to call, based on the HTTP method of the request. In this case, when I do not post a form, the first action method will be called, simply rendering a view. When posting a form, the second action method will be called, allowing me to do some validations.

Speaking of validations... Notice that I can set errors on the ViewData.ModelState collection, and use this ViewData.ModelState.IsValid property to check if everything is OK.

UPDATE: You can also use the controller's UpdateModel method (which updates a model object with form values) for setting data on the model. If the model throws an exception, this will be added to the ViewData.ModelState dictionary too.

One thing left with validation: the view itself!

[code:html]

<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" AutoEventWireup="true" CodeBehind="Contact.aspx.cs" Inherits="ValidationExample.Views.Home.Contact" %>

<asp:Content ID="Content1" ContentPlaceHolderID="MainContent" runat="server">
    <h2>Contact Us</h2>
    <p><%=Html.ValidationSummary()%></p>
    <% using (Html.Form<ValidationExample.Controllers.HomeController>( c => c.Contact("", "", ""), FormMethod.Post)) { %>
        <table border="0" cellpadding="2" cellspacing="0">
            <tr>
                <td>Name:</td>
                <td>
                    <%=Html.TextBox("name", ViewData["name"] ?? "")%>
                    <%=Html.ValidationMessage("name")%>
                </td>
            </tr>
            <tr>
                <td>Email:</td>
                <td>
                    <%=Html.TextBox("email", ViewData["email"] ?? "")%>
                    <%=Html.ValidationMessage("email")%>
                </td>
            </tr>
            <tr>
                <td colspan="2">Message:</td>
            </tr>
            <tr>
                <td colspan="2">
                    <%=Html.TextArea("message", ViewData["message"] ?? "")%>
                </td>
            </tr>
            <tr>
                <td colspan="2">
                    <%=Html.ValidationMessage("message")%>
                </td>
            </tr>
            <tr>
                <td>&nbsp;</td>
                <td>
                    <%=Html.SubmitButton("send", "Send e-mail")%>
                </td>
            </tr>
        </table>
    <% } %>
</asp:Content>

[/code]

Notice that there are 2 new HtmlHelper extension methods: ValidationMessage and ValidationSummary. The first one displays a validation message for one key in the ViewData.ModelState collection, while the latter displays a validation summary of all messages. Here's what my invalid post looks like:

Validation example

kick it on DotNetKicks.com

This is an imported post. It was imported from my old blog using an automated tool and may contain formatting errors and/or broken images.

Leave a Comment

avatar

13 responses

  1. Avatar for Troy Goode
    Troy Goode August 29th, 2008

    Looks like an improvement, but it really is only half a solution without any way to propagate the rules down for client-side validation. Hopefully they'll do something with that in Preview 6?

  2. Avatar for maartenba
    maartenba August 29th, 2008

    Agree with that, but it is a step forward. Bit strange they did not think of client-side validation immediately too?

  3. Avatar for Simone Chiaretta
    Simone Chiaretta August 29th, 2008

    That's a good thing... I don't think these things have been added to the readme file of P5

  4. Avatar for maartenba
    maartenba August 29th, 2008

    There's even more undocumented goodies in there :-) Will post one more next monday.

  5. Avatar for Jesse
    Jesse August 30th, 2008

    I'm not sure about the pessimists above. I would just use Ajax to post to the action you nicely summarized above and I would have client side validation.
    This is very cool.

  6. Avatar for Sean
    Sean August 30th, 2008

    Ok, stupid question. What are those HTML helper methods for? Do they render as client-side JavaScript validation?

  7. Avatar for Brad Wilson
    Brad Wilson August 31st, 2008

    This:

    <% using (Html.Form<ValidationExample.Controllers.HomeController>( c => c.Contact("", "", ""), FormMethod.Post)) { %>

    could just be:

    <% using (Html.Form()) { %>

  8. Avatar for maartenba
    maartenba September 1st, 2008

    @Sean: these helper methods simply render some HTML tags. For example, HtmlHelper.Form() will render a <form> tag, HtmlHelper.ValidationSummary() will render an <ul> with validation messages.

    @Brad: True! Thanks for noticing. Will fix in this post.

  9. Avatar for Mike
    Mike September 3rd, 2008

    Client side validation is possible, you have to look for a validation framework that uses attributes. These attributes can be inspected, and correct javascript can be written to the page based on them (such as required, compare, regex).

    But you have to get the name of the form element coupled with the property of your model class somehow. Hopefully the modelbinder is capable of that.

  10. Avatar for nishzone
    nishzone December 7th, 2008

    I'm created an mvc project with the first beta release and automatically the AccountController had generated ModelState.AddModelError like your example. When I updated to preview 5 from codeplex, I could no longer access ModelState.AddModelError from System.web.mvc.dll....How come you don't seem to be having this issue? Am I missing something?

  11. Avatar for maartenba
    maartenba December 8th, 2008

    I would say: stick with beta 1 as it is a newer version than preview 5.

  12. Avatar for Naresh Yadav
    Naresh Yadav October 15th, 2009

    Shouldn't email be verified as [email protected] . In your example even [email protected] will be a valid email.

  13. Avatar for Maarten
    Maarten October 15th, 2009

    True, this was just an example of the concept.