Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

How to Check for a Duplicate Username. A Quick Example.

0.00/5 (No votes)
12 Feb 2017 1  
A quick example to show how to check for a duplicate username

We’re overwhelmed with tutorials and guides about .NET development. There’s a plethora of how-to guides on every aspect of MVC. I’ve written plenty myself! The trouble is, we don’t just focus on a bit of it, if we’re building a website. We need to understand all those bits, for sure. More than that though, we need to understand how they all fit together. That’s the tricky part. There are not as many articles around like that.

This is the first article in a series of end-to-end examples. In each one, we’ll take a business requirement and work it through from end-to-end. This week, we’re looking at unique username verification. Here’s our requirement:

In order to ensure that my chosen username is not in use already, as a new user of End To End Examples Inc™, I want to see if someone has taken the username I enter.

Let’s include some acceptance criteria, which help define the requirement:

  • Verification must not happen until the user has finished entering his/her username
  • Verification must not happen if the user leaves the field blank
  • The user must see that verification is taking place
  • The user must see success if the username is available
  • The user must see an error if the username is not available

Got an example you’d like me to explain end-to-end? Let me know in the comments!

A Little Housekeeping

The first thing we need is a new MVC project. Crack open Visual Studio and create one. I’m calling mine EndToEndExamples. Let’s stick with standard MVC and no authentication. Make sure you include Web API libraries as well, because we’ll need them.

Now that’s created, let’s tidy it up a bit. We can lose the standard About and Contact views. Delete Views/Home/About.cshtml and Views/Home/Contact.cshtml. Delete the About() and Contact() methods from HomeController. Remove the action links to those 2 pages from _Layout.cshtml as well. They’re in the navbar.

I’ve changed the default namespace for my project to Levelnis.Learning.EndToEndExamples.Web. If you make a similar change, don’t forget to update the Web.config in the Views folder. My system.web.webPages.razor section looks like this:

<system.web.webPages.razor>
  <host factoryType="System.Web.Mvc.MvcWebRazorHostFactory, System.Web.Mvc, 
   Version=5.2.3.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
  <pages pageBaseType="System.Web.Mvc.WebViewPage">
    <namespaces>
      <add namespace="System.Web.Mvc" />
      <add namespace="System.Web.Mvc.Ajax" />
      <add namespace="System.Web.Mvc.Html" />
      <add namespace="System.Web.Optimization"/>
      <add namespace="System.Web.Routing" />
      <add namespace="Levelnis.Learning.EndToEndExamples.Web" />
    </namespaces>
  </pages>
</system.web.webPages.razor>

See the custom namespace at the bottom? That needs to match the default namespace for your project. If it doesn't, you'll run into problems within your views. Now we’ve got a project, let’s think about what we’re about to build. Remember, we’re working on a specific business requirement here. We shouldn’t add anything outside of that requirement. So, what steps do we need to perform?

  1. Add a username textbox to the view, to capture user input
  2. Add some client-side code to show the user that something’s happening
  3. Add a Web API controller to receive that input
  4. Add some logic to check if the username exists and send back an indication
  5. Add some client-side code to send the input to the server and receive the response
  6. Add some client-side code to display the most appropriate indicator

Step 1: Add a Textbox to the View

Let’s add a form to the Index view so we can get things moving. We’ll start by adding a ViewModel to our Index view:

namespace Levelnis.Learning.EndToEndExamples.Web.Models
{
    public class IndexViewModel
    {
        public string Username { get; set; }
    }
}

Let’s update the controller, so we can pass the model to the view:

namespace Levelnis.Learning.EndToEndExamples.Web.Controllers
{
    using System.Web.Mvc;
    using Models;

    public class HomeController : Controller
    {
        public ActionResult Index()
        {
            var model = new IndexViewModel();
            return View(model);
        }
    }
}

We also need to add the model and a textbox to the view. Here’s what Index.cshtml now looks like:

@model Levelnis.Learning.EndToEndExamples.Web.Models.IndexViewModel
@{
    ViewBag.Title = "Home Page";
}
<div class="row">
    <div class="col-sm-8 col-xs-12">
        <h3>Duplicate username validator</h3>
        <form>
            <div class="form-group">
                @Html.LabelFor(m => m.Username)
                @Html.TextBoxFor(m => m.Username, new { @class = "form-control" })
            </div>
        </form>
    </div>
