处理不同文化的验证的最佳方法是啥

Posted

技术标签:

【中文标题】处理不同文化的验证的最佳方法是啥【英文标题】:What is the best way to handle validation with different culture处理不同文化的验证的最佳方法是什么 【发布时间】:2015-12-11 17:43:38 【问题描述】:

我正在尝试构建一个多语言 MVC 应用程序。我的申请中有一个表格,我有输入费用的字段。我能够使用西班牙文化创造记录。

但是在尝试更新记录时,我得到 jquery 验证错误。我收到一条默认错误消息:

该字段必须是数字。

在我的视图模型中,我设置了以下属性。

[LocalizedDisplayName("Label_Cost")]
[RegularExpression("^[^<>,<|>]+$", ErrorMessage = null, ErrorMessageResourceName = "Error_Message_html_Tags_Prevented", ErrorMessageResourceType = typeof(Resources))]
[Range(0, 9999.99, ErrorMessage = null, ErrorMessageResourceName = "Error_Message_Cost_Not_Valid", ErrorMessageResourceType = typeof(Resources))]
public decimal? Cost  get; set; 

我已经在我的 Gobal.asax 文件中设置了以下内容

protected void Application_AcquireRequestState(object sender, EventArgs e)

    try
    
        HttpCookie cookie = HttpContext.Current.Request.Cookies.Get("CurrentCulture");
        string culutureCode = cookie != null && !string.IsNullOrEmpty(cookie.Value) ? cookie.Value : "en";
        CultureInfo ci = new CultureInfo(culutureCode);
        System.Threading.Thread.CurrentThread.CurrentUICulture = ci;
        System.Threading.Thread.CurrentThread.CurrentCulture =
        CultureInfo.CreateSpecificCulture(ci.Name);
    
    catch(Exception ex)
    
        // Code
    

上面的方法在服务器端按预期工作,改变了文化。但是客户端验证在非英语文化中中断,因为 javascript 只识别十进制文字。我想知道使用特定文化验证扩展 mvc 客户端验证的最佳方式。

编辑

参考 Mike 的网址,我在 Js 包中进行了以下更改。 js包如下

public static void RegisterBundles(BundleCollection bundles)

   BundleTable.EnableOptimizations = true;

  bundles.Add(new ScriptBundle("~/bundles/jquery").Include(
                        "~/Scripts/jquery-version.js"));

bundles.Add(new ScriptBundle("~/bundles/globalisation").Include(
               "~/Scripts/globalize.js",
               "~/Scripts/globalize/currency.js",
                "~/Scripts/globalize/date.js",
                "~/Scripts/globalize/message.js",
                "~/Scripts/globalize/number.js",
                "~/Scripts/globalize/plural.js",
                "~/Scripts/globalize/relative-time.js"));

  bundles.Add(new ScriptBundle("~/bundles/globalisationEN").Include(
               "~/Scripts/GlobalisationCulture/globalize.culture.en-AU.js"));

            bundles.Add(new ScriptBundle("~/bundles/globalisationES").Include(
               "~/Scripts/GlobalisationCulture/globalize.culture.es-AR.js"));

            bundles.Add(new ScriptBundle("~/bundles/jqueryuiEN").Include(
                        "~/Scripts/jquery-ui-1.10.3.js"));

            bundles.Add(new ScriptBundle("~/bundles/jqueryuiES").Include(
                        "~/Scripts/jquery-ui-1.10.3.js"));

            bundles.Add(new ScriptBundle("~/bundles/jqueryval").Include(
                "~/Scripts/jquery.validate.js",
                "~/Scripts/jquery.validate.unobtrusive.js",
                "~/Scripts/jquery.unobtrusive-ajax.js",
                "~/Scripts/jquery.validate.globalize.js"));

在布局页面中我已经实现如下

HttpCookie cookie = HttpContext.Current.Request.Cookies.Get("CurrentCulture");
        string culutureCode = cookie != null && !string.IsNullOrEmpty(cookie.Value) ? cookie.Value : "en";
        if (culutureCode.Equals("en-AU", StringComparison.OrdinalIgnoreCase))
        
            culutureCode = "EN";
        
        else if (culutureCode.Equals("es-AR", StringComparison.OrdinalIgnoreCase))
        
            culutureCode = "ES";
        
        else
        
            culutureCode = "EN";
        
@Scripts.Render("~/bundles/jquery",
                    "~/bundles/globalisation",
                    string.Format("~/bundles/globalisation0", culutureCode),
                    "~/bundles/jqueryval",
                    string.Format("~/bundles/jqueryui0", culutureCode))

【问题讨论】:

我个人不擅长文化信息,但您可能想要使用 catch,将其扩展为包含一般异常,并将其写入控制台(用于调试目的):` catch (异常错误) Console.WriteLine(err); ` 祝你好运! 您是否研究过类似问题中给出的解决方案:***.com/questions/5199835/… 为什么你会空捕获异常然后要求 SO 解决这个问题? @Mike 是的,我试过了,但对我不起作用。 您似乎在混合 jquery globalize 脚本。 jQuery globalize 1.0.0 使用currency.jsnumber.jsplural.js 等。但它适用于文化的 cldr 文件。 globalize.culture.es-AR.js etc etc 与 jquery globalize 0.1.x 有关。 【参考方案1】:

您在 RegisterBundles 中添加了捆绑包,但没有在布局页面中使用它们。您还在 RegisterBundles 中添加了多余的 jqueryui 文件。像这样更新您的 RegisterBundles 方法:

public static void RegisterBundles(BundleCollection bundles)
 
   BundleTable.EnableOptimizations = true;
   bundles.Add(new ScriptBundle("~/bundles/jquery").Include(
                    "~/Scripts/jquery-version.js"));
   bundles.Add(new ScriptBundle("~/bundles/globalisation").Include(
            "~/Scripts/globalize.js",                
            "~/Scripts/globalize/currency.js",
            "~/Scripts/globalize/date.js",
            "~/Scripts/globalize/message.js",
            "~/Scripts/globalize/number.js",
            "~/Scripts/globalize/plural.js",
            "~/Scripts/globalize/relative-time.js"));
   bundles.Add(new ScriptBundle("~/bundles/globalisationEN").Include(
           "~/Scripts/GlobalisationCulture/globalize.culture.en-AU.js"));
   bundles.Add(new ScriptBundle("~/bundles/globalisationES").Include(
           "~/Scripts/GlobalisationCulture/globalize.culture.es-AR.js"));
   bundles.Add(new ScriptBundle("~/bundles/jqueryui").Include(
                    "~/Scripts/jquery-ui-1.10.3.js"));      

   bundles.Add(new ScriptBundle("~/bundles/jqueryval").Include(
            "~/Scripts/jquery.validate.js",
            "~/Scripts/jquery.validate.unobtrusive.js",
            "~/Scripts/jquery.unobtrusive-ajax.js",
            "~/Scripts/jquery.validate.globalize.js"));
  

然后像这样更新布局页面:

@section Scripts 

    @Scripts.Render("~/bundles/jquery",
                "~/bundles/globalisation",
                "~/bundles/globalisationEN",
                "~/bundles/globalisationES",
                "~/bundles/jqueryval",
                "~/bundles/jqueryui"))

   <script type="text/javascript">
    $.validator.methods.number = function (value, element) 
        return this.optional(element) ||
            !isNaN(Globalize.parseFloat(value));
    
    $(document).ready(function () 
        Globalize.culture('es-AR'); //set spanish culture
    );

   </script>

希望这会有所帮助:)

【讨论】:

实际上我忘了发布 globalisationEN" 和 globalisationES" 。我已经将它包含在我的应用程序中,并且我已经修改了我的帖子。添加 globalisation.culture js 后,我收到控制台错误“Uncaught TypeError: t.addCultureInfo is not a function” 您能否提及您是如何使用 t.addCultureInfo 的? 错误实际上是从 jquery.gloablisation.js 触发的。【参考方案2】:

有 2 个 jQuery Globalize 插件。

旧版本是v0.0.1,包含一个脚本globalize.js,并有一个子文件夹cultures,您可以在其中找到所有脚本文化,例如:

globalize.culture.en-AU.js globalize.culture.es-AR.js

这些脚本允许您添加任意数量的文化,因此以这种方式构建您的包会非常好:

