部分视图和验证(回发)
标题可能不太清楚(因为我找不到更好的标题),但我试图弄清楚的是,当你有一个普通(而不是部分)视图时,通常有一个 GET 操作方法,它只是渲染具有视图模型的新实例的视图,以及接受视图模型实例作为参数的 POST 操作方法(通常具有相同的名称)。在 POST 操作方法中,您检查 ModelState 是否有效,您将执行您应该执行的操作,如果无效,您将使用相同的视图模型实例再次渲染视图以显示任何错误。
这实际上是我真正喜欢 ASP.NET MVC 的原因之一,但是它如何与部分视图一起使用呢?如果我使用视图模型的实例渲染回部分视图,它只会显示带有白色背景的部分视图,而不是整个 Web 应用程序的上下文。如果我回发一个传递视图模型实例的普通视图,则会导致 StackOverflowException。
这是一个示例:
public ActionResult Login()
{
return PartialView(new LoginViewModel());
}
[HttpPost]
public ActionResult Login(LoginViewModel dto)
{
bool flag = false;
if (ModelState.IsValid)
{
if (_userService.AuthenticateUser(dto.Email, dto.Password, false)) {
var user = _userService.GetUserByEmail(dto.Email);
var uSession = new UserSession
{
ID = user.Id,
Nickname = user.Nickname
};
SessionManager.RegisterSession(SessionKeys.User, uSession);
flag = true;
if(dto.RememberMe)
{
_appCookies.Email = dto.Email;
_appCookies.Password = dto.Password;
}
}
}
if (flag)
return RedirectToAction("Index", "Home");
else
{
ViewData.Add("InvalidLogin", "The login info you provided were incorrect.");
return View(dto); //causes a StackOverflowException
}
}
更新: 这是登录视图:
@inherits ModelWebViewPage<Sharwe.MVC.ViewModels.LoginViewModel>
<div class="userFormHeader"><h2>Login</h2></div>
<div id="loginBox">
@using(Html.BeginForm("Login", "User", FormMethod.Post))
{
@Html.ValidationSummary(true)
<div id="loginFormFields">
<div class="display-field">@this.TextBox(m => m.Email).Class("emailField").Attr("rel", "email").Class("autoText")</div>
<div class="display-field">@this.TextBox(m => m.Password).Class("passwordField").Attr("rel", "password").Class("autoText")</div>
<div>@this.CheckBox(m => m.RememberMe) <span class="smallText">remember me</span></div>
</div>
<div id="loginFormActions">
<div><input type="submit" id="loginSubmit" class="okButton" name="loginSubmit" value="Ok" /></div>
<div> @this.Html.ActionLink("forgot password", "ForgotPassword", "User", new { @class = "verySmallText" } )</div>
</div>
}
</div>
那么我应该如何执行此操作?有什么建议吗?
更新: (在 Darin 的回答之后)
这是我的登录操作方法现在的样子:
[HttpPost]
public ActionResult Login(LoginViewModel dto)
{
bool flag = false;
if (ModelState.IsValid)
{
if (_userService.AuthenticateUser(dto.Email, dto.Password, false))
{
var user = _userService.GetUserByEmail(dto.Email);
var uSession = new UserSession
{
ID = user.Id,
Nickname = user.Nickname
};
SessionManager.RegisterSession(SessionKeys.User, uSession);
flag = true;
if (dto.RememberMe)
{
//create the authentication ticket
var authTicket = new FormsAuthenticationTicket(
1,
user.Id.ToString(), //user id
DateTime.Now,
DateTime.Now.AddMinutes(20), // expiry
true, //true to remember
"", //roles
"/"
);
//encrypt the ticket and add it to a cookie
var cookie = new HttpCookie(FormsAuthentication.FormsCookieName,
FormsAuthentication.Encrypt(authTicket));
Response.Cookies.Add(cookie);
}
}
}
if (flag)
{
return Json(new { redirectTo = Url.Action("Index", "Home") });
}
else
{
ViewData.Add("InvalidLogin", "The login info you provided were incorrect.");
return PartialView(dto);
}
}
The title might not be so clear (because I couldn't find a better one) but what I'm trying to figure out is when you have a normal (as opposed to partial) view, usually there's a GET action method which simply renders the view with a new instance of the view model, and a POST action method (usually with the same name) that accepts an instance of the view model as a parameter. Inside the POST action method, you check the ModelState if it's valid you do what you're supposed to do, if not you render the view again with the same view model instance in order to display any errors.
That's actually one of the things I really like about ASP.NET MVC, but how does that work with Partial Views? If I render back a partial view with the instance of the view model, it only displays the partial view with a white background, out of the context of the whole web application. And if I post back a normal View passing the instance of the view model, that causes a StackOverflowException.
Here's an example:
public ActionResult Login()
{
return PartialView(new LoginViewModel());
}
[HttpPost]
public ActionResult Login(LoginViewModel dto)
{
bool flag = false;
if (ModelState.IsValid)
{
if (_userService.AuthenticateUser(dto.Email, dto.Password, false)) {
var user = _userService.GetUserByEmail(dto.Email);
var uSession = new UserSession
{
ID = user.Id,
Nickname = user.Nickname
};
SessionManager.RegisterSession(SessionKeys.User, uSession);
flag = true;
if(dto.RememberMe)
{
_appCookies.Email = dto.Email;
_appCookies.Password = dto.Password;
}
}
}
if (flag)
return RedirectToAction("Index", "Home");
else
{
ViewData.Add("InvalidLogin", "The login info you provided were incorrect.");
return View(dto); //causes a StackOverflowException
}
}
UPDATE: Here's the login view:
@inherits ModelWebViewPage<Sharwe.MVC.ViewModels.LoginViewModel>
<div class="userFormHeader"><h2>Login</h2></div>
<div id="loginBox">
@using(Html.BeginForm("Login", "User", FormMethod.Post))
{
@Html.ValidationSummary(true)
<div id="loginFormFields">
<div class="display-field">@this.TextBox(m => m.Email).Class("emailField").Attr("rel", "email").Class("autoText")</div>
<div class="display-field">@this.TextBox(m => m.Password).Class("passwordField").Attr("rel", "password").Class("autoText")</div>
<div>@this.CheckBox(m => m.RememberMe) <span class="smallText">remember me</span></div>
</div>
<div id="loginFormActions">
<div><input type="submit" id="loginSubmit" class="okButton" name="loginSubmit" value="Ok" /></div>
<div> @this.Html.ActionLink("forgot password", "ForgotPassword", "User", new { @class = "verySmallText" } )</div>
</div>
}
</div>
So how should I do this? Any suggestions?
UPDATE: (after Darin's answer)
Here's how my Login action method now looks like:
[HttpPost]
public ActionResult Login(LoginViewModel dto)
{
bool flag = false;
if (ModelState.IsValid)
{
if (_userService.AuthenticateUser(dto.Email, dto.Password, false))
{
var user = _userService.GetUserByEmail(dto.Email);
var uSession = new UserSession
{
ID = user.Id,
Nickname = user.Nickname
};
SessionManager.RegisterSession(SessionKeys.User, uSession);
flag = true;
if (dto.RememberMe)
{
//create the authentication ticket
var authTicket = new FormsAuthenticationTicket(
1,
user.Id.ToString(), //user id
DateTime.Now,
DateTime.Now.AddMinutes(20), // expiry
true, //true to remember
"", //roles
"/"
);
//encrypt the ticket and add it to a cookie
var cookie = new HttpCookie(FormsAuthentication.FormsCookieName,
FormsAuthentication.Encrypt(authTicket));
Response.Cookies.Add(cookie);
}
}
}
if (flag)
{
return Json(new { redirectTo = Url.Action("Index", "Home") });
}
else
{
ViewData.Add("InvalidLogin", "The login info you provided were incorrect.");
return PartialView(dto);
}
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
正如您在评论部分所述,AJAX 是您的一个选择,以下是您如何通过 AJAX 化登录表单来继续操作。 jquery 表单插件 非常适合这项工作,我强烈推荐它。
因此,您可以在登录视图中向登录表单提供一个 id:
然后包含一个 javascript,它将对该表单进行 AJAxify:
现在剩下的就是从
Login
控制器操作返回部分视图,以防万一有错误:我们还需要处理成功的情况。这可以通过返回 JSON 字符串来完成:
然后调整客户端脚本:
As you stated in the comments section that AJAX is an option for you here's how you could proceed by AJAXifying the login form. The jquery form plugin is excellent for this job and I would strongly recommend it.
So you could provide an id to the login form in the login view:
and then include a javascript which will AJAxify this form:
Now all that's left is to return a partial view from the
Login
controller action in case there is an error:We need to handle the success case as well. This could be done by returning a JSON string:
and then adapt the client script: