我目前正在开展涉及Sitecore CMS(7.2)的大型项目。对于viewmodel验证,我们使用FluentValidations。由于Sitecore和FluentValidations的结合,我似乎在某种技术僵局中运行。我自己找到了一个解决方案,但我不确定这是否是正确的方法。这就是问题所在:
Sitecore组件包含HTML表单。通过模型绑定器,该形式的每个值都绑定到(复杂)视图模型中的相应字段。这是标准的.NET MVC方法。
但是,viewmodel中的某些值不是表单的一部分。例如,应用程序计算应用突变的日期。用户只能将此日期值视为纯文本,因此无法对其进行编辑。它仍然是视图模型的一部分。为了确保在代码中将此值发回模型,通常会使用隐藏字段。但是如果我使用隐藏字段,则意味着用户可以欺骗该日期,并且由于某些验证依赖于此值,因此它们能够欺骗表单的整个有效性。
此外,在同一个视图模型中,我有一个复杂对象列表,我不能简单地放入隐藏字段(或者我应该将其序列化为JSON,我不想要)。
结论是我需要将这些数据存储在其他地方。用户无法欺骗它,但我仍然能够使用FluentValidations验证用户输入。因此,我决定将整个viewmodel放在用户Session中,并在成功突变后直接删除它。
通过使用会话数据,我遇到了问题。我们先来看看这些步骤:
我遇到问题的地方。 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模型绑定过程保持不变,我更喜欢。
所以上面的解决方案完全符合您的要求,那么您的问题是什么?!嗯,简单:
谢谢!