Skip navigation

Convention Over Configuration - 30 Oct 2009

Creating Applications Using ASP.<st1:stockticker>NET</st1:stockticker> MVC, jQuery, and AJAX: Part I

CoverStory

LANGUAGES: C# | VB.NET

ASP.NET VERSIONS: 3.5

 

Convention Over Configuration

Creating Applications Using ASP.NET MVC, jQuery, and AJAX: Part I

By Emad Ibrahim

 

Many of the new Web 2.0 applications coming out right now are being written in Ruby on Rails (RoR) and for good reason (I dislike the term Web 2.0, but for lack of a better term, I ll go ahead and use it). RoR is a very powerful Web platform that relies heavily on the principal of convention over configuration. This simply means there are a set of conventions that all developers should abide by, and by doing so we eliminate the hours we spend configuring the application.

Microsoft wasn t going to just sit back and watch Web developers flock to another platform, so they came up with an answer to RoR. That answer is the ASP.NET MVC Framework. This is a drastic departure from the ASP.NET Web forms applications we are used to writing. It might actually remind you of classic ASP; don t worry, it is far from that. This is a very powerful framework that, in my opinion, is way more powerful than the competition. For starters, you ll be able to use the best Integrated Development Environment (IDE) on the planet (Visual Studio 2008). This feature alone puts at your fingertips all the powerful tools and features you are used to: IntelliSense, auto-complete, an integrated debugger, master pages, etc.

In this article, we ll create a Web 2.0 application using the ASP.NET MVC Framework, and we ll add AJAX and JavaScript functionality using jQuery.

The ASP.NET MVC Framework

MVC stands for Model-View-Controller. This is a common design pattern that, whether you know it or not, you ve most likely used. The general premise is that the model is the class that talks to your data store, the view is your UI (page, user control, etc.), and the controller handles the interaction between the view and the model. So if you have a search button, the button itself is on the view; when you click it, the controller runs the code, then talks to the model, which in turn queries your database. The controller then receives the results and sends it back to the results view.

 

Unlike ASP.NET Web forms, there is no postback model and click event to be handled on the server, and there is no viewstate. This creates a much cleaner separation between the presentation layer and layers below it and the generated HTML is a lot cleaner and lighter, because it doesn t include a hidden viewstate field and a bunch of JavaScript to manage the postbacks and page lifecycle. To get an idea of this look at Figures 1 and 2, which show the generated HTML for a simple form with one textbox and a button; one was generated using regular Web forms, the other using the MVC Framework.

</p> <p>

  

 value= /wEPDwUJODU2ODYyODA2ZGQk+3rXpDybjZHn9aMYdRBjbvoatQ== />

  

  

 

  

 

   value= /wEWAwL34uW7AwLEu5j0CgLR1viaCayW/W1agurzYOoQmR6cdijk0P/6 />

Figure 1: HTML generated from an ASP.NET Web form.

</p> <p></p> <p>

  

  

  

  

Figure 2: HTML generated from an MVC page.

 

To live by the convention over configuration mantra, the MVC project is organized as shown in Figure 3. This structure is straightforward; we can deduce the content and function of each folder based on its name.


Figure 3: MVC project structure.

One of the most loved (at least for me) features in MVC is URL routing, which allows us to create SEO-friendly and clean URLs. So instead of /users/view.aspx?username=bob, you can do /users/view/bob. And as long as we follow the convention, we won t have to configure anything. In the above URL, users is the controller, view is the action, and bob is the Id. The default route defined in the project will automatically handle the above URL. The route table is defined in the Global.asax.cs file and the default route is shown in Figure 4.

routes.MapRoute( _

   Default , _

   {controller}/{action}/{id} , _

   New With {.controller = Home , .action = Index , .id = } _

)

Figure 4: Default route.

 

jQuery

One quick disclaimer: I love the things I can do with JavaScript and AJAX, but I am lazy and not proficient at JavaScript. The last thing I want to do is write hundreds of lines of code to perform simple effects or pop up a funky, Web 2.0-ish message. Thanks to jQuery, I don t have to do that. jQuery is a powerful, lightweight, and extensible JavaScript library that lets us write code in one or two lines that would otherwise take tens of lines. And the best part is, we don t have to worry about browser compatibility. jQuery works with IE 6.0+, Firefox 2+, Safari 2+, and Opera 9+. And for someone who is lazy and not dexterous with JavaScript, I managed to figure out jQuery in a couple of days simply by looking at samples and reading the online documentation.

