简体   繁体   English

不引人注意的DateTime? 在MVC4中验证

[英]Unobtrusive DateTime? Validation in MVC4

I upgraded an MVC3 solution to MVC4. 我将MVC3解决方案升级为MVC4。 After the migration, the validator is broken. 迁移后,验证程序已损坏。

My input date, if i select German as language, is "20.03.2013". 如果我选择德语作为语言,我的输入日期是“20.03.2013”​​。 I get an validation error in MVC4, but not in MVC3. 我在MVC4中得到验证错误,但在MVC3中没有。 If i replace the format from "20.03.2013" to "20/03/2013" it works in MVC4, but not in MVC3 ;-) 如果我将格式从“20.03.2013”​​替换为“20/03/2013”​​,它适用于MVC4,但不适用于MVC3 ;-)

I set the UI culture of the current thread to german. 我将当前线程的UI文化设置为德语。 The output of the ResX values are in the correct language, so i know there should be no error with the culture. ResX值的输出使用正确的语言, 因此我知道文化应该没有错误。 , only for the site itself. ,仅适用于网站本身。 The error messages are in english, but the site is in german. 错误消息是英文的,但该网站是德语。

I assume this means the validator uses the wrong UI Culture. 我认为这意味着验证器使用了错误的UI文化。

Here is the code i use. 这是我使用的代码。

[Required(ErrorMessageResourceName = "Error_DepartureDate", ErrorMessageResourceType = typeof(Resx.Query))]
public DateTime? DepartureDate { get; set; }

I assume there is something wrong with the default model binder, as the rendered html looks good: 我假设默认模型绑定器有问题,因为渲染的html看起来很好:

data-lang="de" data-mindate="3" data-val="true" data-val-required="Bitte geben Sie das gewünschte Reisedatum des Hinflugs ein." id="DepartureDate" name="DepartureDate" tabindex="3" type="text" value=""

I upgraded the Jscript to the sources that ship when you create a new Mvc application using the Visual Studio 2012 (SP1 is installed) templates. 我将Jscript升级到使用Visual Studio 2012(已安装SP1)模板创建新Mvc应用程序时发布的源代码。 This had no impact. 这没有任何影响。

I have a CultureModelBinder which reads the current culture out of the Session and sets the culture using a small helper function. 我有一个CultureModelBinder,它从Session中读取当前的文化,并使用一个小帮助函数设置文化。

public static void UpdateThreadCulture(CultureInfo culture)
{
  Thread.CurrentThread.CurrentUICulture = culture;            
}

The culture model binder is the default binder. culture文件模型binder是默认的binder。

ModelBinders.Binders.DefaultBinder = new CultureModelBinder();
ModelBinders.Binders.Add(typeof(DateTime?), new DateTimeModelBinder());
// and many, many more

Maybe something changed in the execution order with mvc4 resulting the problem? 也许mvc4的执行顺序发生了变化,导致问题?

Update: The project uses .NET Framework 4.5 as target. 更新:该项目使用.NET Framework 4.5作为目标。

Update 2: 更新2:

I have a combo box where the user can select 16 different languages, each might have it's own specific formatting. 我有一个组合框,用户可以选择16种不同的语言,每种语言都有自己特定的格式。

Eg DE-de -> DD.MM.YYYY; 例如DE-de - > DD.MM.YYYY; en-en -> DD/MM/YYYY; en-en - > DD / MM / YYYY; en-us -> MM/DD/YYYY en-us - > MM / DD / YYYY

I just got a hint about setting the current culture, here is the proof it should be correct as it is. 我刚刚得到了关于设定当前文化的暗示,这里证明它应该是正确的。 This code is not hit when the validators fail, it looks like it happens on the client side. 当验证器失败时,不会触发此代码,看起来它发生在客户端。

public class DateTimeModelBinder : IModelBinder
    {
        private LogService _log = new LogService();

        public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
        {            
            object result = null;
            ValueProviderResult valueResult = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);
            if (valueResult != null)
            {
                try
                {
                    var stateHandler = new StateHandler(controllerContext.HttpContext.Session);                    
                    result = valueResult.ConvertTo(typeof(DateTime?), stateHandler.Culture);                                       
                }
                catch
                {
                    try
                    {
                        result = valueResult.ConvertTo(typeof(DateTime?), CultureInfo.InvariantCulture);
                    }
                    catch (Exception ex)
                    {
                        _log.Error("DateTimeModelBinder parse exception", ex);
                        _log.KeyValue("AttemptedValue", valueResult.AttemptedValue);                                           
                    }                    
                }
            }
            return result;
        }
    }

and for completeness my culture model binder: 为了完整我的文化模型粘合剂:

public class CultureModelBinder : DefaultModelBinder
    {      
        public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
        {
            StateHandler stateHandler = new StateHandler(controllerContext.HttpContext.Session);
            Helper.UpdateThreadCulture(stateHandler.Culture);

            return base.BindModel(controllerContext, bindingContext);
        }        
    }

Update: Maybe there is a correlation to this problem: http://connect.microsoft.com/VisualStudio/feedback/details/705643/a-data-val-date-attribute-is-generated-for-time-fields-in-asp-net-mvc-4 更新:可能与此问题存在关联: http//connect.microsoft.com/VisualStudio/feedback/details/705643/a-data-val-date-attribute-is-generated-for-time-fields-in -Asp净MVC -4-

