MVC Service Based Web Applications - Part I - Introduction

As any developer knows, there's a delicate balance between having a do-it-yourself attitude and finding a library to solve every concrete problem you run into. Too much ambition will leave you wrestling with bugs relating to concepts you aren't well versed in and too much reliance on third party tools can code you into a corner. Speaking from experience, neither is a pleasant situation to be in. With the advent of service based web applications a variety of frameworks and concepts have sprung up with the intention of making developers lives easier. While I could describe in detail what I don't like about WCF, SOAP and ReST I'd rather simply demonstrate my own personal approach that utilizes the strengths of each without sacrificing flexibility. For this project you'll need to use ASP.Net MVC. Personally I'll be using version 3 but there shouldn't be any problems if you use version 4 or later. As this tutorial moves forward I may upgrade if there's a particular feature I want to take advantage of.

This introduction will mostly be about creating an organizing the project. While some of the reasoning behind it all will be explained most of it will be revealed in later postings. Let's start with an empty ASP.Net MVC project and select Razor as the view engine. I'm calling my solution BlogTutorial and the project UI. I always remove the following folders and files since I won't be using them:

\Models\
\Views\Shared\_Layout.cshtml
\Views\Shared\Error.cshtml
\Scripts\jquery-1.5.1-vsdoc.js
\Scripts\jquery-1.5.1.js
\Scripts\jquery-ui-1.8.11.js
\Scripts\jquery.unobtrusive-ajax.js
\Scripts\jquery.unobtrusive-ajax.min.js
\Scripts\jquery.validate-vsdoc.js
\Scripts\jquery.validate.js
\Scripts\jquery.validate.min.js
\Scripts\jquery.validate.unobtrusive.js
\Scripts\jquery.validate.unobtrusive.min.js
\Scripts\MicrosoftAjax.debug.js
\Scripts\MicrosoftAjax.js
\Scripts\MicrosoftMvcAjax.debug.js
\Scripts\MicrosoftMvcAjax.js
\Scripts\MicrosoftMvcValidation.debug.js
\Scripts\MicrosoftMvcValidation.js
\Scripts\modernizr-1.7.js
\Scripts\modernizr-1.7.min.js

Before anyone says anything, I'm not denying the usefulness of any of these scripts. Simply put I feel that they don't offer enough functionality for me to rely on them. Your project needs may differ from mine. I should also mention at this point that we won't be using Razor page views. (Save for the index page.) We're going to write all views using good old HTML5 and jQuery AJAX methods. This may all seem a bit unorthadox but if you'll bear with me I promise there will be a method to this madness.

First, let's create a new script file in the Scripts folder. We'll start off with a single method.

\Scripts\Util.js


function AJAXLoadData(url, data, successCallBack) {
    $.ajax({
        type: "POST",
        data: data,
        url: url,
        contentType: "application/json; charset=utf-8",
        dataType: "json",
        success: function (msg) {
            if (successCallBack) successCallBack(msg);
        },
        error: function (msg) {
            alert("error.");
        }
    });
}

This is a fairly simple method that use the jQuery .ajax() call to retreive data. Wrapping these calls will allow us to easily implement script level caching, enabling JSONP support, automatic exception handling and a host of other great features that would otherwise prove difficult. For now, it's pretty self explanatory to anyone familiar with jQuery and javascript. Next let's set up some support classes. Later on we'll move these to an external class library but for now they can live in our UI project.

\Classes\Exceptions\ClientException.cs


public class ClientException : Exception
{
    public string Number { getprotected set; }
    public string Value { getset; }
    public bool LoggedIn { getset; }
    public bool IsAdmin { getset; }

    public ClientException(string source, string valuestring message, Guid number)
        : base(message)
    {
        this.Source = source;
        this.Number = number.ToString();
        this.Value = value;
    }
    public ClientException(string source, int valuestring message, Guid number)
        : this(source, value.ToString(), message, number)
    {
    }
}

\Classes\Exceptions\ServerException.cs


public class ServerException : Exception
{
    public ServerException(string message)
        : base(message)
    {
    }
}

\Classes\JSONMessageObject.cs


public class JSONMessageObject
{
    protected IList<Exceptions.ClientException> _ClientExceptions = null;
    protected IList<Exceptions.ServerException> _ServerExceptions = null;

    public bool Success { getset; }
    public bool LoggedIn { getset; }
    public bool IsAdmin { getset; }
    public object Data { getset; }
    public IList<Exceptions.ClientException> ClientExceptions
    {
        get
        {
            if (_ClientExceptions == null) _ClientExceptions = new List<Exceptions.ClientException>();
            return _ClientExceptions;
        }
        set
        {
            _ClientExceptions = value;
        }
    }
    public IList<Exceptions.ServerException> ServerExceptions
    {
        get
        {
            if (_ServerExceptions == null) _ServerExceptions = new List<Exceptions.ServerException>();
            return _ServerExceptions;
        }
        set
        {
            _ServerExceptions = value;
        }
    }

    public string toJSON()
    {
        JavaScriptSerializer serializer = new JavaScriptSerializer();
        return serializer.Serialize(new
        {
            Success = Success,
            LoggedIn = LoggedIn,
            IsAdmin = IsAdmin,
            Data = Data,
            ClientExceptions = from ex in ClientExceptions
                               select new
                               {
                                   Source = ex.Source,
                                   Value = ex.Value,
                                   Message = ex.Message,
                                   Number = ex.Number
                               },
            ServerExceptions = from ex in ServerExceptions
                               select new
                               {
                                   Message = ex.Message
                               }
        });
    }
}

The purpose of the JSONMessageObject is to define structure used by every web method in the application to communicate with the client even if that method experiences a catastrophic failure. At worst the method will return a message with the Success boolean set to false and a server exception explaining that the server encountered an error and that the user should contact an administrator. Make no mistake, your web service is very much a protocol in it's own right and hence it benefits from using a standard header. Later on in the project we'll be altering AJAXLoadData to take advantage of some of this information.

Next we'll need to create some controllers. We'll start with two, BaseController and MethodController which will inherit from BaseController. For now we'll remove the Index() view method in BaseController and replace it with the following two methods.

\Controllers\BaseController.cs


protected JsonResult Message(bool success, bool loggedIn, bool isAdmin, object data)
{
    return Message(new Classes.JSONMessageObject()
    {
        Success = success,
        LoggedIn = loggedIn,
        IsAdmin = isAdmin,
        Data = data
    });
}
private JsonResult Message(Classes.JSONMessageObject message)
{
    return Json(message, "application/json", JsonRequestBehavior.AllowGet);
}

Much like the approach used on the client side of things the ASP.Net MVC Json() will never be called directly. We now have the basic framework set up for building an extremely powerful array of web services capable of serving a wide range of clients and configurations. The next step would typically be to get a data project set up and running but I feel that would make this already lengthy post a bit too much to handle. For now we'll simply create a simple test method that spoofs information which can be consumed by an HTML view. To do that, the following code should be inserted into the MethodController.

\Controllers\MethodController.cs


public class FakeData
{
    public string FirstName { getset; }
    public string LastName { getset; }
}

public JsonResult GetSpoofedInfo()
{
    var Names = new List<FakeData>
    {
        new FakeData() {FirstName = "Spencer", LastName = "Ruport"},
        new FakeData() {FirstName = "John", LastName = "Smith"}
    };

    return Message(truefalsefalsenew
    {
        Names = from d in Names
            select new
            {
                Name = d.FirstName + " " + d.LastName
            }
    });
}

Now I suppose I could have created the FakeData class with a single field since they ultimately are concatenated but I wanted to take this opportunity to demonstrate that the database models can be projected to fit the needs of the method. This is a very remedial (and perhaps inappropriate) example but I wanted to establish this ability early on as we'll be using it frequently throughout the development of this project.

Finally lets add one more controller called HomeController and create a view for the default Index() method.

\Views\Home\Index.cshtml


<!DOCTYPE html>

<html>
<head runat="server">
    <script src="/Scripts/jquery-1.5.1.min.js" language="javascript" type="text/javascript"></script>
    <script src="/Scripts/jquery-ui-1.8.11.min.js" language="javascript" type="text/javascript"></script>
    <script src="/Scripts/Util.js" language="javascript" type="text/javascript"></script>
</head>
<body>
    <script language="javascript" type="text/javascript">
        $(document).ready(function() {
            AJAXLoadData("/Method/GetSpoofedInfo"nullfunction (msg) {
                $(".obj_lstNames li").not(".obj_static").remove();

                var template = $("obj_lstNames .obj_template");
                $.each(msg.Data.Names, function (index, item) {

                    var ListItem = template.clone();
                    ListItem.text(item.Name);
                    ListItem.insertBefore(template);
                    ListItem.removeClass("obj_template").removeClass("obj_static").show();
                });
            });
        })
    </script>

    <h1>Welcome!</h1>

    <ul class="obj_lstNames">
        <li class="obj_template obj_static" style="display: none;"></li>
        <li>Loading...</li>
    </ul>    
</body>
</html>

Run the project and see the unordered list populate with the fake information we created in the GetSpoofedInfo() method.

Phase I Links: Download | Demo

Conclusion

Congratulations! You have created an MVC Service Based application with a simple HTML view. In the next phase of the tutorial we'll take a look at how HTML views are loaded and script level caching.

Quick Link: Next: Script Level Caching >>