I just downloaded the ASP.NET MVC Preview 5 bits from Codeplex and started on my first experiment.

One of the first things I did was to modify the default AccountController to use the new Form Posting and Form Validation features of the Preview 5, somebody probably overlooked updating those :)

If anyone else wants the reworked code, feel free to copy paste.

Note this was something done during lunch break in a hurry, it seems to all work logically, but it's possible I'll have to tune it a bit later on.

Controller:

 
[HandleError]
[OutputCache(Location = OutputCacheLocation.None)]
public class AccountController : Controller
{
    public AccountController()
        : this(null, null)
    {
    }

    public AccountController(IFormsAuthentication formsAuth, MembershipProvider provider)
    {
        FormsAuth = formsAuth ?? new FormsAuthenticationWrapper();
        Provider = provider ?? Membership.Provider;
    }

    public IFormsAuthentication FormsAuth
    {
        get;
        private set;
    }

    public MembershipProvider Provider
    {
        get;
        private set;
    }

    [Authorize]
    [AcceptVerbs("GET")]
    public ActionResult ChangePassword()
    {
        ViewData["Title"] = "Change Password";
        ViewData["PasswordLength"] = Provider.MinRequiredPasswordLength;

        return View();
    }

    [Authorize]
    [AcceptVerbs("POST")]
    public ActionResult ChangePassword(string currentPassword, string newPassword, string confirmPassword)
    {
        // Basic parameter validation
        if (String.IsNullOrEmpty(currentPassword))
        {
            ViewData.ModelState.AddModelError("currentPassword", currentPassword, "You must specify a current password.");
        }
        if (newPassword == null || newPassword.Length < Provider.MinRequiredPasswordLength)
        {
            ViewData.ModelState.AddModelError("newPassword", newPassword, String.Format(CultureInfo.InvariantCulture,
                     "You must specify a new password of {0} or more characters.",
                     Provider.MinRequiredPasswordLength));
        }
        if (!String.Equals(newPassword, confirmPassword, StringComparison.Ordinal))
        {
            ViewData.ModelState.AddModelError("newPassword", newPassword, "The new password and confirmation password do not match.");
        }

        if (ViewData.ModelState.IsValid)
        {
            // Attempt to change password
            MembershipUser currentUser = Provider.GetUser(User.Identity.Name, true /* userIsOnline */);
            bool changeSuccessful = false;
            try
            {
                changeSuccessful = currentUser.ChangePassword(currentPassword, newPassword);
            }
            catch
            {
                // An exception is thrown if the new password does not meet the provider's requirements
            }

            if (changeSuccessful)
            {
                return RedirectToAction("ChangePasswordSuccess");
            }
            else
            {
                ViewData.ModelState.AddModelError("password", currentPassword, "The current password is incorrect or the new password is invalid.");
            }
        }

        // If we got this far, something failed, redisplay form
        ViewData["Title"] = "Change Password";
        ViewData["PasswordLength"] = Provider.MinRequiredPasswordLength;

        return View();
    }

    public ActionResult ChangePasswordSuccess()
    {
        ViewData["Title"] = "Change Password";

        return View();
    }

    [AcceptVerbs("GET")]
    public ActionResult Login()
    {
        ViewData["Title"] = "Login";
        ViewData["CurrentPage"] = "login";

        return View();
    }

    [AcceptVerbs("POST")]
    public ActionResult Login(string username, string password, bool? rememberMe)
    {
        // Basic parameter validation
        if (String.IsNullOrEmpty(username))
        {
            ViewData.ModelState.AddModelError("username", username, "You must specify a username.");
        }

        if (ViewData.ModelState.IsValid)
        {
            // Attempt to login
            bool loginSuccessful = Provider.ValidateUser(username, password);

            if (loginSuccessful)
            {
                FormsAuth.SetAuthCookie(username, rememberMe ?? false);
                return RedirectToAction("Index", "Home");
            }
            else
            {
                ViewData.ModelState.AddModelError("*", username, "The username or password provided is incorrect.");
            }
        }

        // If we got this far, something failed, redisplay form
        ViewData["Title"] = "Login";
        ViewData["CurrentPage"] = "login";
        ViewData["username"] = username;

        return View();
    }

    public ActionResult Logout()
    {
        FormsAuth.SignOut();
        return RedirectToAction("Index", "Home");
    }

