在Session中存储viewmodel数据会使用FluentValidation

时间:2015-10-04 16:48:01

标签: c# asp.net-mvc-4 session sitecore fluentvalidation

我目前正在开展涉及Sitecore CMS(7.2)的大型项目。对于viewmodel验证,我们使用FluentValidations。由于Sitecore和FluentValidations的结合,我似乎在某种技术僵局中运行。我自己找到了一个解决方案,但我不确定这是否是正确的方法。这就是问题所在:

情况

Sitecore组件包含HTML表单。通过模型绑定器,该形式的每个值都绑定到(复杂)视图模型中的相应字段。这是标准的.NET MVC方法。

但是,viewmodel中的某些值不是表单的一部分。例如,应用程序计算应用突变的日期。用户只能将此日期值视为纯文本,因此无法对其进行编辑。它仍然是视图模型的一部分。为了确保在代码中将此值发回模型,通常会使用隐藏字段。但是如果我使用隐藏字段,则意味着用户可以欺骗该日期,并且由于某些验证依赖于此值,因此它们能够欺骗表单的整个有效性。

此外,在同一个视图模型中,我有一个复杂对象列表,我不能简单地放入隐藏字段(或者我应该将其序列化为JSON,我不想要)。

结论是我需要将这些数据存储在其他地方。用户无法欺骗它,但我仍然能够使用FluentValidations验证用户输入。因此,我决定将整个viewmodel放在用户Session中,并在成功突变后直接删除它。

问题

通过使用会话数据,我遇到了问题。我们先来看看这些步骤:

  1. (GET)创建了Viewmodel。设置计算日期,并从(慢速)Web服务中检索一次复杂类型列表。
  2. 整个viewmodel存储为会话数据。
  3. 表单会显示给填写表单的用户。有些数据仅显示为只读,如复杂类型的日期和列表。
  4. 用户提交表单,FluentValidations开始验证数据(POST)。
  5. 我遇到问题的地方。 FluentValidations完成的验证在到达POST控制器方法之前启动。这正是我们想要的方式,因为这意味着验证错误会自动添加到ModelState中。但是,出于安全原因,我不想将此数据作为隐藏字段添加到cshtml文件中,这意味着在FluentValidations验证表单时它们是空的。

    这会产生问题,因为某些表单验证依赖于缺少的数据。我基本上想要的是合并与发布到控制器方法的viewmodel一起存储在会话中的viewmodel。但是我必须在FluentValidations开始这项工作之前这样做。

    我目前的解决方案

    很高兴,我了解了FluentValidation的IValidatorInterceptor:一个可以用来做东西的界面'在验证过程开始之前或之后。我使用BeforeMvcValidation方法进行合并过程。代码如下:

    public ValidationContext BeforeMvcValidation(ControllerContext controllerContext, ValidationContext validationContext)
        {
            if (controllerContext.HttpContext.Session == null)
                return validationContext;
    
            var sessionData = controllerContext.HttpContext.Session["some_identifier"];
    
            if (sessionData == null)
                return validationContext;
    
            var mergedObjectToValidate = Utils.MergeViewModelData(sessionData, validationContext.InstanceToValidate);
    
            // Unfortunately, we have to do this..
            var privateSetterProperty = validationContext.GetType().GetProperty(ValidationContextInstancePropertyName);
    
            if (privateSetterProperty == null)
                return validationContext;
    
            privateSetterProperty.SetValue(validationContext, mergedObjectToValidate);
            return validationContext;
        }
    

    基本上这种拦截器方法允许我在验证之前进行合并过程。所以我认为我在这里有解决方案,但正如你所看到的,我正在使用反射来设置属性。这是因为ValidationContext对象中的属性InstanceToValidate具有私有的setter。我没有使用反射就无法设置它。显然,这有点脏。

    它确实按照我想要的方式工作! :)我不需要任何可以被欺骗的隐藏字段(这对于直通处理而言非常糟糕)而且我仍然可以像以前一样使用FluentValidations。此外,MVC模型绑定过程保持不变,我更喜欢。

    实际问题

    所以上面的解决方案完全符合您的要求,那么您的问题是什么?!嗯,简单:

    1. 我使用反射在第三方库中设置私有属性(FluentValidations)。显而易见的答案是:不要这样做。但在这种情况下,它完美无瑕。如果InstanceToValidate-property有一个公共setter,我根本不会发布这个问题:我觉得我已经把它钉了。但不幸的是它是私人的,所以我有什么理由不这样做,也许有人是FluentValidations行为的专家?
    2. 我们说有一个真正的理由说明我不应该这样做;是否有另一种方法具有相同的效果?我可以更早地挂钩,所以在FluentValidations开始之前,也许是某种类型的钩子'在MVC模型绑定过程之后,但在验证开始之前?
    3. 这整个方法是完全错误的,我应该以完全不同的方式解决它吗?
    4. 谢谢!

0 个答案:

没有答案