views:

643

answers:

4

I have been working on this problem for 2 days now and it's an easy problem and I just can't see the error in the code, even after comparing it with other projects where this works.

Could you help out?

I'm working on the account section of my website using ASP.NET Membership and the account controller class that is generated with ASP.NET MVC.

However when registering and the user leaves some fields blank the entered text dosn't show up when the page is redisplayed.

Here is the register post action

[AcceptVerbs(HttpVerbs.Post)]
    public ActionResult Register(FormCollection formValues)
    {

        ViewData["PasswordLength"] = MembershipService.MinPasswordLength;

        if (ValidateRegistration(formValues))
        {

            // Attempt to register the user
            MembershipCreateStatus createStatus = MembershipService.CreateUser(formValues["username"], formValues["password"], formValues["email"]);

            if (createStatus == MembershipCreateStatus.Success)
            {
                FormsAuth.SignIn(formValues["username"], false /* createPersistentCookie */);

                return RedirectToAction("CreateCustomer", "Account");
            }
            else
            {
                ModelState.AddModelError("_FORM", ErrorCodeToString(createStatus));
            }
        }

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

And the ValidateRegistration function

 private bool ValidateRegistration(FormCollection formValues)
    {
        if (String.IsNullOrEmpty(formValues["userName"]))
        {
            ModelState.AddModelError("username", "You must specify a username.");
        }
        if (String.IsNullOrEmpty(formValues["email"]))
        {
            ModelState.AddModelError("email", "You must specify an email address.");

        }
        if (formValues["password"] == null || formValues["password"].Length < MembershipService.MinPasswordLength)
        {
            ModelState.AddModelError("password",
                String.Format(CultureInfo.CurrentCulture,
                     "You must specify a password of {0} or more characters.",
                     MembershipService.MinPasswordLength));

        }
        if (!String.Equals(formValues["password"], formValues["confirmPassword"], StringComparison.Ordinal))
        {
            ModelState.AddModelError("_FORM", "The new password and confirmation password do not match.");
        }
        if (!String.Equals(formValues["email"], formValues["confirmEmail"], StringComparison.Ordinal))
        {
            ModelState.AddModelError("confirmEmail", "The email and confirmation email addresses do not match.");
        }
        return ModelState.IsValid;
    }

And just in case here is the Register view

<%@ Page Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage" %>

Register

Create a New Account

Use the form below to create a new account.

Passwords are required to be a minimum of <%=Html.Encode(ViewData["PasswordLength"])%> characters in length.


<%= Html.ValidationSummary() %>
<% using (Html.BeginForm()) { %> Username: <%= Html.TextBox("username")%>

                <%= Html.ValidationMessage("username") %>
            </td>
            </tr>

            <tr>
            <td>Email:</td>
            <td><%= Html.TextBox("email") %>
                <%= Html.ValidationMessage("email") %>
            </td>
            </tr>
            <tr>
            <td>Confirm Email:</td>
            <td>
                <%= Html.TextBox("confirmEmail") %>
                <%= Html.ValidationMessage("confirmEmail") %>
            </td>
            </tr>
            <tr>
            <td>Password:</td>
            <td>
                <%= Html.Password("password") %>
                <%= Html.ValidationMessage("password") %>
            </td>
            </tr>
            <tr>
            <td>Confirm password:</td>
            <td>
                <%= Html.Password("confirmPassword") %>
                <%= Html.ValidationMessage("confirmPassword") %>
            </td>
            </tr>
            <tr>

            <td></td>
            <td>
                <input type="submit" value="Register" />
            </td>
            </table>
    </div>


<% } %>

+4  A: 

Right off the bat I'm guessing:

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

Off your first section is killing you. The view needs something to display and you're giving it nothing. I've handled this by creating a copy of the passed in values and pushing that back through the View method call:

View(returnedValues);

Where ResetPageCreate is a copy of the original values sent in (In case you have to some how change the information pre save attempt). This way the View has a model to work with and therefore values to display.

Programmin Tool
I was in the same boat. This completely solved my problem. Many thanks.
Slack
+1  A: 

You need to call Model.SetModelValue() because all you've done is tell the form that it has errors and what fields those errors are on BUT you haven't told it what the erroneous data is.

Try something like this:

private bool ValidateRegistration(FormCollection formValues)
{
    if (String.IsNullOrEmpty(formValues["userName"]))
    {
        ModelState.AddModelError("userName", "You must specify a username.");
    }

    if (String.IsNullOrEmpty(formValues["email"]))
    {
        ModelState.AddModelError("email", "You must specify an email address.");
    }

    if (formValues["password"] == null || formValues["password"].Length < MembershipService.MinPasswordLength)
    {
        ModelState.AddModelError("password",
            String.Format(CultureInfo.CurrentCulture,
                 "You must specify a password of {0} or more characters.",
                 MembershipService.MinPasswordLength));
    }

    if (!String.Equals(formValues["password"], formValues["confirmPassword"], StringComparison.Ordinal))
    {
        ModelState.AddModelError("confirmPassword", "The new password and confirmation password do not match.");
    }

    if (!String.Equals(formValues["email"], formValues["confirmEmail"], StringComparison.Ordinal))
    {
        ModelState.AddModelError("confirmEmail", "The email and confirmation email addresses do not match.");
    }

    if (!ModelState.IsValid)
    {
        IDictionary<string, ValueProviderResult> valueProvider = formvalues.ToValueProvider();
        ModelState.SetModelValue("userName", valueProvider["userName"]);
        ModelState.SetModelValue("email", valueProvider["email"]);
        ModelState.SetModelValue("confirmEmail", valueProvider["confirmEmail"]);
        ModelState.SetModelValue("password", valueProvider["password"]);
        ModelState.SetModelValue("confirmPassword", valueProvider["confirmPassword"]);
    }

    return ModelState.IsValid;
}

HTHs, Charles

Ps. You should trim any input you require or are going to validate it's length... I've fallen into a similar trap before :-)

Charlino
Thanks, this done the trick.
dean nolan
No comment for the down vote? Thanks. I would find it hard to down vote this given the code in the question... Not to mention the fact that it worked and marked as correct. Then again, I am bias.
Charlino
A: 

Remember that the web is a stateless environment. So when you redisplay your page for the errors, the re-rendered page knows nothing of the previously entered values unless you were to do something like Programmin Tool recommended.

Zerofiz
+1  A: 

Hey! just a quick suggestion, just saw this post and thought of doing a small suggestion

            IDictionary<string, ValueProviderResult> valueProvider = Form.ToValueProvider();
            foreach (string k in Form.Keys)
            {
                ModelState.SetModelValue(k, valueProvider[k]);
            }
            return View("Index");
elgerva
Thanks, that probably is faster.
dean nolan
How would this be fixed to work in MVC 2? It complains it cant be implicitly converted
dean nolan