2

I have written the code below for resetting users passwords (am using the aspnet membership api) in an C# MVC application, and tested successfully on a sample tutorial application (MVC Music Store). Skip to the end if you wish to read problem description first.

InactiveUsers View (Partial View)

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<System.Web.Security.MembershipUserCollection>" %>

<table class="normal" style="width: 100%; background-color: White;">
       <tr>
            <th>User Name</th>
            <th>Last Activity date</th>
            <th>Locked Out</th>
       </tr>
       <%foreach (MembershipUser user in Model){ %>


       <tr>
           <td><%: Html.RouteLink(user.UserName, "AdminPassword", new { username = user.UserName }) %></td>
           <td><%: user.LastActivityDate %></td>
           <td><%: user.IsLockedOut %></td>
       </tr>


       <% }%>
    </table>

InactiveUsers Controller

public ActionResult InactiveUsers()
    {
        var users = Membership.GetAllUsers();

        return View(users);

    }

changeUserPassword GET and POST Controllers

 public ActionResult changeUserPassword(string username)
        {
            ViewData["username"] = username;

            return View();
        }


        [HttpPost]
        public ActionResult changeUserPassword(ChangePasswordModel model, FormCollection values)
        {
            string username = values["username"];
            string password = values["password"];
            string confirmPassword = values["confirmPassword"];

            MembershipUser mu = Membership.GetUser(username);

            if (password == confirmPassword)
            {
                if (mu.ChangePassword(mu.ResetPassword(), password))
                {
                    return RedirectToAction("Index", "ControlPanel");
                }
                else
                {
                    ModelState.AddModelError("", "The current password does not meet requirements");
                }
            }

            return View();
        }

I also modified the Global.asax.cs file to cater for my route in the InactiveUsers partial:

        // Added in 10/01/11

        RouteTable.Routes.MapRoute(
            "AdminPassword", // routename
            "ControlPanel/changeUserPassword/{username}",
            new { controller = "ControlPanel", action = "changeUserPassword", username = UrlParameter.Optional }
            );

        // END

Now, when I tested on the MVC Music Store, all of my usernames were just words, e.g. Administrator, User, etc. However now I am applying this code to a situation in my workplace and it's not working out quite as planned. The usernames used in my workplace are actually email addresses and I think this is what is causing the problem.

When I click on the RouteLink in the partial InactiveUsers view, it should bring me to the reset password page with a url that looks like this:

http://localhost:83/ControlPanel/changeUserPassword/example1@gmail.com, HOWEVER,

what happens when I click on the RouteLink is an error is thrown to say that the view changeUserPassword cannot be found, and the URL looks like this:

http://localhost:83/ControlPanel/changeUserPassword/example1%40gmail.com - See how the '@' symbol gets messed up?

I've also debugged through the code, and in my GET changeUserPassword, the username is populating correctly: example1@gmail.com, so I'm thinking it's just the URL that's messing it up?

If I type in the URL manually, the changeUserPassword view displays, however the password reset function does not work. An 'Object reference not set to an instance of an object' exception is thrown at the if (mu.ChangePassword(mu.ResetPassword(), password)) line.

I think if I could solve the first issue (URL '@' symbol problem) it might help me along with my second issue.

Any help would be appreciated :)

Stack Trace - as requested

Source Error:

An unhandled exception was generated during the execution of the current web request. Information regarding the origin and location of the exception can be identified using the exception stack trace below.

Stack Trace:

