客户端验证是一种非常有用的技术,它可以在用户提交表单时,防止不必要的服务器往返。如果已知验证会失败,客户端验证可以提前告知用户,从而提高用户体验。默认情况下,模型绑定器在解析用户输入时表现得很好,例如日期格式可以是dd-mm-yyyy、dd-mm-yy、dd/mm/yy或dd.mm.yy,都能被成功解析。然而,小数分隔符的处理则较为复杂。尽管如此,仍然希望使用客户端验证。通过简单地指定输入类型(例如数字、日期等),可以获得一些基本的浏览器验证,但如果不手动添加属性到标记中,无法获得进一步的验证(例如必填)。
示例应用程序是一个新创建的ASP.NET CoreWeb应用程序,使用了模型-视图-控制器(MVC)模式和ASP.NET Core 2.0。默认的(主页)视图被替换为一个包含三种不同类型字段(文本、日期/时间和十进制数)的简单表单。这个表单被提交回控制器,其中ViewBag变量会用服务器端验证的结果进行更新。
HomeViewModel用各种验证属性进行了注解,例如Required、MinLength和Range。
为了提供客户端验证,需要在视图的末尾添加以下代码:
@section Scripts {
@{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}
这段代码渲染了位于Shared文件夹中的_ValidationScriptsPartial.cshtml文件的内容。这个视图目前只添加了jquery.validate,这会导致客户端上出现不希望的行为——输入使用默认值进行验证,例如DD/MM/YYYY和点而不是逗号作为小数分隔符,这与文化习惯不符。结果是,如果使用逗号,客户端验证会失败;如果使用点,服务器端验证会失败。
为了解决这个问题,需要为正确的文化配置jQuery.Validation。这可以通过从Unicode Common Locale Data Repository (CLDR)项目下载所需的文化信息并放置在正确的位置来实现。将展示一种不同的方法,即数据自动下载,但无论哪种方法最适合,示例应用程序都显示了所需的文件夹结构。
项目文件夹中有一个文件在Visual Studio中即使切换“显示所有文件”也不可见。这个文件名为.bowerrc,包含一个json结构的配置,在新项目中只包含一个名为directory的元素,设置为wwwroot/lib。希望将这个文件更改为以下内容:
{
"directory": "wwwroot/lib",
"scripts": {
"preinstall": "npm install cldr-data-downloader@0.2.x",
"postinstall": "node ./node_modules/cldr-data-downloader/bin/download.js -i wwwroot/lib/cldr-data/index.json -o wwwroot/lib/cldr-data/"
}
}
这基本上告诉Bower安装cldr-data-downloader包并下载index.json文件中指定的文件。这个文件不存在,所以除非安装cldr-data,否则每次管理Bower包时都会出错。
一旦有了CLDR数据,就需要安装globalize和jquery-validation-globalize。安装这些包后,bower.json文件应该看起来像这样:
{
"name": "asp.net",
"private": true,
"dependencies": {
"bootstrap": "3.3.7",
"jquery": "3.2.1",
"jquery-validation": "1.17.0",
"jquery-validation-unobtrusive": "3.2.6",
"cldr-data": "29.0.0",
"globalize": "v0.1.1",
"jquery-validation-globalize": "1.0.0",
"cldrjs": "0.5.0"
},
"resolutions": {
"globalize": "^1.0.0",
"jquery": "3.2.1",
"cldrjs": "0.5.0",
"jquery-validation": "1.17.0"
}
}
如前所述,通过在Scripts部分包含_ValidationScriptsPartial.cshtml来包含所需的验证脚本。这个文件需要更新以使用安装的新包。对于开发阶段更新为以下内容。对于发布配置,请相应更新。
可以使用多种机制来检测文化,例如从QueryString、Cookie或Accept-Language HTTP头部。要设置这种本地化中间件,需要在应用程序的startup.cs文件中添加以下代码:
C#
var di = new DirectoryInfo(Path.Combine(env.WebRootPath, @"lib\cldr-data\main"));
var supportedCultures = di.GetDirectories().Where(x => x.Name != "root").Select(x => new CultureInfo(x.Name)).ToList();
app.UseRequestLocalization(
new RequestLocalizationOptions
{
DefaultRequestCulture = new RequestCulture(supportedCultures.FirstOrDefault(x => x.Name == "en-GB")),
SupportedCultures = supportedCultures,
SupportedUICultures = supportedCultures
});
目前,还没有指定要使用哪种文化,会得到一个错误E_DEFAULT_LOCALE_NOT_DEFINED: Default locale has not been defined。要指定文化,调用Globalize.locale(...)与希望使用的文化。文化的名称必须与wwwroot\lib\cldr-data\main中看到的文件夹结构匹配,其中包含下载的文化。
在_ValidationScriptsPartial.cshtml的末尾添加以下代码:
@inject Microsoft.AspNetCore.Hosting.IHostingEnvironment HostingEnvironment
@{
string GetDefaultLocale()
{
const string localePattern = "lib\\cldr-data\\main\\{0}";
var currentCulture = System.Globalization.CultureInfo.CurrentCulture;
var cultureToUse = "en-GB"; //Default regionalisation to use
if (System.IO.Directory.Exists(System.IO.Path.Combine(HostingEnvironment.WebRootPath, string.Format(localePattern, currentCulture.Name))))
cultureToUse = currentCulture.Name;
else if (System.IO.Directory.Exists(System.IO.Path.Combine(HostingEnvironment.WebRootPath, string.Format(localePattern, currentCulture.TwoLetterISOLanguageName))))
cultureToUse = currentCulture.TwoLetterISOLanguageName;
return cultureToUse;
}
}
一旦所有代码组装完毕,可以通过更改浏览器语言或在地址栏中设置文化作为查询字符串参数来测试它是否按预期工作。