bundles.Add(new ScriptBundle("~/bundles/globalisation").Include(
    "~/Scripts/globalize.js",
    "~/Scripts/cultures/globalize.culture.en-AU.js",
    "~/Scripts/cultures/globalize.culture.es-AR.js"
));

Globalize 将拥有一组本地化脚本,您可以简单地使用这些脚本进行设置:

Globalize.culture('en-AU');

Globalize.culture('es-AR');

它可以使用某种接近度来确定您想要使用的最接近的文化。 如果你已经在你的包中加载了globalize.culture.es-AR.js 你可以设置Globalize.culture('es');Globalize 将能够找出你想要使用'es-AR'文化;当然,如果您添加了globalize.culture.es.js,加载程序会选择最后一个。

jQuery Globalize(稳定版)的新版本是v1.0.0,它的工作方式完全不同。

它仍然有名为globalize.js 的主脚本文件,但您必须添加更多脚本才能使其工作。

有人构建了tool,它会根据您要使用的模块类型(数字、日期、货币)准确地告诉您所需的脚本。

如果您选择使用 v1.0.0,您会看到该工具会建议包含基本脚本(仅限数字):

cldr.js cldr/event.js cldr/supplemental.js globalize.js 全球化/number.js

加上一些 CLDR JSON 脚本:

cldr/supplemental/likelySubtags.json cldr/main/locale/numbers.json cldr/supplemental/numberingSystems.json

您可以在 core 包和 numbers 包中找到这些文件。 如果您想验证日期,这是package。 更多信息here.

这些都是 json 文件,不能捆绑。您可以在运行时加载它们,如下所示:

Application.loadCulture = function (culture) 

    $.when(
      $.get(Application.CldrFetch + '/' + culture + '/' + encodeURIComponent("likelySubtags.json")),
      $.get(Application.CldrFetch + '/' + culture + '/' + "numberingSystems.json"),
      $.get(Application.CldrFetch + '/' + culture + '/' + "plurals.json"),
      $.get(Application.CldrFetch + '/' + culture + '/' + "ordinals.json"),
      $.get(Application.CldrFetch + '/' + culture + '/' + "currencyData.json"),
      $.get(Application.CldrFetch + '/' + culture + '/' + "timeData.json"),
      $.get(Application.CldrFetch + '/' + culture + '/' + "weekData.json"),
      $.get(Application.CldrFetch + '/' + culture + '/' + "ca-gregorian.json"),
      $.get(Application.CldrFetch + '/' + culture + '/' + "timeZoneNames.json"),
      $.get(Application.CldrFetch + '/' + culture + '/' + "numbers.json"),
      $.get(Application.CldrFetch + '/' + culture + '/' + "currencies.json")
    )
    .then(function () 
        // Normalize $.get results, we only need the JSON, not the request statuses.
        return [].slice.apply(arguments, [0]).map(function (result) 
            return result[0];
        );

    ).then(Globalize.load).then(function () 
        Globalize.locale(culture);
    );
;

无论如何;假设您想坚持使用旧的 v0.0.1,它仍然是最好的。 您的捆绑包将包含 globalize 脚本和文化脚本:

bundles.Add(new ScriptBundle("~/bundles/globalisation").Include(
    "~/Scripts/globalize.js",
    "~/Scripts/cultures/globalize.culture.en-AU.js",
    "~/Scripts/cultures/globalize.culture.es-AR.js"
));

jQuery validation 提供了一些您可能需要考虑的其他扩展:

additional-methods.js localization/messages_es_AR.js(文化错误消息)

我看到您在Application_AcquireRequestState 中设置您的文化。有人建议最好在Application_BeginRequest 中进行,因为它在管道中较早处理:

    protected void Application_BeginRequest(object sender, EventArgs e)
    
        HttpCookie cookie = HttpContext.Current.Request.Cookies.Get("CurrentCulture");
        string cultureCode = cookie != null && !string.IsNullOrEmpty(cookie.Value) ? cookie.Value : "en";
        CultureInfo ci = new CultureInfo(cultureCode);
        System.Threading.Thread.CurrentThread.CurrentCulture = new System.Globalization.CultureInfo(cultureCode);
        System.Threading.Thread.CurrentThread.CurrentUICulture = System.Threading.Thread.CurrentThread.CurrentCulture;
    