[InvalidOperationException: The view 'changeUserPassword' or its master was not found. The following locations were searched:
~/Views/ControlPanel/changeUserPassword.aspx
~/Views/ControlPanel/changeUserPassword.ascx
~/Views/Shared/changeUserPassword.aspx
~/Views/Shared/changeUserPassword.ascx]
   System.Web.Mvc.ViewResult.FindView(ControllerContext context) +495
   System.Web.Mvc.ViewResultBase.ExecuteResult(ControllerContext context) +208
   System.Web.Mvc.ControllerActionInvoker.InvokeActionResult(ControllerContext controllerContext, ActionResult actionResult) +39
   System.Web.Mvc.<>c__DisplayClass14.<InvokeActionResultWithFilters>b__11() +60
   System.Web.Mvc.ControllerActionInvoker.InvokeActionResultFilter(IResultFilter filter, ResultExecutingContext preContext, Func`1 continuation) +391
   System.Web.Mvc.<>c__DisplayClass16.<InvokeActionResultWithFilters>b__13() +61
   System.Web.Mvc.ControllerActionInvoker.InvokeActionResultWithFilters(ControllerContext controllerContext, IList`1 filters, ActionResult actionResult) +285
   System.Web.Mvc.ControllerActionInvoker.InvokeAction(ControllerContext controllerContext, String actionName) +830
   System.Web.Mvc.Controller.ExecuteCore() +136
   System.Web.Mvc.ControllerBase.Execute(RequestContext requestContext) +111
   System.Web.Mvc.ControllerBase.System.Web.Mvc.IController.Execute(RequestContext requestContext) +39
   System.Web.Mvc.<>c__DisplayClass8.<BeginProcessRequest>b__4() +65
   System.Web.Mvc.Async.<>c__DisplayClass1.<MakeVoidDelegate>b__0() +44
   System.Web.Mvc.Async.<>c__DisplayClass8`1.<BeginSynchronous>b__7(IAsyncResult _) +42
   System.Web.Mvc.Async.WrappedAsyncResult`1.End() +141
   System.Web.Mvc.Async.AsyncResultWrapper.End(IAsyncResult asyncResult, Object tag) +54
   System.Web.Mvc.Async.AsyncResultWrapper.End(IAsyncResult asyncResult, Object tag) +40
   System.Web.Mvc.MvcHandler.EndProcessRequest(IAsyncResult asyncResult) +52
   System.Web.Mvc.MvcHandler.System.Web.IHttpAsyncHandler.EndProcessRequest(IAsyncResult result) +38
   System.Web.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() +8841105
   System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously) +184
109221793
  • 16,477
  • 38
  • 108
  • 160
  • When a view cannot be found the exception usually tells you what are the searched locations. Have you verified that `~/Views/ControlPanel/ChangeUserPassword.aspx` exists? – Darin Dimitrov Jan 10 '11 at 10:28
  • Hi Darin, Yeah it *definitely* does exist. It I type in the URL manually even, the view displays. It's just when I click on the RouteLink that I get this error message. – 109221793 Jan 10 '11 at 10:31
  • Could you post the exact error message stack trace please? – Darin Dimitrov Jan 10 '11 at 10:35
  • Hi Darin, stack trace posted. Thanks – 109221793 Jan 10 '11 at 10:43
  • That's very strange. The exception indicates that the file does not exist. I've tested your scenario and it worked fine with `@` and `%40` in the URL. Are you running this in Cassini? Is it .NET 4.0? – Darin Dimitrov Jan 10 '11 at 10:48
  • That's very strange :-S As I mentioned above, this code worked perfectly with another example I had. I'm not running it in Cassini no, I'm just using the built in ASP.Net Development Server to run it at the moment. It is .Net 4.0 also. Kinda stumped :-S The Global.asax.cs is laid out slightly differently in this application than the tutorial (I didn't build this application), however I have inserted the Routes.MapRoute in the correct place so I shouldn't imagine this would make too much difference :-S – 109221793 Jan 10 '11 at 10:54
  • @Darin, Just to let you know I've fixed it. I changed the routeLink line in the InactiveUsers view to the following: <%: Html.ActionLink(user.UserName, "changeUserPassword", new { username = user.UserName }) %>...seems to be working ok for me now. Thanks for your replies :) – 109221793 Jan 10 '11 at 13:33

2 Answers2

0

Do you realize that %40 is the URL encoded version of the @-symbol? The data in your URL is not messed up, it's URL encoded. This is needed because your data contains a symbol that has a meaning in URL's, so it must be encoded in a way that doesn't break the URL.

You can try URL encoding/decoding it out here: http://meyerweb.com/eric/tools/dencoder/

Dirk
  • 2,167
  • 2
  • 20
  • 29
0

Better to use System.Web.HttpContext.Current.Server.UrlDecode(url1). whether the Url is encoded or not it returns a raw url like "http://localhost:83/ControlPanel/changeUserPassword/example1@gmail.com" and you can get userName from it.

string userName = System.Web.HttpContext.Current.Server.UrlDecode(userName);
RouteTable.Routes.MapRoute(
            "AdminPassword", // routename 
            "ControlPanel/changeUserPassword/" + userName,
            new { controller = "ControlPanel", action = "changeUserPassword", username = UrlParameter.Optional }
            ); 
Daniel B
  • 3,109
  • 2
  • 33
  • 42