Checking the Existence of an ASP.NET Membership Username with ASP.NET AJAX

If you have been using the Membership features that ship with ASP.NET, I'm sure you're familiar with the CreateUserWizard control that lets a user sign up for an account on your site. One of the features of this control (in cooperation with the Membership services) is to check whether a given user name is already taken and display an appropriate message if that's the case. This is an excellent feature as it makes sure no two users can end up with the same user name.

One of the problems with this check is that it takes place at the server. This means the page goes through a full post back which takes some time. Even worse, due to security settings on the control, the two password fields are reset so the user has to enter them again in case they chose an existing user name. It would be a lot easier if you could check the user name before the page is posted back using a bit of client side script. With ASP.NET AJAX and Page Methods, this is a walk in the park.

Create a Client Side User Name Check

You can check the user name at the client using Page Methods. The Page Method is called from the client and then executes some code at the server. The server side method then uses the Membership API to check whether the user name is already taken and returns the value to the client. Writing this functionality takes five steps:

  • Set up a ScriptManager control to allow and handle Page Method calls.
  • Write the Page Method in the page that contains the CreateUserWizard.
  • Add a hidden element (like a span) to the page to hold the error message. Hide the element by default, so it can be shown when the user name already exists.
  • Set up a handler for the UserName text box's blur event.
  • Write some code that is executed when the UserName text box looses focus; the code should call the Page Method and show or hide an appropriate error message.

Setting Up the ScriptManager

If your page is based on a Master Page, you could add the ScriptManager control to that Master Page so it becomes available to all pages in your site. Alternatively, you can add the ScriptManager directly to the page containing the CreateUserWizard. Either way, you need to set the ScriptManager's EnablePageMethods to true:

<asp:ScriptManager ID="ScriptManager1" runat="server" 
               EnablePageMethods="true" />

Write a Page Method that Checks the User Name

To create a Page Method callable from client script, all you need to do is write a static method in the page's code section and apply the WebMethod attribute. To check the existence of the user using the Membership API, the method should accept the proposed user name and return a bool to indicate whether the name exists or not. Your method could look like this:

[WebMethod]
public static bool UserNameExists(string yourName)
{
return Membership.GetUser(yourName) != null;
}

When GetUser returns null, it means the user is not found and the method returns false.

Add the Error Message Label to the Page

There are a number of ways to add the error message to the page. One way is to convert the entire CreateUserWizard to a template using its Smart Task Panel. Then you can add a Label or another element directly in the markup in the ASPX page. Another alternative is to dynamically inject the element at run-time using JavaScript. You can do this in a JavaScript block defined below the CreateUserWizard control like this:

var userNameTextBox = 
   $get('<%= CreateUserWizard1.ClientID %>_CreateUserStepContainer_UserName');
var userNameRequiredMessage = 
   $get('<%= CreateUserWizard1.ClientID %>_CreateUserStepContainer_UserNameRequired');
var errorMessage = document.createElement('span');
errorMessage.style.visibility = 'hidden';
errorMessage.style.color = 'red';
errorMessage.innerHTML = 'User name already taken';
userNameRequiredMessage.parentNode.appendChild(errorMessage);

The first couple of lines of code get a reference to the UserName text box and the error message for the RequiredFieldValidator for the user name. I am using <%= CreateUserWizard1.ClientID %> to get the client side ID of the CreateUserWizard. This is better than hard coding it in the JavaScript as things will break without a compile error if you rename the control. Internally, the UserName control is always called CreateUserStepContainer_UserName so it's OK to hard code that in.

If you were using ASP.NET 4.0, things would be a lot easier. You can set the ClientIDMode to Static which gives the CreateUserWizard a static client ID. This in turn means that the UserName and UserNameRequired elements simply are named UserName and UserNameRequired respectively. This way you can simplify the code to read:

var userNameTextBox = $get('UserName');
var userNameRequiredMessage = $get('UserNameRequired');                  

