MVC结构查询

本文关键字:查询 结构 MVC | 更新日期: 2023-09-27 18:08:00

晚上,我追逐我的尾巴试图得到一个简单的ASP的结构。NET MVC正确。从一开始我就应该说我对ASP和MVC完全陌生,我以前用过一点c#。

举例来说,假设我正在尝试检查两个信息,例如用户输入的用户名和密码,与存储所有用户信息的数据库。如果他们输入了正确的凭据,则会显示用户信息的摘要,如果没有,则会被带回到登录页面(视觉上永远不会离开),并提示请再试一次。

所以我有我的主页(登录页)视图,HomeControllerHomeIndexViewModel。同样,我有一个帐户视图,一个AccountController和一个AccountUserViewModel(也有AccountIndexViewModel,但这并没有真正使用)。

Home视图"接受"(通过它的控制器)一个HomeIndexVM作为它的模型:

@model ViewModels.HomeIndexViewModel
@using (Html.BeginForm("User", "Account", FormMethod.Post))
{
    if (@Model.PreviousAttempts)
    { 
        <p><b>Username or password were not recognised please try again.</b></p>
    }
    <p>Username: @Html.TextBoxFor(x => x.Username)</p>
    <p>Password: @Html.PasswordFor(x => x.Password)</p>
    <input id="btnLogin" type="submit" value="Login" />
}

HomeController:

public class HomeController : Controller
{
    public ActionResult Index(bool invalidLogin = false)
    {
        var vm = new HomeIndexViewModel() { Username = string.Empty, Password = string.Empty, PreviousAttempt = invalidLogin };
        return View(vm);
    }
}

最后是HomeIndexViewModel.cs

public class HomeIndexViewModel
{        
    public string Username { get; set; }
    public string Password { get; set; }
    public bool PreviousAttempt { get; set; }
}

我认为到目前为止还可以。现在点击登录按钮,它将发布到Account, User。

public ActionResult User(UserLogin userLogin)
{
    if (!ModelState.IsValid)
        return RedirectToAction("Index", "Home", new { invalidLogin = true });
    // Match username and password against database
    User user = userLogin.IsValid(userLogin.Username, userLogin.Password);
    if (user != null)
    {
         return this.View(user);
    }
    return RedirectToAction("Index", "Home", new { invalidLogin = true });
}

这里有几件事,您可以看到重定向回到登录页面,带有true标志,以显示登录失败消息。

但更重要的是,显然它不会工作,因为它需要一个UserLogin作为参数。这是一个模型对象,包含:

public class UserLogin
{
    private NewDBSolution_v1Entities accountsDB = new NewDBSolution_v1Entities();
    [Required, MinLength(2)]
    public string Username { get; set; }
    [DataType(DataType.Password), Required]
    public string Password { get; set; }
    public bool PreviousAttempts { get; set; }
    public User IsValid(string username, string password) // Ideally use the local username and password properties rather than pass in as they are the same.
    {            
        // Match username and password against database and return full user info if match found, otherwise return null
    }
}

所以我在问什么…在这种情况下,帐户的用户操作采取HomeIndexViewModel,即使它是Home而不是帐户相关,这是最佳实践吗?或者我应该像我最初做的那样传递一个模型对象,并将其用于验证(我不喜欢的是验证是在从不同视图传递的对象中完成的,如果这有意义的话?)

如何最好地将信息从视图捆绑到Action?我意识到vm和模型对象编译器并不关心他们只是类,但我想得到我的关注点分离正确。

基本上这里所有我需要的是用户名和密码从Home视图,应该捆绑在一个VM或M?

在我看来,有很多类可能只是稍微不同,所以为什么不创建一个并使用它呢?我想这就是继承的由来,但如果每个子类只添加一个不同的属性,你会得到很多吗?

无论如何,我一直在兜圈子,关于最好的结构方式。我确实在某个地方读到过,vm基本上应该是模型上的掩码/适配器,这样视图只能看到它需要的东西。但是这些虚拟机没有与它们相关联的模型。

我现在有点漫无目的了,如果有人能弄明白这一点,给我一些指点,我将非常感激,谢谢。

MVC结构查询

我认为你可能在兜圈子,因为你已经创建了登录视图在HomeController。登录代码是与account相关的,所以为什么不把它放在AccountController中呢?

如果你遵循Post-Redirect-Get模式,就像你在这里尝试做的那样,我倾向于把帖子发到与你收到的相同的动作上。我会将登录操作移到AccountController中。我会将执行数据库检查的代码移到一个单独的类中,而不是将其保留在模型中。也许是会员资格提供者之类的。我将把该提供程序传递给控制器——这允许控制器和模型对象不再担心如何决定用户是否有效。他们可以让提供者告诉他们。

保持实体(无论是UI还是域)简单通常是一种好做法。它们当然应该包含数据库连接对象。

你的控制器看起来像这样:

public class AccountController : Controller
{
    private readonly IMembershipProvider membershipProvider;
    public AccountController(IMembershipProvider membershipProvider)
    {
        this.membershipProvider= membershipProvider;
    }
    public ActionResult Login()
    {
        var viewModel = new LoginViewModel();  
        return View(viewModel);
    }
    [HttpPost]
    [ValidateAntiForgeryToken]
    public ActionResult Login(LoginViewModel viewModel)
    {
        if (!ModelState.IsValid)
        {
            return View(viewModel);
        }
        var user = membershipProvider.Find(viewModel.Username, viewModel.Password);
        if (user != null)
        {
            membershipProvider.SignIn(user, true);
            return RedirectToAction("Index", "Home");
        }
        ModelState.AddModelError("Login", "Your credentials were not recognised. Please try again.");
        return View(viewModel);
    }
}

因为Login操作对于Get和Post都具有相同的名称,所以视图变得更简单。你会在Views/Account文件夹中有一个Login.cshtml视图,如下所示:

@model ViewModels.LoginViewModel
@using (Html.BeginForm())
{
    @Html.AntiForgeryToken() // good practice to add this, ties in with the ValidateAntiForgeryToken attribute on the Post action
    @Html.ValidationSummary() // displays model errors if there are any
    <p>Username: @Html.TextBoxFor(x => x.Username)</p>
    <p>Password: @Html.PasswordFor(x => x.Password)</p>
    <input id="btnLogin" type="submit" value="Login" />
}

要回答这个特定的问题:

如何最好地将信息从视图捆绑到Action?

如果可以的话,让默认的模型绑定器来处理。它非常强大,可以处理从表单传回的数据的大多数情况。所有模型绑定器所做的就是匹配表单集合中的名值对,表单集合是在表单发送到类的属性时传回的。如果您的类具有字符串属性UsernamePassword,则模型绑定器将填充它们。就模型绑定器而言,您提供的实际类是无关的。这将同样有效(但其他参与项目的人不会为此感谢你!):

public class Dave
{
    [Required, MinLength(2)]
    public string Username { get; set; }
    [DataType(DataType.Password), Required]
    public string Password { get; set; }
}

然后在Post动作中:

[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Login(Dave dave)
{
    ...
}

有帮助吗?