您似乎正在使用此jQuery plugin 进行验证。我通常会做的是,一旦我加载脚本,配置文化并设置自定义验证:

    Globalize.culture(this.culture);

    $.validator.methods.number = function (value, element) 
        return this.optional(element) || jQuery.isNumeric(Globalize.parseFloat(value));
    ;

    $.validator.methods.date = function (value, element) 
        return (this.optional(element) || Globalize.parseDate(value));
    ;

    jQuery.extend(jQuery.validator.methods, 
        range: function (value, element, param) 
            var val = Globalize.parseFloat(value);
            return this.optional(element) || (val >= param[0] && val <= param[1]);
        
    );

您缺少的一件事是用于小数的模型粘合剂:

using System;
using System.Web.Mvc;
using System.Globalization;

public class DecimalModelBinder : IModelBinder

    public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    
        ValueProviderResult valueResult = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);
        ModelState modelState = new ModelState  Value = valueResult ;
        object actualValue = null;
        try
        
            //Check if this is a nullable decimal and a null or empty string has been passed
            var isNullableAndNull = (bindingContext.ModelMetadata.IsNullableValueType && string.IsNullOrEmpty(valueResult.AttemptedValue));

            //If not nullable and null then we should try and parse the decimal
            if (!isNullableAndNull)
            
                actualValue = decimal.Parse(valueResult.AttemptedValue, NumberStyles.Any, CultureInfo.CurrentCulture);
            
        
        catch (FormatException e)
        
            modelState.Errors.Add(e);
        

        bindingContext.ModelState.Add(bindingContext.ModelName, modelState);
        return actualValue;
    

可以在您的 Global.asax Application_Start 中设置:

ModelBinders.Binders.Add(typeof(decimal), new DecimalModelBinder());
ModelBinders.Binders.Add(typeof(decimal?), new DecimalModelBinder());

这几乎是你需要的一切。

这种方法只有一个烦人的问题。 假设您使用的是文化en-AU,并且您在数字字段中输入了一个值:10,4。 此数字在 es-AR 中完全有效,但对于 en-AU 文化应该无效。

jQuery Globalize 无论如何都会认为它有效,因为它会在此处将其转换为 104:

$.validator.methods.number = function (value, element) 
    return this.optional(element) || jQuery.isNumeric(Globalize.parseFloat(value));
;

Globalize.parseFloat('10,4') 用于文化 en-AU 会将这个数字转换为 104。

如果您对文化 es-AR 的 Globalize.parseFloat('10.4') 执行相同的操作,也会发生同样的事情;它将再次变为 104。

您可以检查运行此fiddle 的行为。

,. 都是有效符号,因为它们将用作小数分隔符和千位分隔符。

在github 上有几个关于这个主题的问题,我想这很难解决,因为他们现在正在开发新版本,顺便说一下,同样的问题仍然存在。

使用我们的decimal model binder,您将在服务器端面临同样的问题:

decimal.Parse('10,4', NumberStyles.Any, CultureInfo.CurrentCulture);

如果 CultureInfo.CurrentCulture 为 'en-AU',将再次产生相同的结果:104

它可以在那里放置一个断点并查看它如何转换值。

我想这可能会更容易修复,也许使用一些正则表达式。

如果您想使用 jQuery Validator v.0.1.1 或 jQuery Validator v.1.0.0 来使用解决方案,我已经创建了两个存储库 here 和 here。

【讨论】:

以上是关于处理不同文化的验证的最佳方法是啥的主要内容,如果未能解决你的问题,请参考以下文章

处理规则验证的最佳方法是啥

现在 SSLSocketFactory 在 Android 上已被弃用,处理客户端证书身份验证的最佳方法是啥?

在 Rails 中验证多封电子邮件和处理错误的最佳方法是啥?

在 ASP.NET MVC 中使用 Universe 数据库处理身份验证的最佳方法是啥?

在 Javascript 中验证响应来自我的服务器的最佳方法是啥?

React - 处理登录和身份验证的最佳方式是啥?