我已经启动了一个MVC网络应用程序。在正常情况下,当我开发一个站点,并且我需要一个编辑页面时,我将有一个GET请求来获取要编辑的记录,在屏幕上显示它,然后是另一个POST请求以将更改保存到数据库,如下所示:
public ActionResult Edit(int id) { // get the record and display }
[HttpPost]
public ActionResult Edit(CustomClass model) { // save changes to the record}
现在,当您点击编辑GET请求时,我需要对记录进行更改。原因是当你进入编辑模式时,我需要锁定数据库上的记录,这样其他任何人都无法进入编辑模式,或者至少它们会被警告。但是这打破了GET请求的正常规则,从未改变服务器。
我将如何以正确的方式实现这一目标?
更新
需要这样做的原因不是保存到数据库的并发问题。屏幕用于允许客户联系。我不能让2个用户同时联系客户。我需要在此屏幕上标记其他人当前正在处理它的记录。如果有人在编辑屏幕上,那就是他们将要做的事情。这是一个内部应用程序。
答案 0 :(得分:2)
您正在做的事与并发性无关,至少在典型的计算机科学定义中没有。您的并发性更多是人类进程的并发性(不是与多个客户联系)。当两个用户使用相同的记录时,计算机科学并发性与数据的一致性有关。
首先,对于修改数据的查询(即获取)来说,这是一个非常糟糕的主意。查询应该是幂等的,尽管出于显而易见的原因并不总是可行的(审计是一个很好的理由)。但总的来说,get不应该有副作用。这一点尤其重要,因为网络浏览器可以自由地优化"通过预加载它认为用户可能想要查看的页面的用户体验。但它只能通过获取请求而非帖子来实现。
另一个原因是出于安全考虑。如果您可以修改get中的数据,这意味着攻击者可以在您不知情的情况下愚弄您执行操作,只需将图像放在他们希望您执行的URL的页面上,通常会使其成为1x1大小的像素,以便您可以&# 39;甚至看不到它。
最后,网页是无状态的。换句话说,确定..你可以锁定页面......但网页只能暂时访问数据库..如果浏览器崩溃或用户刚刚打开页面会怎样?该记录无法解锁"。你可以通过一个解锁僵尸锁的过程解决这个问题......或者你可以让你的锁定时间相关(即此时锁定发生,它在x分钟内有效)但是如果用户有页面打开时间超过锁定时间?
一般来说,这不是一个好主意。你应该有非常明确的锁语义。在这种情况下,您真正想要的更多的是互斥锁甚至是访问令牌......您必须请求令牌才能联系用户,并且此令牌访问可以是一个独特的过程。例如,您可能有一个"联系人"存储联系人尝试的表,此表用作令牌获取过程的一部分。
答案 1 :(得分:1)
我倾向于不以你描述的方式“锁定”,我会使用一组解耦的宁静服务。在编辑现有资源时,使用PUT请求。然后,您可以在标题中输入详细信息,如果记录仍然新鲜,它应该会成功。如果它已经改变,你会收到一个错误。
有一篇很好的文章here,我觉得你会发现它很有用。 我一直遇到锁定机制的问题,当你的客户端在编辑中途死亡并且数据库记录被锁定或者有人走了一半时,你会进入超时并锁定恢复机制,这是我自那以后发生的事情。学到的是你可以做的遗留问题。
查看文章可能意味着更改代码,但我认为更好。
看到你的评论后:
如果您的业务需求是要立即查看是否已经在编辑中,那么真正解决此问题的唯一方法是在某个地方放置一条记录,说明您正在编辑记录,这是我后来后悔的模型但是在几个项目中确保操作的唯一确定的方法是原子实际上是放置一个锁记录但是我没有扩展数据库中对象的记录我有一个单独的锁表,完成超时如果它确实是一项业务需求,那么它将从此开始。
答案 2 :(得分:1)
如果要求真的是"锁定"记录然后这将是一个明智的方法。当然,在GET中进行编辑并不特别是RESTful,但该参数的语义可以采用任何一种方式。您实际上并不是编辑资源本身,只是将其从正在编辑的中锁定。资源保持不变。它是"数据"之间的语义差异。和"元数据",但整个问题无论如何都是关于语义。
但请注意,这种pessimistic concurrency(锁定记录)在Web应用程序上往往是一个坏主意。是真的要求吗?或者只是某人对实际业务需求的解释?你会经常发现后者。
在Web应用程序中,执行乐观并发模型通常是一种更标准的做法。像#"最后编辑的那样简单"编辑记录时,可以检查记录上的时间戳。如果数据库中的记录自该用户提取以来发生了更改,则用户会收到错误,并在获取更新的记录版本后要求再次进行更改。
对我而言,这主要是猜想,我不知道这里的实际业务需求或用户体验。我只是建议悲观并发是业内越来越少见的选择。
答案 3 :(得分:0)
我正在重新考虑如何处理锁定,也许作为流程的一部分,POST请求首先发送到锁定,然后重定向到编辑页面的get请求。然后,如果锁定到位,此编辑页面将仅显示详细信息。这将解决在get上修改数据的问题,并解决在他们不应该手动输入编辑页面URL的问题。
某种"锁定"的业务需求。不能变。由于联系人应用程序与管理应用程序是分开的,如果他们到达此屏幕,我就无法联系任何客户。
答案 4 :(得分:0)
如果没有实际锁定客户表的记录,您可以使用单独的特殊表来更新第一个用户的GET方法中的会话详细信息,表ID和记录ID以及时间戳等信息。如果第二个用户访问同一个客户,那么在您的GET方法中,您首先检查此特殊表,如果找到此客户记录ID,则您不允许第二个用户访问该客户。
在POST方法中,更新客户后,从特殊表中删除条目,从而“释放”客户。
为了处理浏览器崩溃等问题,您需要定期检查特殊表格并“免费”为客户提供无效会话。