【问题标题】:How do I create a concise and RESTful wizard under MVC?如何在 MVC 下创建一个简洁的 RESTful 向导?
【发布时间】:2010-10-17 10:59:48
【问题描述】:

我尝试在构建应用程序时尽可能使用 RESTful,但我不确定的一件事是如何创建向导式工作流程,既要 RESTful 又简洁。

以多页注册流程为例。

选项 1:我可以为每个步骤创建一个控制器,并在用户到达该步骤(或返回该步骤)时调用 new 或 edit。我以 step1_controller、step2_controller 等结尾...

选项 2: 我可以创建一个控制器,并使用参数、会话变量、状态机等来跟踪他们在注册过程中的位置。所以我有 signup_controller/step?id=1

第一个选项是严格的 REST,但不是很简洁,并以一些额外的控制器结束。第二种方案更简洁,但打破了REST,我愿意这样做,但我不会掉以轻心。

还有更好的选择吗?

我正在使用 ruby​​ on rails,但这个问题适用于其他 MVC 实现,例如 ASP.NET MVC

【问题讨论】:

    标签: ruby-on-rails asp.net-mvc model-view-controller rest


    【解决方案1】:

    如果你在这里应用一些 DDD 逻辑,与 MVC 中的“M”互补,UI 的状态(注册进度)属于应用层,它可以直接与域和基础设施层对话(四层:UI 、应用程序、领域和基础设施)。 DDD的概念让你先“思考”如何在代码中解决方案。让我们一步一步来...

    这是进度条

    您要在此处保持的状态是注册的步骤或进度。所以,我的第一步是记录进度或“步骤”。例如,第 1 步:获取用户名/密码,第 2 步:获取电子邮件。在这种情况下,我将应用逻辑将模型“移动”到下一步。最有可能使用 RegistrationService (RegistrationService.NextStep()) 上的 NextStep() 方法。

    啊,不过属于App层

    我将在应用层创建一个名为 RegistrationService 的服务。我会在这里放置一个名为 NextStep() 的方法。但请记住,域不会在这里保存模型的状态。在这种情况下,您希望将状态集中在应用程序层。所以在这种情况下,NextStep() 不会作用于模型对象(因为它不是域责任的一部分),而是作用于 UI。因此,您需要一些东西来保留注册过程的状态。

    远离领域模型,ViewModel 怎么样?

    所以现在我们知道我们必须在 UI 中保留某些东西的状态。 MVC 允许一个称为 ViewModels 的概念(在 ASP.NET MVC 中,不确定 RoR 叫什么)。 ViewModel 表示将由视图和/或部分视图显示的模型。

    ViewModel 将是保存此对象状态的绝佳场所。让我们称之为 RegistrationProgressViewModel() 并在其上粘贴 NextStep() 方法。当然,这意味着应用层必须保留 RegistrationProgressViewModel 的位置,而应用层将根据 NextStep 操作更改其内部结构。如果它很复杂,您可能需要在应用层创建一个 RegistrationProgressService() 并将 NextStep() 放在其中以抽象您的逻辑。

    如何传递 ViewModel?

    最后一点是如何跟踪该对象的状态。由于 Web 应用程序是无状态的,因此您必须通过应用程序以外的其他方式保留控制权。在这种情况下,我将恢复为:1)将 ViewModel 序列化到客户端并让客户端来回传递它,或者 2)保留 ViewModel 的服务器端副本,并来回传递某种类型的标识符到客户并返回。

    这是一个值得思考的好例子,因为我自己还没有这样做过。对于#2,保存此 ViewModel 状态的最安全和最保险的方法是将其持久化到基础设施层(是的,APP 层可以直接与基础设施层对话)。这对我来说似乎有很多工作,因为有些东西可能会消失,我的数据库中会有部分注册。

    但是,#2 会将用户的私人信息(用户名、密码、电子邮件、CC # 等)全部保存在服务器端,而不是来回传递。

    终于有答案了!

    所以,经过它,我们想出了:

    • 在应用层创建 RegistrationProgressViewModel()。
    • 在应用层中使用 NextStep(ViewModel vm) 方法创建一个 RegistrationProgressService()。
    • 执行 NextStep() 时,通过 Infrastructure 层将 ViewModel 持久化到数据库中。

    这样,您无需跟踪视图或 UI 本身上的“step?id=2”,因为随着您的前进,ViewModel 会不断更新和更新(经过身份验证、验证、持久化到数据库)。

    因此,您的下一个问题是在 UI 中“前进”。这可以通过 1 个控制器轻松完成,使用步骤或命名步骤。

    抱歉,我在下面编写 C# 代码,因为那是我的语言。

    public class RegistrationController : Controller
    {
      // http://domain.com/register
      public ActionResult Index()
      {
        return View(new RegistrationProgressViewModel);
      }
    
      // http://domain.com/register
      // And this posts back to itself.  Note the setting 
      // of "CurrentStep" property on the model below.
      //
      public ActionResult Index(
          RegistrationProgressViewModel model)
      {
    
        // The logic in NextStep() here checks the
        // business rules around the ViewModel, verifies its
        // authenticity, if valid it increases the
        // ViewModel's "CurrentStep", and finally persists
        // the viewmodel to the DB through the Infrastructure
        // layer.
        //
        RegistrationProgressService.NextStep(model);
    
        switch (model.CurrentStep)
        {
          case 2:
            // wire up the View for Step2 here.
            ...
            return View(model);
          case 3:
            // wire up the View for Step3 here.
            ...
            return View(model);
          case 4:
            // wire up the View for Step4 here.
            ...
            return View(model);
          default:
            // return to first page
            ...
            return View(model);
        }
      }
    }
    

    您会注意到,这将验证模型内部状态的“业务逻辑”抽象到了 RegistrationProcessService.NextStep() 方法中。

    很好的锻炼。 :)

    最后,您的“RESTful”网址是一个漂亮而干净的 POST 到:/register,它需要一个具有特定属性的 ViewModel。如果 ViewModel 无效,/register 不会进入下一步。

    【讨论】:

    • 感谢发帖。这是一个很好的治疗。它基本上是选项 2。它不是我关心的 RESTful url,但实际上坚持使用 7 个定义的动词。
    • 不是“最快”的方式,但我的回答是在“将 CurrentStep”提升到 2 或 3 之前使用业务规则/逻辑来评估对象(viewModel)进行身份验证,等等。但是包含在其中的业务规则可以控制你的“哪一步”。
    • 所以视图模型必须包含在应用层中对吗?否则会与 UI 层产生循环依赖。
    • @Vince:对于这个答案,是的,他们将在应用程序层中。对于某些项目,我在 App_Code 中创建了我的代码,并指出该目录是我的“应用程序层”。其他项目,我为我的应用程序层创建了一个 ProjectName.UI,为我的 UI 创建了 ProjectName.UI.Website,以及我的 ProjectName.UI.WcfService 等。请注意,它们都共享 ProjectName.UI。但总而言之,当我使用 POCO 类迁移到 Entity Framework 4 时,这一切都变得无声无息。我现在为我的 WCF 层生成另一组 POCO,隔离我的域 - 但仍然使用来自同一个容器的相同 T4!
    【解决方案2】:

    我实际上不太关心在一次性向导中维护 REST。我认为 REST 是最重要的,它具有可重复的操作——您希望 url 基本上是可收藏的,这样无论您何时去那里,您都可以返回相同的数据视图。在多步骤向导中,您的依赖项无论如何都会破坏 REST 的这种观点。我的感觉是拥有一个具有潜在独立操作或使用查询参数来指示您正在执行的步骤的单个控制器。这就是我构建激活向导(需要多个步骤)的方式。

    【讨论】:

    • +1 表示“REST 并不总是正确的方法”。看看那里的一些 REST 宣传,你可能会认为做非 REST 会让你成为一个穴居人。
    • 这是一个很好的观点 - REST 在哪里有用,在哪里可能没用。对我来说,最大的原因是我发现它确实让事情变得干净,当你认为你需要打破 REST 时,你应该好好考虑一下。
    • 好答案。 REST 对于向导来说真的没有意义。如果第 2 步依赖于第 1 步的数据,如果用户将第 2 步添加为书签会怎样?
    【解决方案3】:

    虽然答案确实是非常不错的选择,但我仍然使用以下方法:

    Shoulders of Giants | A RESTful Wizard Using ASP.Net MVC

    绝对值得一看。虽然我必须说,这里给出的答案让我想在有时间的时候重新制作这个巫师。

    【讨论】:

    • +1 我错过了几年前发布的这个答案。是的,这是在 MVC 中与我的回答相反的另一种方式。回过头来看,我现在使用更独立的“UserMovedToNextStep()”事件驱动工作流,为我的向导练习更多的事件溯源模式。它允许非常容易地向后和向前回复 - 但是,需要做更多的工作。
    猜你喜欢
    • 2011-11-16
    • 2016-10-10
    • 1970-01-01
    • 1970-01-01
    • 2010-11-25
    • 2021-10-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多