我尝试在构建应用程序时尽可能地保持RESTful,但我不确定的一件事是如何创建向导类型的工作流程,RESTful和简洁.
例如,采用多页注册流程.
选项1:我可以为每个步骤创建一个控制器,并在用户进入该步骤(或返回到该步骤)时调用new或edit.我以step1_controller,step2_controller等结束...
选项2:我可以使用参数,会话变量,状态机 - 无论如何创建一个控制器并跟踪它们在注册过程中的位置.所以我有signup_controller/step?id = 1
第一个选项是严格的REST,但不是很简洁,最后是一些额外的控制器.第二个选项更简洁,但打破了REST,我愿意这样做,但我不会掉以轻心.
有更好的选择吗?
我在rails上使用ruby,但是这个问题适用于其他MVC实现,比如ASP.NET MVC
如果你在这里应用一些DDD逻辑,它与MVC中的"M"相称,那么UI(注册进度)的状态属于应用层,它可以直接与域和基础设施层对话(四层:UI,应用,域和基础设施).DDD的概念让您"思考"如何首先在代码中解决解决方案.让我们一步一步......
您要在此处维护的状态是注册的步骤或进度.所以,我的第一步是记录进度或"步骤".例如,步骤1:获取用户名/通行证,步骤2:获取电子邮件.在这种情况下,我将应用逻辑"移动"模型到下一步.最有可能在RegistrationService(RegistrationService.NextStep())上使用NextStep()方法.
我将在Application层中创建一个名为RegistrationService的服务.我会在这里放一个名为NextStep()的方法.但请记住,Domain不会在这里保持模型的状态.在这种情况下,您需要将状态集中在Application层.因此,在这种情况下,NextStep()将不作为模型对象(因为它不是域责任的一部分)而是作用于UI.因此,您需要一些东西来保留注册过程的状态.
所以现在我们知道我们必须在UI中保留某些东西的状态.MVC允许一个名为ViewModels的概念(在ASP.NET MVC中,不确定RoR称之为什么).ViewModel表示将由视图和/或部分视图显示的模型.
ViewModel是保存此对象状态的绝佳位置.我们称之为RegistrationProgressViewModel()并在其上粘贴NextStep()方法.当然,这意味着Application层必须保留RegistrationProgressViewModel的位置,并且APplication层将根据NextStep操作更改它的内部.如果它很复杂,您可能希望在应用程序层中创建一个RegistrationProgressService()并将NextStep()放在其中以抽象出逻辑.
最后一部分是如何跟踪该对象的状态.由于Web应用程序是无状态的,因此您必须通过应用程序之外的其他方式保留控制权.在这种情况下,我将恢复为:1)将ViewModel序列化到客户端并让客户端来回传递它,或者2)保留ViewModel的服务器端副本,并来回传递某种类型的标识符对客户而言.
这是一个很好的例子,因为我自己还没有这样做.对于#2,保存此ViewModel状态的最安全和最安全的方法是将其持久保存到Infrastructure层(是的,APp层可以直接与Infrastructure层对话).这对我来说似乎有很多工作,对于可能会死亡的事情,我会在我的数据库中进行部分注册.
但是,#2会在服务器端保留用户的私人信息(用户名,密码,电子邮件,CC#等),而不是来回传递.
因此,在完成它之后,我们提出了:
在Application层中创建RegistrationProgressViewModel().
在Application层中使用NextStep(ViewModel vm)方法创建RegistrationProgressService().
执行NextStep()时,通过Infrastructure层将ViewModel持久保存到数据库.
这样,您就不必跟踪View或UI本身上的"step?id = 2",因为ViewModel会在您前进时更新和更新(Authenticated,Verified,持久保存到DB).
因此,您的下一个关注点是在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"url是一个漂亮而干净的POST:/ register,它需要填写具有特定属性的ViewModel.如果ViewModel无效,则/ register不会前进到下一步.
我实际上不太关心在一次性向导中维护REST.我认为,REST是最重要的,可重复的操作 - 您希望网址基本上是可收藏的,这样无论何时去,都可以获得相同的数据视图.在多步骤向导中,您有依赖关系,无论如何都会破坏REST的这个视角.我的感觉是让一个控制器具有可能独立的操作或使用查询参数来指示您正在执行的步骤.这就是我构建激活向导(需要多个步骤)的方法.