    protected override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        if (filterContext.HttpContext.User.Identity is WindowsIdentity)
        {
            throw new InvalidOperationException("Windows authentication is not supported.");
        }
    }

    [AcceptVerbs("GET")]
    public ActionResult Register()
    {
        ViewData["Title"] = "Register";
        ViewData["PasswordLength"] = Provider.MinRequiredPasswordLength;

        return View();
    }

    [AcceptVerbs("POST")]
    public ActionResult Register(string username, string email, string password, string confirmPassword)
    {
        // Basic parameter validation
        if (String.IsNullOrEmpty(username))
        {
            ViewData.ModelState.AddModelError("username", username, "You must specify a username.");
        }

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

        if (password == null || password.Length < Provider.MinRequiredPasswordLength)
        {
            ViewData.ModelState.AddModelError("password", password, String.Format(CultureInfo.InvariantCulture,
                     "You must specify a password of {0} or more characters.",
                     Provider.MinRequiredPasswordLength));
        }

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

        if (ViewData.ModelState.IsValid)
        {

            // Attempt to register the user
            MembershipCreateStatus createStatus;
            MembershipUser newUser = Provider.CreateUser(username, password, email, null, null, true, null, out createStatus);

            if (newUser != null)
            {
                FormsAuth.SetAuthCookie(username, false /* createPersistentCookie */);
                return RedirectToAction("Index", "Home");
            }
            else
            {
                ViewData.ModelState.AddModelError("*", username, ErrorCodeToString(createStatus));
            }
        }

        // If we got this far, something failed, redisplay form
        ViewData["Title"] = "Register";
        ViewData["PasswordLength"] = Provider.MinRequiredPasswordLength;
        ViewData["username"] = username;
        ViewData["email"] = email;

        return View();
    }

    public static string ErrorCodeToString(MembershipCreateStatus createStatus)
    {
        // See http://msdn.microsoft.com/en-us/library/system.web.security.membershipcreatestatus.aspx for
        // a full list of status codes.
        switch (createStatus)
        {
            case MembershipCreateStatus.DuplicateUserName:
                return "Username already exists. Please enter a different user name.";

            case MembershipCreateStatus.DuplicateEmail:
                return "A username for that e-mail address already exists. Please enter a different e-mail address.";

            case MembershipCreateStatus.InvalidPassword:
                return "The password provided is invalid. Please enter a valid password value.";

            case MembershipCreateStatus.InvalidEmail:
                return "The e-mail address provided is invalid. Please check the value and try again.";

            case MembershipCreateStatus.InvalidAnswer:
                return "The password retrieval answer provided is invalid. Please check the value and try again.";

            case MembershipCreateStatus.InvalidQuestion:
                return "The password retrieval question provided is invalid. Please check the value and try again.";

            case MembershipCreateStatus.InvalidUserName:
                return "The user name provided is invalid. Please check the value and try again.";

            case MembershipCreateStatus.ProviderError:
                return "The authentication provider returned an error. Please verify your entry and try again. If the problem persists, please contact your system administrator.";

            case MembershipCreateStatus.UserRejected:
                return "The user creation request has been canceled. Please verify your entry and try again. If the problem persists, please contact your system administrator.";

            default:
                return "An unknown error occurred. Please verify your entry and try again. If the problem persists, please contact your system administrator.";
        }
    }
}

// The FormsAuthentication type is sealed and contains static members, so it is difficult to
// unit test code that calls its members. The interface and helper class below demonstrate
// how to create an abstract wrapper around such a type in order to make the AccountController
// code unit testable.

public interface IFormsAuthentication
{
    void SetAuthCookie(string userName, bool createPersistentCookie);
    void SignOut();
}

public class FormsAuthenticationWrapper : IFormsAuthentication
{
    public void SetAuthCookie(string userName, bool createPersistentCookie)
    {
        FormsAuthentication.SetAuthCookie(userName, createPersistentCookie);
    }
    public void SignOut()
    {
        FormsAuthentication.SignOut();
    }
}

Login View:

 
<%@ Page Language="C#" MasterPageFile="~/Views/Shared/Site.Master" AutoEventWireup="true" CodeBehind="Login.aspx.cs" Inherits="GuildSite.Views.Account.Login" %>


    

Login

Please enter your username and password below. If you don't have an account, please <%= Html.ActionLink("register", "Register") %>.

<%= Html.ValidationSummary()%>
">
Username: <%= Html.TextBox("username") %>
Password: <%= Html.Password("password") %>
Remember me?

Register View:

 
<%@ Page Language="C#" MasterPageFile="~/Views/Shared/Site.Master" AutoEventWireup="true" CodeBehind="Register.aspx.cs" Inherits="GuildSite.Views.Account.Register" %>


    

Account Creation

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()%>
">
Username: <%= Html.TextBox("username") %>
Email: <%= Html.TextBox("email") %>
Password: <%= Html.Password("password") %>
Confirm password: <%= Html.Password("confirmPassword") %>
 
Comments: 3
 
  • Tod Thomson

    Hi David,

    I just stumbled upon your post while looking for something unrelated and it made me want to shoot myself...

    I just started on ASP.NET MVC on Monday, and I really didn't like a bunch of things about the example code (which I have been using to create my own application).

    Namely:

    1. HTTP Method handing
    2. Validation

    So I decided to "roll my own" - anyhow, as you will have guessed already, your updated example above shows me that I don't really need my stuff anymore...

    However, this has brought me to a conclusion, i.e. I know where I went wrong.

    I need better documentation on ASP.NET MVC. i.e. my lack of knowing where the documentation has lead me to re-invent the wheel.

    So my question is - where is the documentation? how did you find out about these new features? Just from the DLLs or are there any API docs etc?

    If you could give me any pointers around documentation, it would be much appreciated.

    Cheers!

    Tod.

     
     
  • Hi Tod,

    It's not yet in beta, so don't hope for a complete full documentation :)

    However, here is the power of blogs, I'll link the most useful links about mvc:

    http://blog.wekeroad.com/mvc-storefront/ - Rob Conery, great series about a full blown mvc app

    http://weblogs.asp.net/scottgu/ - Scott Guthrie, you'll find all info on new releases on his blog

    http://www.asp.net/mvc/ - Official downloads, videos and other stuff

    Enjoy :)

     
     
  • You're been voted!!
    Track back from http://webdevvote.com/AspNet/David_Cumps_Modified_MVC_AccountController_for_Preview_5

     
     
  • Leave a reply
    Items marked with * are required. (Name, Email, Comment)
    Comment is missing some required fields.
     
     
     
    To make sure you are not a computer, please type in the characters you see.