For example, if you wanted to toggle an HTML element s visibility and also animate it, you could simply write: $( #mydiv ).toggle( slow ). Yes, this is a very simple example, but imagine the alternative of writing your own JavaScript and making it work with all browsers and handle exceptions gracefully. Note that the dollar sign ($) is shorthand for calling the jQuery API and is interchangeable with jQuery. So $( #mydiv ) is equivalent to jQuery( #mydiv ).

Create the Application

Now that we have a basic understanding of what MVC and jQuery are, let s create our application. The first thing we need to do is enable some basic account management for users to sign up (create an account) and log in. We want the URLs to be /account/signup and /account/login.

We can t log in until we ve created an account, so let s start with sign up. We start by creating an Account folder under the Views folder, then add a Signup page. In MVC-world, we don t create a Page; instead, we create a ViewPage. A ViewPage is the counterpart of a Page in the Web form world. If you right-click on the Account folder and click Add | New Item, then navigate to Web | MVC, you ll see the dialog box shown in Figure 5.

 


Figure 5: The Add New Item dialog box.

 

Figure 5 shows the available MVC-related items. The MVC Controller class is nothing more than a class that inherits from System.Web.Mvc.Controller. The other items are similar to their counterparts in the Web form world, so we get a content page, a page, a master page, and a user control. The MVC View Content Page is different than an MVC View Page in that it requires an MVC View Master Page.

Let s add a View Content Page and use the Site.master master page located at Views\Shared. We ll name this page signup.aspx.

Let s create the UI for the sign-up page. For the sake of simplicity, all we need to collect are username, password, and e-mail. The page should look similar to Figure 6; the associated markup is shown in Figure 7.

 


Figure 6: The sign-up form.

 

  

  

   Sign up

  

  

   User Name:

   <%=Html.TextBox( username , , New With {.class = textbox })%>

  

  

  

  

   Password:

   <%=Html.Password( password )%>

  

  

  

  

   Email:

   <%=Html.TextBox( email , , New With {.class = textbox })%>

  

  

  

  

  

   <%=IIf(IsNothing(ViewData( errorMessage )), ,

   Html.Encode(ViewData( errorMessage )))%>

   <%=Html.Button( btnSignup , Sign up , javascript:Account.signup )%>

  

  

Figure 7: Markup for the sign-up form.

Basically, all we do here is add a label, a textbox, and a span for validation for username, password, and e-mail. There are a few things new to MVC, so let s take a look.

First, take note of the embedded server code Html.TextBox, Html.Password, and Html.Button. Html is a helper class that is part of the MVC Framework; it contains a bunch of helper methods to simplify HTML generation. Each of these methods has multiple overloads and, don t forget, because the MVC Framework is open source, you can write your own helper methods. For example, the overloads for the TextBox method are shown in Figure 8.

 


Figure 8: Overloads for the Html.TextBox method.

There are helper methods to create dropdown lists, checkbox lists, buttons, password fields, images, and hyperlinks, among others. But remember, all these methods are doing is generating HTML, so the line:

<%=Html.TextBox("email", "",

 New With {.class = "textbox"})%>

generates the following HTML:

 id="email" value="" />

Notice that the Sign up button calls the JavaScript method Account.signup. I ll explain this below.

View Logic

Now that we ve created the view, let s add some UI logic. We need to add some field validation, as well as submit the form to the server. Because we need to AJAX-ify the site and reduce round trips, we ll perform this logic in JavaScript. Let s create a JavaScript file under the Content folder and name it Account.js. This file contains the signup method shown in Listing One that gets called from the Sign up button.

I know this looks like a lot of JavaScript, but if you read through it, you ll realize how simple, concise, and maintainable it really is. We first hide all the validation spans and messages, then validate the password, e-mail, and username; if either one fails, we display a descriptive message in the appropriate span without having to post anything to the server. If all validations succeed, we create an AJAX POST request and post the data (i.e., username, password, and e-mail) to the URL /account/signupsubmit. If we get a successful response back, we check to see if we succeeded in registering an account. Account sign-up could fail for several reasons, such as an invalid username, a duplicate e-mail address, or some other unhandled server exception. Note the use of jQuery selectors and you ll immediately understand their power. For example, the line:

$('#divSignup #btnSignup').attr("disabled", "disabled");

 

selects the element named btnSignup inside the element named divSignup and adds to it the disabled= disabled attribute. This will disable the button right before the AJAX request begins, which will prevent erroneous double-clicks by the user. We then enable the button upon receiving a response to the AJAX request by calling:

$('#divSignup #btnSignup').removeAttr("disabled");

 

Another example of jQuery selectors is shown in the way we hide all the validation spans with one line of code:

$("#divSignup span.validator").hide();

This basically selects all the span elements with a validator class inside the element divSignup , then calls the hide method on each matched element.

So, using jQuery, we are able to select the exact element in the HTML DOM and manipulate it accordingly. We do all that with very few lines of JavaScript code (most of the time, just one line) that will work on all browsers.

Also notice that our AJAX request is of type POST, and the data being submitted is serialized in JSON format. You ll see later that we don t need to write any code to deserialize this request and that the MVC Framework automatically knows how to handle it.

Controller

From the JavaScript we can see that we are submitting the form to the URL /account/signupsubmit and we are submitting username, email, and password as properties of a JSON object. Using the route convention of /controller/action, we can immediately see that we need an account controller with a SignupSubmit action. So let s create an account controller by right-clicking the Controllers folder and adding an MVC Controller class. The convention is to name it AccountController. This will create the controller class with a default Index action that throws an exception. We need to create two actions inside the controller. One action, named Signup, renders the sign-up page when the user navigates to /account/signup; the other receives the sign-up form submission named SignupSubmit. The Signup method is straightforward; all it does is render a view, as shown in Figure 9.

Public Function Signup As ViewResult

   Return View( signup )

End Function

Figure 9: The Signup action method.

The SignupSubmit receives the data from the form and creates the user account, as shown in Figure 10.

Public Function SignupSubmit( _

   ByVal username As String, _

   ByVal password As String, _

   ByVal email As String) As JsonResult

   Dim jsonData As New JsonData

   Try

   If String.IsNullOrEmpty(username) Then

   jsonData.errorMessage = _

   User name cannot be blank.

   ElseIf String.IsNullOrEmpty(password) Then

   jsonData.errorMessage = _

   Password cannot be blank.

   ElseIf password.Length < _

   Membership.Provider.MinRequiredPasswordLength Then

   jsonData.errorMessage = _

   String.Format( _

   Password must be {0} character long. , _

   Membership.Provider.MinRequiredPasswordLength)

   ElseIf String.IsNullOrEmpty(email) Then

   jsonData.errorMessage = Email cannot be blank.

   ElseIf Not IsValidEmail(email) Then

   jsonData.errorMessage = Invalid email address.

   ElseIf Not IsValidUsername(HttpContext, username) Then

   jsonData.errorMessage = Invalid User name.

   Else

   Try

   create the user using the built-in

   Membership(APIs)

   Dim status As MembershipCreateStatus

   Dim user As MembershipUser = _

   Membership.Provider.CreateUser( _

   username, password, email, Nothing, _

   Nothing, True, Nothing, status)

   If IsNothing(user) Then

   Throw New _

   MembershipCreateUserException(status)

   End If

   FormsAuthentication.SetAuthCookie(username, False)

   jsonData.isSuccessful = True

   Catch e As MembershipCreateUserException

 jsonData.errorMessage = e.Message

   End Try

   End If

   Catch e As Exception

   jsonData.errorMessage = e.Message

   End Try

   Return Json(jsonData)

End Function

Figure 10: The SignupSubmit method.

The code in Figure 10 validates the input data, creates an account, then returns the results as a JSON object. The thing to note here is that the method signature matches the data submitted by the AJAX request, i.e., username, password, and email. If you put a breakpoint in the code in Figure 10 you ll see that the variables username, password, and email match the values entered in the form. This automatically happens because the MVC Framework knows how to route and serialize the request automatically. The returned JSON object is a very simple class that has a few properties to encapsulate the response (see Figure 11).

Private Class JsonData

   Private _errorMessage As String =

   Public Property errorMessage As String

 Get

   Return _errorMessage

   End Get

   Set(ByVal value As String)

   _errorMessage = value

   End Set

   End Property

   Private _isSuccessful As Boolean = False

   Public Property isSuccessful As Boolean

   Get

   Return _isSuccessful

   End Get

   Set(ByVal value As Boolean)

   _isSuccessful = value

   End Set

   End Property

End Class

Figure 11: The JsonData class.

 

All we have to do in our SignupSubmit method is create an instance of JsonData, set the values for isSuccessful and errorMessage accordingly, then return the object like this:

return Json(jsonData);

 

You don t have to worry about object serialization it s all handled automatically for you. The JavaScript method above understands the returned results and reacts accordingly. As an example, if we were to try to sign up with an existing username, the response JSON string would be:

 

{ errorMessage : The username is already in use. ,

  isSuccessful :false}.

 

The full HTTP response (header and body) is shown in Figure 12.

Server: ASP.NET Development Server/9.0.0.0

Date: Fri, 13 Jun 2008 14:42:45 GMT

X-AspNet-Version: 2.0.50727

Cache-Control: private

Content-Type: application/json; charset=utf-8

Content-Length: 71

Connection: Close

{ errorMessage : The username is already in use. ,

  isSuccessful :false}

Figure 12: HTTP response.

 

That s all. We repeat the same steps for the log-in and log-out actions that are included in the accompanying source code (see end of article for download details). You ll also notice a minor change to the navigation menu in the master page. The changes, shown in Figure 13, selectively emit the appropriate HTML according to the authentication state, i.e., display log-in and sign-up links when logged out and display a log-out link when logged in.

Figure 13: Navigation menu in master page.

 

Conclusion

We ve created our first view, controller, and action. If you look at the source code you ll see that I used the built-in ASP.NET membership API to create the user accounts, so there is no model per se in this example because it is abstracted by the API.

We also covered form creation, submission, and validation. We used the jQuery JavaScript library to simplify our UI logic, add validation, and submit a form using an AJAX request. We also saw how we pass data from the client to the server and vice versa using JSON serialization.

In Part II we ll delve more into jQuery and use some cool animation effects and DOM manipulation techniques to enhance the user experience and make our site more responsive. We ll also look at other features of the MVC Framework, such as user controls, routing customization, and ViewData, and talk about the model part of the framework.

Source code accompanying this article is available for download to asp.netPRO subscribers at www.aspnetPRO.com.

 

Emad Ibrahim, MCSD, has been a professional developer for nine years and has worked with .NET since the first beta. He has held titles ranging from architect to senior developer to consultant. He quit his job with Avanade to start playing around with new stuff and catch up with technology. Since then he created an open source twitter clone at yonkly.com. He also is an active blogger and mostly blogs about software development and technology-related stuff. His blog is at www.emadibrahim.com.

 

Begin Listing One The JavaScript signup method

signup: function {

   //hide all validation spans and messages

   $( #divSignup span.validator ).hide;

   $( #signupMessage ).hide;

   //get the user name text box and value

   var txtUserName = $( #divSignup #username );

   var userName = $.trim(txtUserName.val);

   //validate that username exists

   if (userName.length == 0) {

   Account._showMessage( #divSignup #valSignupUserName ,

   User name cannot be blank. , true);

   $(txtUserName).focus;

   return;

   }

   //get password text box and value

   var txtPassword = $( #divSignup #password );

   var password = txtPassword.val;

  

   //validate password length

   if (password.length < 4) {

   Account._showMessage( #divSignup #valSignupPassword ,

   Password must be 4 character long. , true);

   $(txtPassword).focus;

   return;

   }

   //get email text box and value

   var txtEmail = $( #divSignup #email );

   var email = $.trim(txtEmail.val);

   //validate that email exists

   if (email.length == 0) {

   Account._showMessage( #divSignup #valSignupEmail ,

   Email cannot be blank. , true);

   $(txtEmail).focus;

   return;

   }

   //valide email format

   if (!Account._isValidEmail(email)) {

   Account._showMessage( #divSignup #valSignupEmail ,

   Invalid email address. , true);

   $(txtEmail).focus;

   return;

   }

   //display status message

   Account._showMessage( #divSignup #signupMessage ,

   Signing up... , false);

   //disable submit button to prevent double submission

   $( #divSignup #btnSignup ).attr( disabled ,

   disabled );

   //post form to server using an ajax request

   $.ajax({

   type: POST ,

   dataType: json ,

   url: /account/signupsubmit ,

   data: { userName: userName,

   password: password,

   email: email

   },

   success: function(result) {

   //returned successfully from server

   //enable the submit button

   $( #divSignup #btnSignup ).removeAttr( disabled );

   //if results were successful then redirect to /home

   if (result.isSuccessful == true) {

   location.href = /home ;

   }

   else {

   //a server error happened, get the error message

   var msg = result.errorMessage;

   //hide the status message

   $( #divSignup #signupMessage ).hide;

   //display the appropriate error message

   if (msg.indexOf( username ) > -1) {

   Account._showMessage

   ( #divSignup #valSignupUserName ,

   msg, true);

   $(txtUserName).focus;

   }

   else if (msg.indexOf( E-mail ) > -1) {

   Account._showMessage

   ( #divSignup #valSignupEmail ,

   msg, true);

   $(txtEmail).focus;

   }

   else {

   Account._showMessage

   ( #divSignup #signupMessage ,

   msg, true);

   }

   }

   }

End Listing One

 

 

 

Hide comments

Comments

  • Allowed HTML tags: <em> <strong> <blockquote> <br> <p>

Plain text

  • No HTML tags allowed.
  • Web page addresses and e-mail addresses turn into links automatically.
  • Lines and paragraphs break automatically.
Publish