How to Remote validate email or check if email exists

Komengem picture Komengem · Jan 17, 2013 · Viewed 11.4k times · Source

I am trying to check if Email exists at registration so that there are no duplicate emails in the database. I am doing this with the MVC4 default Internet Application but i have added an Email field to the RegisterviewModel. I also noticed that, this is well done with UserName field but i have no idea how they did it. I tried to follow This blog below but no luck as there is no response when i click create. Tug Berk. When i use firebug i am getting Status: 302 Found when Json action excutes

This is what i have have:

UserProfile

[Table("UserProfile")]
public class UserProfile
{
    [Key]
    [DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
    public int UserId { get; set; }
    public string UserName { get; set; }
    public string Email { get; set; }
}

RegisterViewModel

public class RegisterModel
{
    [Required]
    [Display(Name = "User name")]
    public string UserName { get; set; }

    [Required]
    [DataType(DataType.EmailAddress)]
    [Display(Name = "Email")]
    [Remote("DoesUserEmailExist", "Account", HttpMethod = "POST", ErrorMessage = "Email address already exists. Please enter a different Email address.")]
    public string Email { get; set; }

    [Required]
    [StringLength(100, ErrorMessage = "The {0} must be at least {2} characters long.",      MinimumLength = 6)]
    [DataType(DataType.Password)]
    [Display(Name = "Password")]
    public string Password { get; set; }

    [DataType(DataType.Password)]
    [Display(Name = "Confirm password")]
    [System.ComponentModel.DataAnnotations.Compare("Password", ErrorMessage = "The password and confirmation password do not match.")]
    public string ConfirmPassword { get; set; }
}

Register Action

    [HttpPost]
    [AllowAnonymous]
    [ValidateAntiForgeryToken]
    public ActionResult Register(RegisterModel model)
    {
        if (ModelState.IsValid)
        {
            try //CreateUserAndAccount
            {
                WebSecurity.CreateUserAndAccount(model.UserName, model.Password, new { model.Email });
                WebSecurity.Login(model.UserName, model.Password);
                return RedirectToAction("Index", "Home");

            }
            catch (MembershipCreateUserException e)
            {
                ModelState.AddModelError("", ErrorCodeToString(e.StatusCode));
            }
        }

        // If we got this far, something failed, redisplay form
        return View(model);
    }

Json Action

[HttpPost]
    public JsonResult DoesUserEmailExist(string email)
    {

        var user = Membership.GetUserNameByEmail(email);

        return Json(user == null);
    }

Edit: Added Register View

     @model Soccer.Models.RegisterModel

     @{
     ViewBag.Title = "Register";
     }

     <hgroup class="title">
     <h1>@ViewBag.Title.</h1>
     <h2>Create a new account.</h2>
     </hgroup>

 @using (Html.BeginForm()) {
 @Html.AntiForgeryToken()
 @Html.ValidationSummary()

 <fieldset>
    <legend>Registration Form</legend>
    <ol>
        <li>
            @Html.LabelFor(m => m.UserName)
            @Html.TextBoxFor(m => m.UserName)
        </li>
        <li>
            @Html.LabelFor(m => m.Email)
            @Html.TextBoxFor(m => m.Email)
        </li>
        <li>
            @Html.LabelFor(m => m.Password)
            @Html.PasswordFor(m => m.Password)
        </li>
        <li>
            @Html.LabelFor(m => m.ConfirmPassword)
            @Html.PasswordFor(m => m.ConfirmPassword)
        </li>
    </ol>
    <input type="submit" value="Register" />
</fieldset>
 }

 @section Scripts {
 @Scripts.Render("~/bundles/jqueryval")
 }

Is there something i shouldn't be doing or is there another simpler option i could take here?

Answer

Komengem picture Komengem · Jan 18, 2013

For starter, i would like to say thanks to JOBG for intracting with me through this. What i showed in the question above should still work if you use Database First as it demands that you set Email column UNIQUE in the database, however to make it work with EF Code First i had to do the following to the register action, also note that with this solution, you don't need Remote Annotation in the model:

Register Action

    [HttpPost]
    [AllowAnonymous]
    [ValidateAntiForgeryToken]
    public ActionResult Register(RegisterModel model)
    {
        if (ModelState.IsValid)
        {
            // Insert a new user into the database
            using (UsersContext db = new UsersContext())
            {
                UserProfile email = db.UserProfiles.FirstOrDefault(u => u.Email.ToLower() == model.Email.ToLower());
                try
                {
                    // Check if email already exists
                    if (email == null)
                    {
                        WebSecurity.CreateUserAndAccount(model.UserName, model.Password, new { Email = model.Email });
                        WebSecurity.Login(model.UserName, model.Password);
                        return RedirectToAction("Index", "Home");
                    }
                    else
                    {
                        ModelState.AddModelError("Email", "Email address already exists. Please enter a different email address.");
                    }
                }
                catch (MembershipCreateUserException e)
                {

                    ModelState.AddModelError("", ErrorCodeToString(e.StatusCode));
                }
            }
        }

        // If we got this far, something failed, redisplay form
        return View(model);
    }

Note: Take note that this action validates Email before UserName and that it requires that you take a trip to the database atleast once before you create your object.