Alexander Beletsky's development blog

My profession is engineering

Web development: ASP.NET Ajax, ScriptManager to recover a password

Remember a site we started to create in previous articles. What we’ve got is simple web application, with a registration functionality. During registration you submit email, password and secret phrase. We going to use this secret phrase today to recover forgotten passwords. Functionality is bit artifictial but my goal is to demonstrate usage of ASP.NET Ajax framework, namely ScripManager object as very usefully component for ajaxifying your applications.

Preparation


I’ve added another page called ForgetPassword.aspx, it looks like this:


Script Manager is really powerfull component and one of major feature is how it simplifies working with web services. So, let’s add ScriptManager to application. The place to do that are typically MasterPage to make ScrpitManager be accessible from every content page. It have to be placed in ‘form’ with runat=”server” attribute, otherwise runtime exception will appear on page loading.

<div class="content">
 <div id="left">
   <form id="form1" runat="server">
      <asp:ScriptManager ID="ScriptManager" runat="server">
        <Scripts>
          <asp:ScriptReference Path="~/js/forgetpassword.js" ScriptMode="Auto" />
        </Scripts>
        <Services>
          <asp:ServiceReference Path="~/services/ForgetPassword.asmx" />
        </Services>
      </asp:ScriptManager>
             
      <asp:ContentPlaceHolder ID="ContentPlaceHolder1" runat="server">
      </asp:ContentPlaceHolder>
    </form> 
 </div>  

* This source code was highlighted with Source Code Highlighter.


Script Manager contains two references - ScriptReference, ServerReference. It is a declarative way to add references, in the same time it is possible to do it programmatically (that is more complicated). What you need to do is to add such declarations, the rest will be done by control. Next, we will add forgetpassword.js script to script folder and new web service instance to services folder. Now, application ready for AJAX!

Ajaxify It


Before we do something more serious let’s check that setup we created are actually working. Go to webservice code (ForgetPassword.asmx.cs):

namespace WebApplication.Services
{
  /// <summary>
  /// Summary description for ForgetPassword
  /// </summary>
  [WebService(Namespace = "http://tempuri.org/")]
  [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
  [System.ComponentModel.ToolboxItem(false)]
  // To allow this Web Service to be called from script, using ASP.NET AJAX, uncomment the following line.
  [System.Web.Script.Services.ScriptService]
  public class ForgetPassword : System.Web.Services.WebService
  {
    [WebMethod]
    public string HelloWorld()
    {
      return "Hello World";
    }
  }
}

* This source code was highlighted with Source Code Highlighter.


Please note, there is attribute of service class [System.Web.Script.Services.ScriptService] that must be uncommented as soon as webservice is being used from java script code. Do not forget to uncomment it. And simple method is present there, HelloWorld that just returns a string. On a ForgetPassword.aspx page we have such form:

<asp:Content ID="Content2" ContentPlaceHolderID="ContentPlaceHolder1" runat="server">
  <h2>Please fill form below</h2>
  <div class="box">
    <label>Email: </label><asp:TextBox ID="EmailText" runat="server"></asp:TextBox>
    <label>Secret Phrase: </label> <asp:TextBox ID="SecretPhraseText" runat="server"></asp:TextBox>
    <label>&nbsp</label> <input id="forgot" type="button" value="Forget password" class="button" onclick="onForgotPasswordClick()"/> 
  </div>
  <span id="Password"></span>
</asp:Content>

* This source code was highlighted with Source Code Highlighter.


By pressing on ‘forget’ button onForgotPasswordClick() javascript function will be called (this is the place where we would do AJAX calls). Currently the code is really simple:

function onForgotPasswordClick() {
  WebApplication.Services.ForgetPassword.HelloWorld(onMethodSucceeded, onMethodFailed);
}

function onMethodSucceeded(results, args) {
  alert(results);
}

function onMethodFailed(error) {
}

* This source code was highlighted with Source Code Highlighter.


One one line of code in onForgotPasswordClick() do a bit complex things. It register a call to web service (you have to reference web service with a full class name), HelloWorld does not have any arguments, so we are passing nothing, but register 2 callback functions. One that will be called on successfull finish of AJAX call, second will be called on any failure. Even it might seems no so ‘magic’ ones who created AJAX application without any frameworks would notice how less code have to be created to accomplish this. Finally, we displaying results in alert window. Just run application, click ‘Forgot password’ button and see:



Real work


As soon as we see its OK, let’s do some real work. If users email and secret pharase matches corresponding record in database, we going to show user password on screen without reloading of page. We will add new business object and implementation for recovery of password. I would just paste tests and code of business object (as it is really small and simple one), the rest implementation you will be able to see by going to GitHub. (for ones who followed my previous articlies - I’ve done some refactoring switching to Repository pattern for DAL, since it is more correct and reusable way of working with data, please see details in code).

Tests:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NUnit.Framework;
using Company.Product.BLL.Tests.Mocks;
using NUnit.Framework.SyntaxHelpers;
using Company.Product.DAL;

namespace Company.Product.BLL.Tests
{
  [TestFixture]
  public class ForgotPasswordTests
  {
    [Test]
    public void Smoke()
    {
      //INIT
      var data = new ForgotPasswordDataMock();

      //ACT/POST
      var c = new ForgotPassword(data);
    }

