C# MVC: контроллер сброса пароля пользователя: проблемы с адресами электронной почты в качестве имен пользователей

Я написал приведенный ниже код для сброса паролей пользователей (я использую API членства aspnet) в приложении C # MVC и успешно протестировал образец учебного приложения (MVC Music Store). Пропустите до конца, если вы хотите сначала прочитать описание проблемы.

Просмотр InactiveUsers (частичный просмотр)

<%@ 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

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

        return View(users);

    }

changeUserPassword Контроллеры GET и POST

 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();
        }

Я также изменил файл Global.asax.cs, чтобы он соответствовал моему маршруту в партиале InactiveUsers:

        // Added in 10/01/11

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

        // END

Теперь, когда я тестировал в музыкальном магазине MVC, все мои имена пользователей были просто словами, например. Администратор, пользователь и т. д. Однако теперь я применяю этот код к ситуации на своем рабочем месте, и он работает не совсем так, как планировалось. Имена пользователей, используемые на моем рабочем месте, на самом деле являются адресами электронной почты, и я думаю, что это и является причиной проблемы.

Когда я нажимаю на RouteLink в частичном представлении InactiveUsers, я должен перейти на страницу сброса пароля с URL-адресом, который выглядит следующим образом:

http://localhost:83/ControlPanel/changeUserPassword/[email protected] , ОДНАКО,

что происходит, когда я нажимаю на RouteLink, возникает ошибка, говорящая о том, что представление changeUserPassword не может быть найдено, и URL-адрес выглядит следующим образом:

http://localhost:83/ControlPanel/changeUserPassword/example1%40gmail.com - Видите, как перепутался символ «@»?

Я также отлаживал код, и в моем GET changeUserPassword имя пользователя заполняется правильно: [email protected], поэтому я думаю, что это просто URL-адрес, который все испортил?

Если я ввожу URL-адрес вручную, отображается представление changeUserPassword, однако функция сброса пароля не работает. В строке if (mu.ChangePassword(mu.ResetPassword(), пароль)) создается исключение «Ссылка на объект, не являющаяся экземпляром объекта».

Я думаю, что если бы я мог решить первую проблему (проблема с символом '@' в URL), это могло бы помочь мне со второй проблемой.

Любая помощь будет оценена по достоинству :)

Трассировка стека — по запросу

Ошибка источника:

Во время выполнения текущего веб-запроса возникло необработанное исключение. Информацию о происхождении и расположении исключения можно определить с помощью приведенной ниже трассировки стека исключений.

Трассировки стека:

[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

person 109221793    schedule 10.01.2011    source источник
comment
Когда представление не может быть найдено, исключение обычно сообщает вам, какие местоположения искомые. Вы убедились, что ~/Views/ControlPanel/ChangeUserPassword.aspx существует?   -  person Darin Dimitrov    schedule 10.01.2011
comment
Привет, Дарин. Да, он определенно существует. Даже если я ввожу URL-адрес вручную, отображается представление. Просто когда я нажимаю на RouteLink, я получаю это сообщение об ошибке.   -  person 109221793    schedule 10.01.2011
comment
Не могли бы вы опубликовать точную трассировку стека сообщений об ошибках, пожалуйста?   -  person Darin Dimitrov    schedule 10.01.2011
comment
Привет Дарин, опубликована трассировка стека. Спасибо   -  person 109221793    schedule 10.01.2011
comment
Это очень странно. Исключение указывает, что файл не существует. Я проверил ваш сценарий, и он отлично работал с @ и %40 в URL-адресе. Вы запускаете это в Cassini? Это .NET 4.0?   -  person Darin Dimitrov    schedule 10.01.2011
comment
Это очень странно :-S Как я уже упоминал выше, этот код отлично работал с другим примером, который у меня был. Я не запускаю его в Cassini, нет, я просто использую встроенный сервер разработки ASP.Net для его запуска в данный момент. Это также .Net 4.0. Немного в тупике :-S Global.asax.cs в этом приложении расположен немного иначе, чем в учебнике (я не создавал это приложение), однако я вставил Routes.MapRoute в нужное место, поэтому я не должен представить это будет иметь слишком большое значение :-S   -  person 109221793    schedule 10.01.2011
comment
@Дарин, просто чтобы ты знал, что я это исправил. Я изменил строку routeLink в представлении InactiveUsers на следующее: ‹%: Html.ActionLink(user.UserName, changeUserPassword, new {username = user.UserName}) %›... теперь у меня работает нормально. Спасибо за ваши ответы :)   -  person 109221793    schedule 10.01.2011


Ответы (2)


Вы понимаете, что %40 — это закодированная в URL версия символа @? Данные в вашем URL-адресе не перепутаны, они закодированы в URL-адресе. Это необходимо, потому что ваши данные содержат символ, который имеет значение в URL-адресах, поэтому они должны быть закодированы таким образом, чтобы не нарушать URL-адрес.

Вы можете попробовать кодировать/декодировать URL здесь: http://meyerweb.com/eric/tools/dencoder/

person Dirk    schedule 10.01.2011

Лучше использовать System.Web.HttpContext.Current.Server.UrlDecode(url1). независимо от того, закодирован ли URL-адрес или нет, он возвращает необработанный URL-адрес, например «http://localhost:83/ControlPanel/changeUserPassword/[email protected]», и вы можете получить от него имя пользователя.

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 }
            ); 
person Daniel B    schedule 23.11.2011