How to Remote validate email or check if email exists
Asked Answered
A

2

5

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?

Alveolate answered 17/1, 2013 at 3:16 Comment(0)
A
8

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.

Alveolate answered 17/1, 2013 at 23:52 Comment(0)
U
3

Hard to tell without the View code but my first suspect is js, make sure you have jQuery validation plug-in set in place, it should be firing the validation with .blur() event, inspect the page with firebug(or similar) and look for an ajax POST that hits DoesUserEmailExist when the field loses focus, if this is not happening then something is missing in client side as Remote attribute and the service DoesUserEmailExist looks ok.

Side Note: Make sure you have the UNIQUE constraint in the database for the email field, this is the only real way to make sure you have no duplicate emails, even with the DoesUserEmailExist verification, because of concurrency.

Ulema answered 17/1, 2013 at 5:3 Comment(10)
I have added a Register view. As for UNIQUE constraint, i am not sure if that's possible with code first entity framework. Considering that, this has already been assigned to UserIdAlveolate
You are right EF5 does not support UNIQUE with code first argh, maybe that's one of the reasons why i don't use it, i prefer db first... as for the question, this bundle ~/bundles/jqueryval renders the needed js so that's not the problem, did you try firebug to check for the request?Ulema
I just run it, Status: 302 Found That's what i got, something is definitely wrong hereAlveolate
Just a quick notice, i just realised that under Post only the first letter of the email string is assigned to Email. Should it be like that? Email=kAlveolate
302 Found, there is something wrong with your route, is the action and controller name correct in Remote Attribute? if you put a breakpoint in your DoesUserEmailExist Does it gets hit?Ulema
Well, everything is noted above in the question. I think its all thereAlveolate
Mmm looking again 302 is a redirect. Is DoesUserEmailExist behind some authentication? Maybe you are missing [AllowAnonymous]...Ulema
Well, i looked into [AllowAnonymous] no luck. Is there a difference between WebSecurity.cs and Membership.cs because if you look at register action, we are calling WebSecurity.cs while in Json action Membership.cs is called. After all the tries, i feel as though the problem is bewteen those 2 classes.Alveolate
I just realised something, this method demands that the column in the table is set to UNIQUE.....i may need to find a different method to achieve what i want.Alveolate
+1 for sticking around, thanks alot. What i did works with Database First. The solution i just posted works with EF Code First, atleast to my knowledgeAlveolate

© 2022 - 2024 — McMap. All rights reserved.