Update: Read the following article: http://weblogs.asp.net/scottgu/archive/2010/06/10/jquery-globalization-plugin-from-microsoft.aspx 更新:阅读以下文章: http//weblogs.asp.net/scottgu/archive/2010/06/10/jquery-globalization-plugin-from-microsoft.aspx

tried the following out: 尝试以下方法:

Loaded the scripts in the following order: 按以下顺序加载脚本:

/Scripts/jquery-1.8.3.min.js
/Scripts/globalize.js
/Scripts/cultures/globalize.cultures.js
// and much more other scripts...

added the call. 加了电话。 the output was correctly "DE". 输出正确“DE”。

var currentLanguage = $("#DepartureDate").attr("data-lang");
        alert(currentLanguage);       
        $.preferCulture(currentLanguage);

No affect to the validators... 对验证者没有影响......

The point is that Mvc3 doesnt validate at all dates on the client side that is the point. 关键是Mvc3不会在客户端的所有日期验证这一点。 You just set the cultrure on the server side....but your culture settings are not reflected at all on the client side...at least the Mvc engine doesnt do it automatically. 你只需在服务器端设置文件......但你的文化设置并没有在客户端反映出来......至少Mvc引擎并没有自动完成。 The only way to handle properly dates and numbers on the client side with cultures that differs from English is to use aa javascript globalization library that is able to parse properly dates in all cultures, and to set the client side culture equal to the server side culture, then you have to redefine properly all validation methods to use globalized functions. 在客户端使用与英语不同的文化来正确处理日期和数字的唯一方法是使用能够在所有文化中正确解析日期的javascript全球化库,并将客户端文化设置为与服务器端文化相同,那么你必须正确地重新定义所有验证方法以使用全球化函数。 Please read this post of my blog that clarifies how to handle properly globalization on the client side: http://www.dotnet-programming.com/post/2011/12/14/Globalization-Validation-and-DateNumber-Formats-in-AspNet-MVC.aspx 请阅读我的博客文章,阐明如何在客户端处理正确的全球化: http//www.dotnet-programming.com/post/2011/12/14/Globalization-Validation-and-DateNumber-Formats-in -AspNet-MVC.aspx

Moreover, please dont confuse CurrentCulture with CurrentUICulture CurrentUICulture doesnt affect at all the way numbers or dates are handled, but only the resource files containing culture specifi resources such as localized strings. 此外,请不要将CurrentCulture与CurrentUICulture混淆CurrentUICulture不会影响所有数字或日期的处理方式,而只会影响包含文化特定资源(如本地化字符串)的资源文件。

Finally, it is very unefficient to set the culture in the model binder, since the model binder is a recursive function so it si called hundreds of times during model reconstruction, and the culture setting operation is not a simple variable setting operation but it has a not negligible cost. 最后,在模型绑定器中设置文化是非常不合理的,因为模型绑定器是递归函数,因此在模型重建期间它被调用了数百次,并且文化设置操作不是简单的变量设置操作,而是具有不可忽视的成本。 It is better to write a global controller filter to handle culture setting (I always do this way) so the operation is performed just once per request 最好编写一个全局控制器过滤器来处理文化设置(我总是这样做),因此每个请求只执行一次操作

Try overwriting the default date validator: 尝试覆盖默认日期验证器:

// Replace dots so jq validator can understand what's going on
$(function () {
    $.validator.addMethod(
    "date",
    function (value, element) {
        var s = value;
        s = value.replace(/\./g, '/');

        // Chrome requires tolocaledatestring conversion, otherwise just use the slashed format
        var d = new Date();
        return this.optional(element) || !/Invalid|NaN/.test(new Date(d.toLocaleDateString(value))) || !/Invalid|NaN/.test(new Date(s));
    },
    ""
    );
});

$.validator.unobtrusive.parse();

Looking through the code with ILSpy it appears to be the newly introduced ClientDataTypeModelValidatorProvider that is adding the data-val-date attribute. 使用ILSpy查看代码,它似乎是新引入的ClientDataTypeModelValidatorProvider,它正在添加data-val-date属性。

If you want to revert to MVC3 style functionality then simply removing that provider from the list of model validation providers does the trick. 如果要恢复到MVC3样式功能,那么只需从模型验证提供程序列表中删除该提供程序即可。

It doesn't solve the problem but in a few lines of code may remove the problems caused by jquery validation's lack of globalization. 它没有解决问题,但在几行代码中可能会消除由jquery验证缺乏全球化引起的问题。

using System.Web.Mvc;

...

protected void Application_Start()
{
  var providers = ModelValidatorProviders.Providers;
  var clientDataTypeModelValidatorProvider = providers.OfType<ClientDataTypeModelValidatorProvider>().FirstOrDefault();
  if ( clientDataTypeModelValidatorProvider != null )
  {
    providers.Remove( clientDataTypeModelValidatorProvider );
  }

  ...
}

Microsoft suggests to replace the new jquery.validate.js from mvc4 with the old jquery.validate.js from the MVC3 project template. Microsoft建议使用MVC3项目模板中的旧jquery.validate.js从mvc4替换新的jquery.validate.js。 This works, but the funny thing is, the bug is not in MVC4, it was in MVC3. 这很有用,但有趣的是,bug不在MVC4中,而是在MVC3中。

We use the way which was described by Francesco, see the accepted answer. 我们使用Francesco描述的方式,看到接受的答案。

如果这只发生在Chrome中,则是由于jquery版本在日期验证中存在文化错误。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM