对于简单登录,MVC模式中组件的职责是什么

时间:2011-02-16 07:41:48

标签: c# asp.net-mvc openid

我正在尝试理解MVC模式,我得到了模型负责维护状态的一般概念,View负责显示模型,Controller负责修改模型并调用相应的View (S)。我想尝试实现一个使用OpenID的简单ASP.NET MVC登录页面,以便了解它是如何工作的。

我已经下载了DotNetOpenAuth-3.4.6,我正在查看示例,特别是他们的MVC示例。不幸的是,样本实际上没有模型,只有控制器:

namespace OpenIdRelyingPartyMvc.Controllers
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Web;
    using System.Web.Mvc;
    using System.Web.Security;
    using DotNetOpenAuth.Messaging;
    using DotNetOpenAuth.OpenId;
    using DotNetOpenAuth.OpenId.RelyingParty;

    public class UserController : Controller
    {
        private static OpenIdRelyingParty openid = new OpenIdRelyingParty();

        public ActionResult Index()
        {
            if (!User.Identity.IsAuthenticated)
            {
                Response.Redirect("~/User/Login?ReturnUrl=Index");
            }

            return View("Index");
        }

        public ActionResult Logout()
        {
            FormsAuthentication.SignOut();
            return Redirect("~/Home");
        }

        public ActionResult Login()
        {
            // Stage 1: display login form to user
            return View("Login");
        }

        [ValidateInput(false)]
        public ActionResult Authenticate(string returnUrl)
        {
            var response = openid.GetResponse();
            if (response == null)
            {
                // Stage 2: user submitting Identifier
                Identifier id;
                if (Identifier.TryParse(Request.Form["openid_identifier"], out id))
                {
                    try
                    {
                        return openid.CreateRequest(Request.Form["openid_identifier"]).RedirectingResponse.AsActionResult();
                    }
                    catch (ProtocolException ex)
                    {
                        ViewData["Message"] = ex.Message;
                        return View("Login");
                    }
                }
                else
                {
                    ViewData["Message"] = "Invalid identifier";
                    return View("Login");
                }
            }
            else
            {
                // Stage 3: OpenID Provider sending assertion response
                switch (response.Status)
                {
                    case AuthenticationStatus.Authenticated:
                        Session["FriendlyIdentifier"] = response.FriendlyIdentifierForDisplay;
                        FormsAuthentication.SetAuthCookie(response.ClaimedIdentifier, false);
                        if (!string.IsNullOrEmpty(returnUrl))
                        {
                            return Redirect(returnUrl);
                        }
                        else
                        {
                            return RedirectToAction("Index", "Home");
                        }
                    case AuthenticationStatus.Canceled:
                        ViewData["Message"] = "Canceled at provider";
                        return View("Login");
                    case AuthenticationStatus.Failed:
                        ViewData["Message"] = response.Exception.Message;
                        return View("Login");
                }
            }
            return new EmptyResult();
        }
    }
}

也许样本太简单而无法实际涉及模型,因为所有状态信息都包含在Cookie中。随后,我实现了一个简单的数据库,它有一个Users表:

Users
+ user_id
+ open_id
+ last_login

我认为我需要一个LoginModel

public class LogInModel
{
    [Required]
    [DisplayName("OpenID")]
    public string OpenID { get; set; }
}

A DisplayModel

public class DisplayModel
{
    [DisplayName("User ID")]
    public string UserID{ get; set; }

    [DisplayName("OpenID")]
    public string OpenID { get; set; }

    [DisplayName("Last Login")]
    public DateTime LastLogin{ get; set; }
}

此外,我可能需要ModifyModel,但DisplayModel可以重复使用,并可能重命名为UserModel(以正确反映模型的使用情况)。