</div>

Step 2: Show the User that Something’s Happening

Nice and simple. When we hook everything up, we’ll need an indicator to show whether the username is available. We’ll use font awesome for this, so we need to add it. It’s a Nuget package, so go ahead and run this command from the Package Manager Console:

Install-Package FontAwesome

We can now add some icons to our form. These will show whether we find the username or not. We’ll make the form an inline form as well. Change the Index.cshtml form code to this:

<form class="form-inline">
    <div class="form-group">
        @Html.LabelFor(m => m.Username)
    </div>
    <div class="form-group">
        @Html.TextBoxFor(m => m.Username, new { @class = "form-control" })
    </div>
    <span class="hide" id="Indicator"> 
    <i class="fa fa-circle-o-notch fa-spin fa-lg"> </i> </span>
    <span class="hide btn btn-success" 
    id="IndicatorOk"> <i class="fa fa-check fa-lg"> 
    </i> </span>
    <span class="hide btn btn-danger" id="IndicatorError"> 
    <i class="fa fa-times fa-lg"> </i> </span>
</form>

View the original article.

Notice we’ve got 3 hidden spans, which we’ll toggle as necessary. We now need to show the spinner after we enter some text. We’ll hook into the onblur event for this. Remember our acceptance criteria? We need to make sure we only run this check if there’s data in the username field. Let’s add a scripts section to the Index view:

@section scripts{
    <script>
        (function($) {
            $('#Username').on('blur',
                function () {
                    var username = $(this).val();
                    if (username.length > 0) {
                        $('#Indicator').removeClass('hide');
                    }
                });
        }(jQuery))
    </script>

Step 3: Add a Web API Controller

We’ll leave the client for a moment and take a look at the API controller. This is where we’ll decide whether the username is available or not. Add an empty Web API 2 controller to Controllers/Api (create the folder if it doesn’t exist). Here’s mine:

using System.Web.Http;

namespace Levelnis.Learning.EndToEndExamples.Web.Controllers.Api
{
    public class VerifyUsernameController : ApiController
    {
        public IHttpActionResult Get()
        {
            return Ok("OK");
        }
    }
}

If I now browse to api/verifyusername, I see the OK string:

<string xmlns="http://schemas.microsoft.com/2003/10/Serialization/">OK</string>

Wait a second, though. Wouldn’t it be better if that was JSON instead of XML? We can fix that in a jiffy. We need to add a formatter. Add a BrowserJsonFormatter class to your project:

namespace Levelnis.Learning.EndToEndExamples.Web
{
    using System;
    using System.Net.Http.Formatting;
    using System.Net.Http.Headers;

    public class BrowserJsonFormatter : JsonMediaTypeFormatter
    {
        public BrowserJsonFormatter()
        {
            SupportedMediaTypes.Add(new MediaTypeHeaderValue("text/html"));
        }

        public override void SetDefaultContentHeaders
            (Type type, HttpContentHeaders headers, MediaTypeHeaderValue mediaType)
        {
            base.SetDefaultContentHeaders(type, headers, mediaType);
            headers.ContentType = new MediaTypeHeaderValue("application/json");
        }
    }
}

Now that’s in place, we need a single line in the WebApiConfig to use it. Pop this as the first line in the Register method:

config.Formatters.Add(new BrowserJsonFormatter());

Now, when you refresh the page, you should see the stringOK”, instead of that clunky XML. So far so good. We need to pass a username into our method next. Let’s add a parameter to the Get method:

public IHttpActionResult Get(string username)
{
    return Ok(username);
}

Step 4: Check If the username Exists

This just checks we’re seeing the parameter we pass in. Now let’s do something useful with it. In a real-life system, we’d go to a database at this point. We’d have a bunch of business rules to satisfy around username validity. We might even suggest alternatives if the username is gone. We’re not doing any of that though. We’ll keep our example nice and simple. We’ll use a JSON data source. Add a UsernameData class to the Api folder:

namespace Levelnis.Learning.EndToEndExamples.Web.Controllers.Api
{
    using System.Collections.Generic;

    public class UsernameData
    {
        public const string DataSource = @"
{
  ""Usernames"":[
    ""smiler"",
    ""dave.smith"",
    ""happygolucky17"",
    ""tom2017"",
    ""ab1234"",
  ]
}";
        public IList<string> Usernames { get; set; }
    }
}

All we’ve got here is a string of JSON data, which we’ll deserialise into a UsernameData object. We’ll use JSON.NET for this. All our JSON data contains is a bunch of strings in an array. As long as we have somewhere for JSON.NET to put those strings when it deserialises, we’ll be fine. The Usernames property does that for us. We need a VerifyUsernameResponse to hold the data we send back from our controller:

public class VerifyUsernameResponse
{
    public string OriginalUsername { get; set; }
    public bool IsAvailable { get; set; }
}

Let’s update the Get method:

namespace Levelnis.Learning.EndToEndExamples.Web.Controllers.Api
{
    using System.Linq;
    using System.Web.Http;
    using Newtonsoft.Json;

    public class VerifyUsernameController : ApiController
    {
        public IHttpActionResult Get(string username)
        {
            var usernameData = 
            JsonConvert.DeserializeObject<UsernameData> (UsernameData.DataSource);
            var usernames = usernameData.Usernames.Select(x => x.ToLower());
            var isUsernameTaken = usernames.Contains(username.ToLower());
            var response = new VerifyUsernameResponse
            {
                OriginalUsername = username,
                IsAvailable = !isUsernameTaken
            };
            return Ok(response);
        }
    }
}

What’s going on here? The first line uses JSON.NET to parse the JSON string into a UsernameData object. This works because our UsernameData class contains a Usernames property, which is a list of strings. After that, we check if the username is in our list or not. I'm mapping the original username here so we can see it make the round trip.

Step 5: Send the username to the API and Grab the Response

Back we go to the client now. We need to make an AJAX call. Update the blur event handler to call the API, like so:

$('#Username').on('blur',
    function () {
        var username = $(this).val();
        if (username.length > 0) {
            $('#Indicator').removeClass('hide');
            $.ajax({
                url: '/api/verifyusername',
                method: 'GET',
                data: { username: username }
            }).done(function(response) {
                $('#Indicator').addClass('hide');
            });
        }
    });

At this stage, we’re making the call to the API. We’re then hiding the working indicator once we’ve got the response. The final piece is to display the appropriate indicator. Let’s look at that next.

Step 6: Display the Appropriate Indicator

The final step is to show success or failure as appropriate. To see this in action, I’ve added a wait of 1 second to the API call. If you want to do the same, add this to the top of the API Get method:

Thread.Sleep(1000);

That will allow you to see the working indicator before the success or failure one takes its place. Otherwise, it all happens too quickly. Here are the final changes to the script:

@section scripts{
    <script>
        (function ($) {
            var resetIndicators = function() {
                $('#Indicator').addClass('hide');
                $('#IndicatorOk').addClass('hide');
                $('#IndicatorError').addClass('hide');
                $('#IndicatorLabel').addClass('hide');
            }

            $('#Username').on('blur',
                function () {
                    resetIndicators();
                    var username = $(this).val();
                    if (username.length > 0) {
                        $('#Indicator').removeClass('hide');
                        $.ajax({
                            url: '/api/verifyusername',
                            method: 'GET',
                            data: { username: username }
                        }).done(function(response) {
                            $('#Indicator').addClass('hide');
                            var indicatorId = response.IsAvailable ? 'Ok' : 'Error';
                            var indicatorText = response.IsAvailable ? ' is available!' : 
                                                ' has been taken, sorry!';
                            $('#Indicator' + indicatorId).removeClass('hide');
                            $('#IndicatorLabel').removeClass('hide');
                            $('#IndicatorLabel').html(response.OriginalUsername + indicatorText);
                        });
                    }
                });
        }(jQuery))
    </script>

We display the original username again. This is just so you can see that it’s made the full round-trip to the API and back. We hide all the indicators before we call the API each time. Then we show the one we want, based on what comes back.

Can you think of an example you’d like me to explain end-to-end? Let me know in the comments!

View the original article.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here