The remainder of the code creates a new DOM element using document.createElement. It sets the innerText to the error message, changes its font color and then hides the element by setting its visibility to hidden. Finally, the element is injected to the page, right before the existing error message for the required user name using insertBefore. Instead of assigning font and other information to the element directly, you could of course set its className property to refer to some CSS class. Now that the message is injected to the page, it's ready to be used by other code in the page.

Set up a Handler for the User Name text box's blur Event

Setting up the handler to call some code when the user moves the cursor away from the text box is easy. All you need is a single line of code:

$addHandler(userNameTextBox, 'blur', UserNameExists);                  

This method uses the userNameTextBox variable declared earlier and assigns UserNameExists as the method to call when the control looses focus. The final action now is to implement UserNameExists and have it call the Page Method to determine if the user exists or not.

Calling the Page Method to Check Whether the User Exists

To call a Page Method, you call the name of your method on the PageMethods class that ASP.NET AJAX makes available for you. When doing so, you need to define a callback method that is called when the page method returns its answer. Inside this callback you can check the return value of the method call and then hide or show the error message depending on the outcome. The implementation can be as simple as this:

function UserNameExists()
{
  var userName = userNameTextBox.value;
  PageMethods.UserNameExists(userName, UserNameExistsCallback);
}

function UserNameExistsCallback(result)
{
  errorMessage.style.visibility = result ? 'visible' : 'hidden';
}

The UserNameExists JavaScript method calls the UserNameExists Page Method using the PageMethods class and passes it the user name that it retrieves from the userNameTextBox element. When the Page Method returns, the callback method UserNameExistsCallback is executed. This method hides or shows the error message depending on the value of the result parameter it receives. When the user already exists, the error message is shown by setting the visibility to visible. When the user name is not already taken, the error message is hidden.

With this setup, the user will see an error message appaear as soon as she moves the focus away from the UserName text box.

Summary

By using ASP.NET AJAX, Page Methods and the Membership API, implementing a client side user name check is a matter of minutes. What's cool about this method is that it doesn't require a change to the existing sign up page. There's no need to rip apart the CreateUserWizard and add any HTML or CSS to existing elements. All you need to do is define a server side Page Method and write a bit of JavaScript.

Note that this is just a simple example of using Page Methods. It's easy to come up with many other useful examples that improve the user experience in your ASP.NET driven web site.

Download Files

Source Code for this Article


Where to Next?

Wonder where to go next? You can read existing comments below or you can post a comment yourself on this article .


Consider making a donation
Please consider making a donation using PayPal. Your donation helps me to pay the bills so I can keep running Imar.Spaanjaars.Com, providing fresh content as often as possible.



Feedback by Other Visitors of Imar.Spaanjaars.Com

On Wednesday, December 02, 2009 4:45:23 PM mark newton said:
Excellent article and it works a treat for a simple login wizard with just one screen. I want to add an extra screen for other user input but when I do this I get an error. Any ideas?
On Wednesday, December 02, 2009 6:01:40 PM Imar Spaanjaars said:
Hi mark,

Difficult to say, as you're not telling where, when and under what circumstances you're recieving which exception.

My guess is that the code tries to access an HTML element that is not present in the page in your wizard.

Try posting your message, with code and a detailed explanation here: http://p2p.wrox.com/index.php?referrerid=385 and I'll take a look.

Cheers,

Imar
On Friday, January 29, 2010 7:57:47 AM sahridhayan said:
Hai,

great! This can be used for general Form also,
Where there is no MemberShip Provider. I tried it must useful.

