C# 在会话中存储viewmodel数据会导致使用FluentValidation进行验证时出现问题

C# 在会话中存储viewmodel数据会导致使用FluentValidation进行验证时出现问题,c#,asp.net-mvc-4,session,sitecore,fluentvalidation,C#,Asp.net Mvc 4,Session,Sitecore,Fluentvalidation,我目前正在从事一个涉及(7.2)的大型项目。对于viewmodel验证,我们使用。由于Sitecore和FluentValidations的结合,我似乎陷入了某种技术僵局。我自己也找到了解决办法,但我不确定这是否是正确的方法。问题是: 处境 有一个包含HTML表单的Sitecore组件。通过modelbinder,此表单的每个值都绑定到(复杂)viewmodel中对应的字段。这是标准的.NETMVC方法 但是,viewmodel中的某些值不是表单的一部分。例如,应用程序将计算应用突变的日期。用户

我目前正在从事一个涉及(7.2)的大型项目。对于viewmodel验证,我们使用。由于Sitecore和FluentValidations的结合,我似乎陷入了某种技术僵局。我自己也找到了解决办法,但我不确定这是否是正确的方法。问题是:

处境 有一个包含HTML表单的Sitecore组件。通过modelbinder,此表单的每个值都绑定到(复杂)viewmodel中对应的字段。这是标准的.NETMVC方法

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

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

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

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

  • (获取)创建Viewmodel。设置计算日期,并从(慢速)Web服务检索一次复杂类型列表
  • 整个viewmodel存储为会话数据
  • 表单将显示给填写表单的用户。某些数据仅显示为只读,如日期和复杂类型列表
  • 用户提交表单,FluentValidations开始验证数据(POST)
  • 这就是我遇到问题的地方。FluentValidations完成的验证在到达POST controller方法之前生效。这正是我们想要的方式,因为这意味着验证错误会自动添加到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模型绑定过程保持不变,我更喜欢这样

    实际问题 因此,上述解决方案完全符合您的要求,那么您的问题是什么?!好吧,很简单:

  • 我使用反射在第三方库(FluentValidations)中设置私有属性。显而易见的答案是:不要走那条路。但在这种情况下,它可以完美地工作。如果InstanceToValidate属性有一个公共setter,我甚至不会发布这个问题:我会觉得我把它钉死了。但不幸的是,它是私人的,所以有什么真正的原因我不应该这样做,也许有人是FluentValidations行为的专家
  • 让我们说,我不应该走这条路有一个真正的原因;有没有其他方法具有相同的效果?我可以更早地挂接吗,在FluentValidations开始之前,也许在MVC模型绑定过程之后但在验证开始之前挂接
  • 这整个方法是错误的吗?我应该用完全不同的方法来解决吗
  • 谢谢