所以现在我有几个问题:

  1. 控制器是负责验证用户输入还是在输入传递给模型时完成(即在Identifier.TryParse上调用openid_identifier)?
  2. 我想允许用户登录,更改他们的信息(即user_id)并查看他们的信息(即user_id,open_id和last_login)。我需要多少个型号(ModifyModelDisplayModelLogInModel)?
  3. 模特的生命周期是多少?模型是静态存在还是仅由控制器创建并传递给视图?
  4. 添加database as a model而不是制作上述模型会更好吗?

1 个答案:

答案 0 :(得分:2)

1)可能是肯定的,但更好的方法是在ViewModel上使用数据注释

2)一个模型会做。模型应表示整体对象,在本例中为“用户”。如果每个视图所需的信息差异很大,则将它们分离到View Models中。

3)不确定你的意思。 MVC(和ASP.NET一般)基于HTTP协议,因此无状态。因此,当您点击URL时,会分配一个Controller,然后在代码需要时添加对象 - 这包括数据库连接。然后,当请求完成后,一切都消失了(无论如何都是托管资源)。尽量不要与“模特”这个词混淆。它不是物理实体,而是编程模型的一个区域。

4)通常,您的“模型”是您的Repository / DAL / ORM,它包装您的基础数据库,并代表您的域模型。您的视图不应该关注域。这是您的控制器/模型的工作。您的View应该可以满足它的需求,而不再需要。这就是为什么根据经验,永远不会直接绑定到ORM模型,使用ViewModel。


编辑 - 回应评论中的问题:

  

对不起......对于1,数据注释表明验证是在模型上执行的,但是OpenId.Identifier还提供了一些可以验证输入的函数(例如TryParse),因此它可以更加一致地完成所有操作对模型进行验证还是验证的“地点”通常不是那么严格?

将数据注释放在 ViewModel 上,这些是创建的模型的表示,以使View更加轻松。数据注释不应放在您的实际模型实体(实体框架,L2SQL等)上。数据注释应该用于输入验证(密码比较,字符长度,电话号码,电子邮件地址等)。 业务验证应在域中完成。我会说OpenId是一种服务而不是域的一部分。如果他们有一些验证功能,您可以将这些调用包装在自定义数据注释中,并将它们放在ViewModel上。这将是一个干净和一致的方法。

  

对于3,我同意HTTP协议是无状态的,但如果我理解正确,cookie是维持状态的一种方式,而模型提供了另一种方式。

你的权利,cookie是维持状态的一种方式。常见的用途是Forms身份验证票证。另一个是会议。 TempData 使用Session(以智能方式 - 自动弹出)。该模型不是“替代方式” - 它不维持状态。将在下面详细讨论。

  

在应用程序编程中,状态(即对象)通常在应用程序的整个生命周期中都是持久的,因此如果我要在控制台应用程序中创建模型,只要存在对它的引用,它就会存在。所以当你说“对象是新的”时,这是否意味着Model对象已经被实例化(比如在控制台应用程序中)而我只是“更新”它或者这是否意味着我构建了一个新模型?

在控制台应用中 - 您的权利。只要控制台应用程序正在运行,对象就会处于活动状态。但是在ASP.NET MVC Web应用程序中,“父”是一个ASP.NET工作线程,在请求进入时分配。所有必需的对象(控制器,数据库连接,存储库,域对象)都是“子”。这个线程,如果这是有道理的。一旦该线程消失,所有相关对象也都消失了。

模型没有“神奇的实例化” - 模型是您域的整体视图/表示,通常由域模型(实体,业务逻辑)和库。

唯一的例外是“模型绑定”。当您将表单提交到强类型为“模型”的[HttpPost]操作时(应该是ViewModel)。 ASP.NET MVC(通过反射)将基于HTTP POST中的字段构建此“模型”。

当您执行“UpdateModel”之类的操作时,所做的就是通过模型绑定更新您提供的操作方法中提供的对象。没有实际的数据库正在更新。

不知道我还能说些什么。你似乎对“模特”感到困惑。我建议你抓一本Steven Sanderson的 Pro ASP.NET MVC 2 Framework 一书。它太棒了,从头开始解释所有内容 - 简单来说,然后加快速度,这样你的专家就会在本书的最后。