Showing posts with label MVC. Show all posts
Showing posts with label MVC. Show all posts

I had the pleasure of presenting at the SA Dev workshop on 27 July 2011.Thanks to everyone who attended and please continue to support the local communities.

My discussion was based on Web application UI testing with open source tools and Visual Studio Testing tools. More specific I was covering AutoTest.Net for C# unit testing, QUnit for JavaScript testing and Web Tests and Load Tests in Visual Studio 2010.

Here is the links to my slides and demo application:

Web UI Testing Slides

World of Football Demo Application

Any feedback is welcome and thanks again!

Cheerio!

Today I want to show you how to develop an authentication system with JSON using ASP .Net MVC 3.0 and WCF.
Firstly I want to discuss the system design. I’m using Asp .Net MVC 3.0 framework to publish Html views to the client browser and for defining pretty URLs in the project. There is no business logic in the MVC framework. I define all my business rules in an WCF Service that only respond to JSON requests.  I’m using JQuery from my Html pages to query the web services for my business data.
Asp MVC WCF Json Design
You might ask yourself why I would decide on this design?
Benefits:
  • Clear separation of concern.
  • Smaller request and response data transfers.
  • Very Scalable  System ( Quickly move web services to Windows Azure ).
  • Dynamic Html Views Design.
Negatives:
  • JSON data is plain text.
  • Require SSL for secure and encrypted data transfer.
I hope you understand the benefits and negatives. There are more benefits and negatives that can be added. SSL is very important to be enabled when using this approach for authentication. Otherwise the user’s password will be available when transferred to the server.
Authentication Code Example:
Download the code to follow: JQueryLogin.zip
I created the default Asp .Net MVC 3.0 project with unit test project included. For this blog I did not do any unit testing. A nice challenge for you! In the solution there are three projects. JqueryLogin project, is the MVC web project. JqueryLogin.WebService project, is the WCF service that will handle JSON requests and business logic. JqueryLogin.Contracts project, is the WCF contracts that is defined for each request and response.
In the MVC web project I use the Razor View Engine. I also clean-up the code to just provide Views with no logic in the controllers. Especially go and look at the AccountController. I did not touch any views that is created by the template. Also I added the necessary JavaScript files that is required to do Ajax requests and create dynamic Html views.
In the _Layout.cshtml I added a bit of special JavaScript resolve function to help resolve URLs in my other JavaScript files. I got the original code from another blog (Forgot where?) but fixed it for MVC 3.0.
<script type="text/javascript">
    Url = function () { }

    Url.prototype =
        {
            _relativeRoot: "@Url.Content("~/")",

            resolve: function (relative) {
                var resolved = relative;
                if (relative.charAt(0) == '~') resolved = this._relativeRoot + relative.substring(2);
                return resolved;
            }
        }

    $Url = new Url();
</script>

With this function I will be able to resolve URLs like this in JavaScript:
window.location.href = $Url.resolve("~/Home/Index");

Now lets look at the other JavaScript files. The file ajax.js has some infrastructure and setup code that will help me with the requests to the web service. The User.js file is where the main logic is to register, sign-in and sign-out users to the web application. 

At the top of the file I define two object Register and SignIn with properties that match the same properties as the RegisterRequest Contract and SignInRequest Contract. The code that follow is where I define where the web service is that needs to be called. The web service methods are called after the Html form pass validation on the submit. Here is the code for sign-in and sign-out of the user:
/// <reference path="jquery-1.4.1.js" />
/// <reference path="jquery.validate.min.js" />
/// <reference path="ajax.js" />

var SignIn = {
    UserName : '',
    Password : '',
    RememberMe: ''
};

var UserServiceURL = "../WebService/UserService.svc/";
var UserServiceProxy = new serviceProxy(UserServiceURL);

$(document).ready(function () {
    $("#loginForm").validate({ submitHandler: function (form) {
        SignInUser();
    }
    });

    $('a[href="/Account/LogOff"]').click(function () {
        SignOutUser();

        return false;
    });

});

function SignInUser() {
    blockDisplay();
    var request = BuildSignInRequest();

    UserServiceProxy.invoke({
        serviceMethod: "SignIn",
        data: { request: request },
        callback: function (response) {
            $('#status').empty().html("<strong>Success: " + response.Message + "</strong>");

            $.unblockUI();
            window.location.href = $Url.resolve("~/Home/Index");       
        },
        error: function (xhr, errorMsg, thrown) {
            OnPageError(xhr, errorMsg, thrown);

            $.unblockUI();
        }
    });

    return false;
}

function SignOutUser() {
    blockDisplay();

    UserServiceProxy.invoke({
        serviceMethod: "SignOut",
        data: null,
        callback: function (response) {
            $('#status').empty().html("<strong>Success: " + response.Message + "</strong>");

            $.unblockUI();
            window.location.href = $Url.resolve("~/Home/Index");       
        },
        error: function (xhr, errorMsg, thrown) {
            OnPageError(xhr, errorMsg, thrown);

            $.unblockUI();
        }
    });

    return false;
}

function BuildSignInRequest() {
    SignIn.UserName = $('input[name="UserName"]').val();
    SignIn.Password = $('input[name="Password"]').val();
    SignIn.RememberMe = $('input[name="RememberMe"]').val();

    if (SignIn.RememberMe === "on") {
        SignIn.RememberMe = true;
    }
    else {
        SignIn.RememberMe = false;
    }

    return SignIn;
}

WCF Web Service Setup

Now let look at the web service to get all of this working. Firstly you define you web service in an interface file like IUserService.cs. In the UserService.svc mark up you have to change the factory to be able to handle JSON requests and responses.
<%@ ServiceHost Language="C#" Service="JqueryLogin.WebService.Service.UserService" Factory="System.ServiceModel.Activation.WebScriptServiceHostFactory" %>

In the UserService implementation you have to set AspNetCompatibilityRequirements attribute. This will allow for cookies to be set for when the user is authenticated.
[ServiceBehavior(IncludeExceptionDetailInFaults = true)]
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Required)]
public class UserService : IUserService
{
    // Implementation Code
}

The implementation of the user authentication can be found in the AccountMembershipManager class and the FormsAuthenticationManager class.  The last part that is required for the service is the service binding. For this service I use wsHttpBinding.
<services>
  <service behaviorConfiguration="DefaultBehavior" name="UserService">
    <endpoint binding="wsHttpBinding" contract="JqueryLogin.WebService.IUserService" />
    <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
  </service>
</services>

Show Some Results

I use Chrome Browser for debugging. I will show you the traced request and response output for Sign-in and Sign-out of user. I underline in red in the images the important details to notice.

SignIn Request – Click to Enlarge

SignInRequest

SignIn Response – Click to Enlarge

SignInResponseContent


SignOut Request – Click to Enlarge

SignOutRequest

SignOut Response – Click to Enlarge

SignOutResponseContent

Summary

The original idea of using JSON and WCF for creating a responsive website came when I read Chris Love blog entries “Creating a WCF Service for JSON” and “WCF and JQuery Using JSON”. The original JavaScript infrastructure come from him. As you can see that JSON allow for speedy web development and responsive web pages. JQuery just make it so easy to create Ajax calls.

Hope you enjoy this entry and any feedback or questions are welcome.

Cheerio!

This entry is just an extension to the blog Memory Dump to the Rescue by Bruno Marques. In he’s blog he shows how to generate a mini dump on a exception in code. Please read he’s blog first. If you want to use this in ASP .Net MVC you could take the same approach:

  1: [HttpGet]
  2: public ActionResult Crash()
  3: {
  4:     try
  5:     {
  6:        // compiler doesn't allow division by a zero constant,
  7:        // so we have to use an auxiliary variable
  8:        int zero = 0;
  9:        int value = 123456789 / zero;
 10:     }
 11:     catch (Exception)
 12:     {
 13:        string path = Path.Combine(HttpRuntime.AppDomainAppPath, @"App_Data\Dumps\");
 14:        string fileName = string.Concat(path, Process.GetCurrentProcess().ProcessName, ".dmp");
 15:        MiniDump.WriteDump(fileName);
 16:     }
 17: 
 18:     return View();
 19: }

The first thing to note in the code is that I write the dump file to my App_Data folder under a sub folder named Dumps. This is a better approach for file and folder access rights in ASP .Net applications. I have a small issue with this approach. If I have to insert all this code to write a dump file in all my actions then my code will become very cluttered and maintenance of the code becomes difficult.


The Better Approach


All developers that have developed with ASP.Net MVC know of the Attribute [HandleError] that you can put on top of your controllers. With this we can create our own custom attribute to handle the error and create a mini dump file for the application.


Here is the custom attribute code:

  1: [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true, Inherited = true)]
  2:  public class DumpHandleError : HandleErrorAttribute
  3:  {
  4:     public override void OnException(ExceptionContext filterContext)
  5:     {
  6:         string path = Path.Combine(HttpRuntime.AppDomainAppPath, @"App_Data\Dumps\");
  7:         string fileName = string.Concat(path, Process.GetCurrentProcess().ProcessName, ".dmp");
  8:         MiniDump.WriteDump(fileName);
  9: 
 10:         base.OnException(filterContext);
 11:     }
 12: }

Now I can cleanup my controller code as follow:

  1: namespace DumpMvcApplication.Controllers
  2: {
  3:     [DumpHandleError]
  4:     public class HomeController : Controller
  5:     {
  6:         [HttpGet]
  7:         public ActionResult Index()
  8:         {
  9:             ViewData["Message"] = "Welcome to ASP.NET MVC!";
 10: 
 11:             return View();
 12:         }
 13: 
 14:         [HttpGet]
 15:         public ActionResult Crash()
 16:         {
 17:             // compiler doesn't allow division by a zero constant,
 18:             // so we have to use an auxiliary variable
 19:             int zero = 0;
 20:             int value = 123456789 / zero;
 21: 
 22:             return View();
 23:         }
 24:     }
 25: }

All unhandled exceptions will be handled by my custom attribute to create a mini dump for the application.


Some additional tips is to create a abstract base controller with the attribute added and inherit other controllers from the base controller. Currently when the code creates a mini dump it will overwrite the dump file. If you want individual mini dumps for each exception I would recommend to add a DateTime.Now to the filename, but be aware that you have to cleanup your dumps else you might run out of disk space.


Hope this helps to ease your debugging experience for ASP .Net MVC applications.


Please comment and let me know on improvements that can be made to the code.


Cheerio!


Disqus