ASP.NET MVC - Как поддерживать ModelState с другого контроллера?

У меня есть HomeController с действием индекса, которое показывает представление Index.aspx. Он имеет логин/пароль для входа в раздел. Когда пользователь нажимает кнопку отправки, он отправляется в действие "Вход" в AccountController.

        <% Html.BeginForm("Login", "Account", FormMethod.Post); %>

В этом действии он проверяет правильность имени пользователя и пароля и, если он недействителен, отправляет пользователя обратно на страницу входа с сообщением о том, что учетные данные были плохими.

    [HttpPost]
    public ActionResult Login(LoginViewModel Model, string ReturnUrl)
    {
        User user = MembershipService.ValidateUser(Model.UserName, Model.Password);
        if (user != null)
        {
            //Detail removed here
            FormsService.SignIn(user.ToString(), Model.RememberMe);
            return Redirect(ReturnUrl);
        }
        else
        {
            ModelState.AddModelError("", "The user name or password provided is incorrect.");
        }
        // If we got this far, something failed, redisplay form
        return RedirectToAction("Index", "Home");  // <-- Here is the problem.  ModelState is lost.
    }

Но здесь проблема: ValidationSummary всегда пуста, потому что мы теряем модель, когда мы RedirectToAction.

Итак, вопрос: Как отправить пользователя на действие на другом контроллере без перенаправления?

Ответ 1

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

return View("~/Views/Home/Index.aspx", model);

или

Также часто возникает отдельная страница входа и перенаправление на эту страницу, если сбой входа в систему. Обе страницы будут отправляться в одно и то же действие входа. Facebook делает это, например.

или

Поскольку вы хотите отобразить сообщение об ошибке

return RedirectToAction("Index", "Home", new { LoginAttempts = 1 });

то в действии индекса введите параметр LoginAttempts и выберите соответствующее сообщение об ошибке.

Ответ 2

Используйте TempData для сохранения состояния между запросами. Используйте специальные атрибуты для удобства, как показано здесь.

Несколько моментов, чтобы упомянуть:

  • Не возвращать View прямо из вашего POST-действия, уважать шаблон Post-Redirect-Get.
  • Не злоупотребляйте TempData. Он только должен сохранять состояние модели прямо перед перенаправлением и извлекать его сразу после перенаправления.

Ответ 3

Три варианта

  • Вы можете вызвать действие напрямую, но клиентская сторона не изменит URL. Поэтому вместо вызова RedirectToAction вы можете напрямую вызвать метод Index() класса HomeController.

    HomeController c = new HomeController();
    c.ViewData = this.ViewData;
    return c.Index(data);
    

    Это немного сложно. Возможно, вам придется установить и другие вещи, кроме ViewData, которые необходимы для ModelState.

  • Вы также можете использовать словарь TempData и заполнить его любыми данными, которые вы хотите, и использовать их.

  • Самый простой, когда вы предоставляете полный путь к представлению

    return View("~/Views/Home/Index.aspx", data);
    

Лучшее предложение, используемое крупными игроками

Если мы посмотрим, как другие сайты используют этот сценарий. Возьмем, например, Twitter (Поскольку @David говорит Facebook, очевидно, делает то же самое). Вы можете войти в систему из действия Home/Index (так сказать, если оно было разработано с использованием Asp.net MVC). Но при неудаче входа в систему отображается отдельная страница входа, в которой отображаются ошибки проверки. В вашем случае это будет Account/SignIn. Это будет иметь смысл, и вы можете напрямую вернуть свое представление с ошибками проверки. Когда все будет в порядке, вы сделаете это, как вы это делаете сейчас. Вернитесь обратно к Home/Index.

Ответ 4

Ну, вы всегда можете это сделать

return View("~/Views/Home/Index.aspx", myModel);

Это не реальное перенаправление, URL-адрес клиента все равно укажет на /login/, но по крайней мере у вас есть ваше модальное состояние

Ответ 5

Попробуйте использовать

return View("Index", "Home", Model)