    [Test]
    public void RestorePassword_NoUser()
    {
      //INIT
      var data = new ForgotPasswordDataMock();
      var forgot = new ForgotPassword(data);

      //ACT
      var password = forgot.RestorePassword("no such user", "secret");

      //POST
      Assert.That(password, Is.Null, "It is not possible to return password for non-existing user.");
    }

    [Test]
    public void RestorePassword_WrongSecretPhrase()
    {
      //INIT
      var data = new ForgotPasswordDataMock();
      var forgot = new ForgotPassword(data);

      data.InsertUser(new User() { Email = "user@a.com", Password = "password", SecretPhrase = "correct secret" });

      //ACT
      var password = forgot.RestorePassword("user@a.com", "wrong secret");

      //POST
      Assert.That(password, Is.Null, "It is not possible to return password if wrong secret phrase used.");
    }

    [Test]
    public void RestorePassword_CorrectUserAndSecret()
    {
      //INIT
      var data = new ForgotPasswordDataMock();
      var forgot = new ForgotPassword(data);

      var expectedPassword = "password";
      data.InsertUser(new User() { Email = "user@a.com", Password = expectedPassword, SecretPhrase = "correct secret" });

      //ACT
      var password = forgot.RestorePassword("user@a.com", "correct secret");

      //POST
      Assert.That(password, Is.EqualTo(expectedPassword), "Wrong password value returned.");
    }
  }
}

* This source code was highlighted with Source Code Highlighter.


Code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Company.Product.DAL;

namespace Company.Product.BLL
{
  public class ForgotPassword
  {
    private IUsersRepository _data;

    public ForgotPassword(IUsersRepository data)
    {
      _data = data;
    }

    public string RestorePassword(string email, string secret)
    {
      var user = _data.FindUserByEmail(email);

      if (user == null)
        return null;

      if (user.SecretPhrase == secret)
        return user.Password;

      return null;
    }
  }
}

* This source code was highlighted with Source Code Highlighter.


After it is ready and tested we are going to integrate with object to Web Service method call.

/// <summary>
/// Summary description for ForgetPassword
/// </summary>
[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
[System.ComponentModel.ToolboxItem(false)]
// To allow this Web Service to be called from script, using ASP.NET AJAX, uncomment the following line.
[System.Web.Script.Services.ScriptService]
public class ForgotPasswordService : System.Web.Services.WebService
{
  [WebMethod]
  public string RestorePassword(string email, string secret)
  {
    var forgotPassword = new ForgotPassword(new UsersRepository());

    return forgotPassword.RestorePassword(email, secret);
  }
}

* This source code was highlighted with Source Code Highlighter.


After that it is time to use Web Service from javascript code. I’m using jQuery library to work with divs, classes and so on. jQuery does not require any special to add about it, it is so popular that it has been included to VS 2010 and Microsoft is contributing to jQuery as well. Code uses simple and cool effect of fadeOut slideDown effect, just to make application bit more attactive.

function onForgotPasswordClick() {
  var email = $('#email').val();
  var secret = $('#secret').val();

  $("#resultsdiv").fadeOut("fast", function() {
    removeClasses();
    WebApplication.Services.ForgotPasswordService.RestorePassword(email, secret, onMethodSucceeded, onMethodFailed);  
  });  
}

function onMethodSucceeded(results, args) {
  var password = results;

  if (password == null) {
    $("#results").addClass("red");
    $("#results").html("Your email or secret phrase is invalid");   
  }
  else {
    $("#results").addClass("green");
    $("#results").html("Your password: " + password);
  }

  $("#resultsdiv").slideDown("fast");
}

function onMethodFailed(error) {
}

function removeClasses() {
  if ($("#results").hasClass("green")) {
    $("#results").removeClass("green");
  } else if ($("#results").hasClass("red")) {
    $("#results").removeClass("red");
  }
}

* This source code was highlighted with Source Code Highlighter.


Results


As soon as I input some wrong data into email/secret phase control, I’ll see fadding out div that informs me about problem. Like that,



But if I put correct values where, I’ll get my password back:



Conclusions


Nowadays it is even hard to imagine serious web application with out AJAX. It is mostly everywhere. Because of such usabilty and popularity a lot of vendors proposing tools and framework to hanlde common AJAX issues. And of cause, tools for working with AJAX is started to be included to .NET framework. ScriptManager is only one part of it framework. For futher exploration I would recomend to look on ASP.net AJAX Toolkit that is build upon .Net framework abilitites and propose a lot of interesting controls. Moreover, it is open source and you can go ahead and hack it or contribute it.

Sources


Sources are available on GitHub: http://github.com/alexbeletsky/Concept

Bookmark and Share