Instead of MemberShip.MethodName (you have to call your
BusinessComponent.MethodName

Same way in fetching Client ID (instead  of CreateWizard try to get your Text Box client ID)

Thanks,

Sahridhayan
On Saturday, January 30, 2010 9:55:01 AM Imar Spaanjaars said:
Hi sahridhayan,

Yes, Page Methods are not unqiue to Membership. You can execute any code from Page Methods you see fit, as long as they don't try to access any instance members of the current page.

Cheers,

Imar
On Friday, June 11, 2010 6:50:43 PM Mohammad Irfan said:
Thanks for posting such a nice article. I have tested the application and it is working on most of the browsers like IE, Safari, Chrome. But it is not working on FireFox. Can you please provide me the solution?
On Friday, June 11, 2010 8:00:14 PM Imar Spaanjaars said:
Hi there,

This article could benefit from some cross-browser jQuery. Will see if I have the time to update it some day.

In the mean time, replace the two lines that fill and add the error message with this:

errorMessage.innerHTML = 'User name already taken';
userNameRequiredMessage.parentNode.appendChild(errorMessage);

Somehow, Firefox added my error message as a child of the error message from the CreateUserWizard. Since that control is hidden by default you never see the "user name taken" message....

Cheers,

Imar
On Saturday, June 12, 2010 9:38:48 AM Mohammad Irfan said:
Thanks Imar for your quick reply. I posted comment yesterday indicating that the code works on most of the browsers but not on FireFox.

I found exact reason. The problem is in -
function UserNameExistsCallback(result)
{
        errorMessage.style.visibility = result ? 'visible' : 'hidden';
}

visibility property is not working on FF. I just replaced the line with simple alert and it worked, like -

function UserNameExistsCallback(result)
{
        if(result)
            alert('Username you entered already exist. Please try another username');
}


Anyway thanks again for such nice code.

Regards
Mohammad Irfan
On Saturday, June 12, 2010 9:43:17 AM Imar Spaanjaars said:
Hi there,

It works fine for me in Firefox with the fix I supplied yesterday. Are you sure you changed innerText to innerHTML?

Imar
On Saturday, June 12, 2010 9:52:21 AM Mohammad Irfan said:
Thanks Imar. Its working on all browsers.
On Sunday, July 04, 2010 3:15:15 AM Rob said:
Hi Imar,

With regard to the error I was writing about in my last post...I neglected to let you know when the error occurs.  If I use a user name that already exists, it works great letting me know and the error message displays.  It's only after the create user button is cilcked that the error occurs!  I am so sorry for leaving that important information out in my last message!  Thanks again, Rob
On Sunday, July 04, 2010 9:07:30 AM Imar Spaanjaars said:
Hi Rob,

Last post? I don't see a last post from you. Maybe it was rejected when you posted something that contains HTML tags?

Imar
On Saturday, August 28, 2010 9:40:02 AM matt said:
can the same process be applied to the users email address to make sure it is unique and if so how?

thanks
On Saturday, August 28, 2010 9:42:02 AM Imar Spaanjaars said:
Hi matt,

Yes, you can, using Membership.FindUsersByEmail.

Cheers,

Imar
On Tuesday, August 31, 2010 9:05:28 AM matt said:
hi,

i tried to rig up the code to have the email address text box checked as well. in my code behind i installed the code below and adapted your code for this method but seems not to be working. could you demonstrate?


    Membership.FindUsersByEmail
On Tuesday, August 31, 2010 9:10:13 AM Imar Spaanjaars said:
Hi matt,

Did you create an additonal parameter for the e-mail address on the service method? Or did you create a new service method? The latter is probably easier. Then you should be able to do something like this:

[WebMethod]
public static bool EmailAddressExists(string emailAddress)
{
  return Membership.FindUsersByEmail(emailAddress).Count > 0;
}

Cheers,

Imar
On Tuesday, August 31, 2010 11:20:54 PM matt said:
hi Imar,
yes I put the code in page behind .cs but in
aspx not sure how it should look.

i tried adding the same structure but it wont fire when
i demo it.

Do I make a new javacript block or put the code next to user name
check

thanks
On Tuesday, August 31, 2010 11:42:02 PM matt said:
ok got it working now.
just one thing if they carry on completing other textboxes even though it displays error on the will they be able
to submit the form at the end or will it block it
On Wednesday, September 01, 2010 6:13:03 AM Imar Spaanjaars said:
Hi matt,

>> on the will they be able to submit the form at the end or will it block it

Not exactly sure if this makes sense, but I guess the answer is: why don't you try it out?

The UserName check is more or less optional in my initial example. Since .NET carries out the same check at the server again, it doesn't really matter if you submit or not.
If you want to prevent the from from being submitted at the client, you need to write some additional JavaScript, to disable the submit button for example. Otherwise you may need to take some precautions at the srver.

Cheers,

Imar
On Saturday, October 23, 2010 12:29:06 AM litlmike said:
I tried you method and it worked great, thanks!

However, you made a mention of ASP.NET 4 and modifying the code JavaScript and ClientIDMode.  But, when I delete those lines of code and set it to "static", the method no longer works.  Any suggestions?
On Saturday, October 23, 2010 7:29:05 AM Imar Spaanjaars said:
Hi litlmike,

It works for me when I use the sample page in a new ASP.NET 4.0 web site, set the ClientIDMode to static on the CreateUserWizard and then use the two lines of code I listed.

Can you confirm the client ID of the controls are UserName and UserNameRequired respectively in the client side HTML? And what happens when you debug this code?

Imar
On Saturday, October 23, 2010 3:26:41 PM litlmike said:
Thanks for your response.  I did resolve the issue after some trial and error, I misunderstood what you mean when you said:
"
This way you can simplify the code to read:

var userNameTextBox = $get('UserName');
var userNameRequiredMessage = $get('UserNameRequired');"

I took that mean reducing the entire block (listed below) to just the 2 lines, instead of "replacing" those 2 lines and keeping the rest the same.

"var userNameTextBox =
   $get('<%= CreateUserWizard1.ClientID %>_CreateUserStepContainer_UserName');
var userNameRequiredMessage =
   $get('<%= CreateUserWizard1.ClientID %>_CreateUserStepContainer_UserNameRequired');
var errorMessage = document.createElement('span');
errorMessage.style.visibility = 'hidden';
errorMessage.style.color = 'red';
errorMessage.innerText = 'User name already taken';
userNameRequiredMessage.insertBefore(errorMessage);
"
On Sunday, October 24, 2010 8:59:24 AM Imar Spaanjaars said:
Hi litlmike,

Ah, I see. No, it's just the code that finds the controls that is easier; you still need the other code to carry out operations on those controls.

Cheers,

Imar
On Friday, April 29, 2011 1:24:43 PM Mark said:
Is there a version of this article's source code in Visual Basic that I can download?
On Friday, April 29, 2011 1:42:36 PM Imar Spaanjaars said:
Hi Mark,

Nope, there isn't. But there's exactly one line of c# code in there (excluding the function definition) so it shouldn't be too hard to translate....

Cheers,

Imar
On Tuesday, September 13, 2011 7:53:50 AM Peter Schneider said:
Hi Imar;

I am working thru your example and my only issue is that I can't get the SignUp.aspx to display using the frontend master page.  When I try to use it, I get the following error:

Content controls have to be top-level controls in a content page or a nested master page that references a master page.

Other than this, it works fine.  Thank you for such excellect examples, both in your Wrox book and your personal site.!!!

Pete
On Tuesday, September 13, 2011 12:55:19 PM Imar Spaanjaars said:
Hi Peter,

Sounds like your SignUp page is not a true content page, but a regular ASPX page. To fix that, in Visual Studio create a new page based on a Master, and tghen manually copy the contents of your SignUp page into this new page.

Hope this helps,

Imar
On Monday, November 14, 2011 12:05:28 PM Sophia said:
Hi Imar,
This post is really nice. It is working fine in IE, chrome but it is not showing results in FF. What is the problem with FF?

Thanks
On Monday, November 14, 2011 12:12:29 PM Imar Spaanjaars said:
Hi Sophia,

Search the comments for FireFox and you'll find a solution.

Cheers,

Imar
On Monday, November 14, 2011 12:34:28 PM Sophia said:
Hi Imar,

I did not get you. You mean by searching on google or anything else?
On Monday, November 14, 2011 12:42:12 PM Imar Spaanjaars said:
No, the comments on this article. Others came up with the same question before.

Cheers,

Imar
On Monday, November 14, 2011 1:14:23 PM Sophia said:
Hi Imar,
Thanks, it works now...
But still a question. In the code below what magic has happened? And how you determine that Firefox added error message as a child of the error message from the CreateUserWizard. Since that control is hidden by default you never see the "user name taken" message....

errorMessage.innerHTML = 'User name already taken';
userNameRequiredMessage.parentNode.appendChild(errorMessage);
On Monday, November 14, 2011 2:13:47 PM Imar Spaanjaars said:
>> Since that control is hidden by default you never see the "user name taken" message....

It may be hidden from the UI, but it's there in the final HTML in the browser. I looked at the code to see how things were set up.

parentNode.appendChild then finds the parent of the error message and then adds the new child, so it ends up as a sibling to the error message.

Cheers,

Imar
On Tuesday, May 22, 2012 6:01:03 AM vivek mishra said:
Hi Imar, I just used the code and for me it is working fine on all major browsers.
Also, I have one question. I want to validate(not by using regular expression validator as it can be easily bypassed) the email id provided by the user to make sure it is a valid email id at registration without sending an email. Is it possible with java script? Please reply if it is possible.

Thanks for the article,
Vivek Mishra
On Tuesday, May 22, 2012 6:54:00 AM Imar Spaanjaars said:
Hi vivek,

No, you can't do that with JavaScript. You can only check if the address is syntactically valid, not if it exists.

Cheers,

Imar

On Wednesday, May 30, 2012 9:41:42 AM Richard Laing said:

I don't know how you find time to respond to all these queries personally, but it's appreciated (and yes, I am going to make a donation!).

I am working through Beginning ASP.NET 4.0 and got to this page via the link in Chapter 16. I cannot get this extra code to work, even after copying and pasting from the provided source code file. Is it perhaps something to do with the 'insertBefore' method(?) mentioned above but not found in any of my code? Am I missing something there?
On Wednesday, May 30, 2012 9:23:35 PM Imar Spaanjaars said:
Hi Richard,

Which browser are you testing this on? Can you send me the code for the page by e-mail? (You can find my e-mail address in the book). Alternatively, post a message in the book's own forum at the Wrox site (http://p2p.wrox.com) where it's easier to share code and discuss this.

Cheers,

Imar
On Friday, April 05, 2013 1:05:22 PM Wilson Tan said:
I love your your book Beginning ASP.NET 4. I learned ASP.NET from scratch using only your book. Very organized and easy to follow.

I applied this client side name check in the signup.aspx page in your example in the book but I am getting this error message:

202
Error: Unable to get value of the property 'parentNode': object is null or undefined

What is causing this error?

Thanks.
On Friday, April 05, 2013 1:12:07 PM Imar Spaanjaars said:
Hi Wilson,

Are you using my code as-is or did you make any modifications? Maybe you named your elements differently?

Which browser are you using?

Imar
On Friday, April 05, 2013 2:43:57 PM Wilson Tan said:
I really appreciate your quick response.

I'm using IE 9.

I used your code as-is, although I converted the code to VB.

The JScript debugger is pointing to this line as the source of the error:

userNameRequiredMessage.parentNode.appendChild(errorMessage);

Thanks.

On Friday, April 05, 2013 2:48:46 PM Imar Spaanjaars said:
Odd. Are you sure the names of the controls are the same? Does <%= CreateUserWizard1.ClientID %> result in the ID of the element? And does your HTML contain the relevant error message element?

Imar
On Friday, April 05, 2013 3:10:06 PM Wilson Tan said:
Are you sure the names of the controls are the same? I created the page exactly as instructed in the exercise so the control names are the same. The page works without this clientside name check.

Does <%= CreateUserWizard1.ClientID %> result in the ID of the element? I do not know exaclty how to get the answer for this.

And does your HTML contain the relevant error message element?
" var errorMessage = document.createElement('span');
        errorMessage.style.visibility = 'hidden';
        errorMessage.style.color = 'red';
        errorMessage.innerHTML = 'Username already taken.';
        userNameRequiredMessage.parentNode.appendChild(errorMessage);"

The error message displays when I enter a username that is already in the database. But when I enter a valid new username, I get the error message whne I click on the Create User button. The username however is created successfully.
On Friday, April 05, 2013 3:18:29 PM Imar Spaanjaars said:
What I mean is this code:

var userNameRequiredMessage =
   $get('<%= CreateUserWizard1.ClientID %>_CreateUserStepContainer_UserNameRequired');

What does that give you in the client side HTML? Does $get work for you on other elements?

Also have you tried a different browser? Does it work then?

It's a bit hard for me to suggest something without seeing the page at work, or the underlying HTML. Try posting this on a forum such as http://p2p.wrox.com so I can look at the full design time code as well as the page at run-time.

Cheers,

Imar
On Friday, April 05, 2013 3:50:12 PM Wilson Tan said:
Looks like the problem is browser specific. I don't get the error in Firefox and Chrome.

I think the problem is with IE 9. When I click on View Source, nothing comes up, so I can't even see the client HTML.

Using Firefox, here's what I got:

        var userNameRequiredMessage = $get('cpMainContent_CreateUserWizard1_CreateUserStepContainer_UserNameRequired');
        
Is there a work around to make this work with IE 9?

Thanks.
On Friday, April 05, 2013 6:42:48 PM Imar Spaanjaars said:
Hard to tell without seeing your full source. I don't have IE 9 here at the moment so I can't check for myself.

Imar
On Monday, September 02, 2013 9:51:49 PM Curt Phillips said:
Hi Imar. I am working through Beginning ASP.net for c# and vb. I implemented the method in this article with no problem. Could you help me understand how to customize it so the create user button is disabled/enabled depending on whether the username is already in use. I'm curious about how to do it with both c# and javascript if either one would work. I gave it a shot like this in c# (also I have the ClientIDMode="Static" for the createuserwizard):

[WebMethod]
        public static Boolean UserNameExists(string yourName)
        {
            bool DisableButton = Membership.GetUser(yourName) != null;
            if ( DisableButton == true)
            {
              CreateUserWizard1.StepNextButtonButton.enabled = false;
            }
            else
            {
                CreateUserWizard1.StepNextButtonButton.enabled = true;        
            }
            return DisableButton;
        }
Thanks for your time and the great book!
Curt
On Tuesday, September 03, 2013 6:09:20 PM Imar Spaanjaars said:
Hi Curt,

Good to hear you like the book.

Since you're making an AJAX call to a static method, there is no server side button you can access. You could take the result of the call and use it in client side script to disable the button.

Hope this helps,

Imar

Talk Back! Comment on Imar.Spaanjaars.Com

I am interested in what you have to say about this article. Feel free to post any comments, remarks or questions you may have about this article. The Talk Back feature is not meant for technical questions that are not directly related to this article. So, a post like "Hey, can you tell me how I can upload files to a MySQL database in PHP?" is likely to be removed. Also spam and unrealistic job offers will be deleted immediately.

When you post a comment, you have to provide your name and the comment. Your e-mail address is optional and you only need to provide it if you want me to contact you. It will not be displayed along with your comment. I got sick and tired of the comment spam I was receiving, so I have protected this page with a simple calculation exercise. This means that if you want to leave a comment, you'll need to complete the calculation before you hit the Post Comment button.

If you want to object to a comment made by another visitor, be sure to contact me and I'll look into it ASAP. Don't forget to mention the page link, or the QuickDocId of the document.

For more information about the Talk Back feature, check out this news item.