MVC 5.2.3 - 全球化 - 日期时间到带范围的十进制
MVC 5.2.3 - Globalization - DateTime & Decimal with Range
长话短说,我开始了一个使用 MVC 5.2.3(具有所有更新的 JS 框架)的新项目,期望我在 MVC 2 验证中遇到的许多问题都得到解决,我错了.
基本上,我试图为 DateTime
和 Decimal
提供验证,我快把自己逼疯了。
对于小数字段,在 EN 和 DE(文化)中使用浏览器,我遇到了逗号和点(小数除法)以及我设置的范围的问题。
使用 DateTime
我什至尝试了 DisplayFormat
只是为了显示日期而不是时间,无论如何 day/month/year 的顺序与 .
或/
或 -
只是失败。
示例:
- 在 DE 中 - 键入:999,999 --> 结果:错误 "Das Feld "D1 DE"muss zwischen 0 und 999,9999 liegen。" - 应该被接受。
- 在 EN 中 - 输入:999.999 --> 结果:被接受,因为它应该是(正确的)
同样适用于日期时间显示格式和类似的 JS 验证....
分享一下我目前的情况:
测试对象
public class TestObject
{
....
[Display(Name = "D2", ResourceType = typeof(WebApplication1.Models.Res.TestObject))]
[Range(0, 999.9999)]
public decimal? D1 { get; set; }
// On DBContext I have defined the range/precision
// modelBuilder.Entity<TestObject>().Property(x => x.D2).HasPrecision(7, 4);
[Display(Name = "Date1", ResourceType = typeof(WebApplication1.Models.Res.TestObject))]
//[DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = @"{0:dd\/MM\/yyyy}")]
public DateTime Date1 { get; set; }
....
}
view.cshtml
<div class="form-group">
@Html.LabelFor(model => model.D2, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.D2, new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(model => model.D2, "", new { @class = "text-danger" })
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.Date1, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.Date1, new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(model => model.Date1, "", new { @class = "text-danger" })
</div>
</div>
_layout.cshtml
<script src="~/Scripts/globalize/globalize.js" type="text/javascript"></script></script>
<script src="~/Scripts/globalize/cultures/globalize.culture.de.js" type="text/javascript"></script>
<script src="~/Scripts/globalize/cultures/globalize.culture.en-US.js" type="text/javascript"></script>
<script>
$.validator.methods.number = function (value, element) {
return this.optional(element) ||
!isNaN(Globalize.parseFloat(value));
}
$.validator.methods.date = function (value, element) {
return this.optional(element) ||
Globalize.parseDate(value);
}
$(document).ready(function () {
Globalize.culture('@System.Threading.Thread.CurrentThread.CurrentCulture');
});
</script>
DecimalModelBinder
public class DecimalModelBinder : DefaultModelBinder
{
public override object BindModel(ControllerContext controllerContext,
ModelBindingContext bindingContext)
{
object result = null;
// Don't do this here!
// It might do bindingContext.ModelState.AddModelError
// and there is no RemoveModelError!
//
// result = base.BindModel(controllerContext, bindingContext);
string modelName = bindingContext.ModelName;
string attemptedValue =
bindingContext.ValueProvider.GetValue(modelName).AttemptedValue;
// Depending on CultureInfo, the NumberDecimalSeparator can be "," or "."
// Both "." and "," should be accepted, but aren't.
string wantedSeperator = NumberFormatInfo.CurrentInfo.NumberDecimalSeparator;
string alternateSeperator = (wantedSeperator == "," ? "." : ",");
if (attemptedValue.IndexOf(wantedSeperator) == -1
&& attemptedValue.IndexOf(alternateSeperator) != -1)
{
attemptedValue =
attemptedValue.Replace(alternateSeperator, wantedSeperator);
}
try
{
if (bindingContext.ModelMetadata.IsNullableValueType
&& string.IsNullOrWhiteSpace(attemptedValue))
{
return null;
}
result = decimal.Parse(attemptedValue, NumberStyles.Any);
}
catch (FormatException e)
{
bindingContext.ModelState.AddModelError(modelName, e);
}
return result;
}
}
Global.asax
public class MvcApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
...
ModelBinders.Binders.Add(typeof(decimal), new DecimalModelBinder());
ModelBinders.Binders.Add(typeof(decimal?), new DecimalModelBinder());
...
}
private void Application_BeginRequest(Object source, EventArgs e)
{
HttpApplication application = (HttpApplication)source;
HttpContext context = application.Context;
string culture = null;
if (context.Request.UserLanguages != null && Request.UserLanguages.Length > 0)
{
culture = Request.UserLanguages[0];
Thread.CurrentThread.CurrentCulture = new System.Globalization.CultureInfo(culture);
Thread.CurrentThread.CurrentUICulture = Thread.CurrentThread.CurrentCulture;
}
}
}
web.config
<globalization culture="auto" uiCulture="auto" enableClientBasedCulture="true" />
根据错误消息,我看到您正确显示了数据,但没有正确解析它。
我认为问题出在这里:
$.validator.methods.number = function (value, element) {
return this.optional(element) ||
!isNaN(Globalize.parseFloat(value));
}
在这里看不到文化用法,可能是某些 classes 没有正确的文化。根据标准。 999,999
在德语中代表 999 999
- 精度部分没有任何数字的大整数。检查 javascript 的输入并尝试调试它。
首先,enableClientBasedCulture
当前未使用 (MSDN)。看起来您的全球化 class 总是被设置为 ENG
文化,即使您在 document.ready
事件中执行了明确的 all。尝试在 web.config
中明确设置德国文化并检查您的逻辑。它应该适用于 DE
但不适用于 EN
。如果是这样,那么您每次尝试解析数据时都应该提供正确的文化。
但是,如果不是,则问题不在全球化中。
看来我还需要创建自己的范围方法.....
现在好像可以用了。
$.validator.methods.range = function(value, element, param) {
console.log('----------------');
console.log(value);
console.log(element);
var globalizedValue = value.replace(",", ".");
console.log(param[0]);
var globalizedValueParam0 = param[0];
console.log(param[1]);
var globalizedValueParam1 = param[1];
console.log(globalizedValueParam0);
console.log(globalizedValueParam1);
//return this.optional(element) || (globalizedValue >= param[0] && globalizedValue <= param[1]);
return this.optional(element) || (globalizedValue >= globalizedValueParam0 && globalizedValue <= globalizedValueParam1);
};
长话短说,我开始了一个使用 MVC 5.2.3(具有所有更新的 JS 框架)的新项目,期望我在 MVC 2 验证中遇到的许多问题都得到解决,我错了.
基本上,我试图为 DateTime
和 Decimal
提供验证,我快把自己逼疯了。
对于小数字段,在 EN 和 DE(文化)中使用浏览器,我遇到了逗号和点(小数除法)以及我设置的范围的问题。
使用 DateTime
我什至尝试了 DisplayFormat
只是为了显示日期而不是时间,无论如何 day/month/year 的顺序与 .
或/
或 -
只是失败。
示例:
- 在 DE 中 - 键入:999,999 --> 结果:错误 "Das Feld "D1 DE"muss zwischen 0 und 999,9999 liegen。" - 应该被接受。
- 在 EN 中 - 输入:999.999 --> 结果:被接受,因为它应该是(正确的)
同样适用于日期时间显示格式和类似的 JS 验证....
分享一下我目前的情况:
测试对象
public class TestObject
{
....
[Display(Name = "D2", ResourceType = typeof(WebApplication1.Models.Res.TestObject))]
[Range(0, 999.9999)]
public decimal? D1 { get; set; }
// On DBContext I have defined the range/precision
// modelBuilder.Entity<TestObject>().Property(x => x.D2).HasPrecision(7, 4);
[Display(Name = "Date1", ResourceType = typeof(WebApplication1.Models.Res.TestObject))]
//[DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = @"{0:dd\/MM\/yyyy}")]
public DateTime Date1 { get; set; }
....
}
view.cshtml
<div class="form-group">
@Html.LabelFor(model => model.D2, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.D2, new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(model => model.D2, "", new { @class = "text-danger" })
</div>
</div>
<div class="form-group">
@Html.LabelFor(model => model.Date1, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-md-10">
@Html.EditorFor(model => model.Date1, new { htmlAttributes = new { @class = "form-control" } })
@Html.ValidationMessageFor(model => model.Date1, "", new { @class = "text-danger" })
</div>
</div>
_layout.cshtml
<script src="~/Scripts/globalize/globalize.js" type="text/javascript"></script></script>
<script src="~/Scripts/globalize/cultures/globalize.culture.de.js" type="text/javascript"></script>
<script src="~/Scripts/globalize/cultures/globalize.culture.en-US.js" type="text/javascript"></script>
<script>
$.validator.methods.number = function (value, element) {
return this.optional(element) ||
!isNaN(Globalize.parseFloat(value));
}
$.validator.methods.date = function (value, element) {
return this.optional(element) ||
Globalize.parseDate(value);
}
$(document).ready(function () {
Globalize.culture('@System.Threading.Thread.CurrentThread.CurrentCulture');
});
</script>
DecimalModelBinder
public class DecimalModelBinder : DefaultModelBinder
{
public override object BindModel(ControllerContext controllerContext,
ModelBindingContext bindingContext)
{
object result = null;
// Don't do this here!
// It might do bindingContext.ModelState.AddModelError
// and there is no RemoveModelError!
//
// result = base.BindModel(controllerContext, bindingContext);
string modelName = bindingContext.ModelName;
string attemptedValue =
bindingContext.ValueProvider.GetValue(modelName).AttemptedValue;
// Depending on CultureInfo, the NumberDecimalSeparator can be "," or "."
// Both "." and "," should be accepted, but aren't.
string wantedSeperator = NumberFormatInfo.CurrentInfo.NumberDecimalSeparator;
string alternateSeperator = (wantedSeperator == "," ? "." : ",");
if (attemptedValue.IndexOf(wantedSeperator) == -1
&& attemptedValue.IndexOf(alternateSeperator) != -1)
{
attemptedValue =
attemptedValue.Replace(alternateSeperator, wantedSeperator);
}
try
{
if (bindingContext.ModelMetadata.IsNullableValueType
&& string.IsNullOrWhiteSpace(attemptedValue))
{
return null;
}
result = decimal.Parse(attemptedValue, NumberStyles.Any);
}
catch (FormatException e)
{
bindingContext.ModelState.AddModelError(modelName, e);
}
return result;
}
}
Global.asax
public class MvcApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
...
ModelBinders.Binders.Add(typeof(decimal), new DecimalModelBinder());
ModelBinders.Binders.Add(typeof(decimal?), new DecimalModelBinder());
...
}
private void Application_BeginRequest(Object source, EventArgs e)
{
HttpApplication application = (HttpApplication)source;
HttpContext context = application.Context;
string culture = null;
if (context.Request.UserLanguages != null && Request.UserLanguages.Length > 0)
{
culture = Request.UserLanguages[0];
Thread.CurrentThread.CurrentCulture = new System.Globalization.CultureInfo(culture);
Thread.CurrentThread.CurrentUICulture = Thread.CurrentThread.CurrentCulture;
}
}
}
web.config
<globalization culture="auto" uiCulture="auto" enableClientBasedCulture="true" />
根据错误消息,我看到您正确显示了数据,但没有正确解析它。
我认为问题出在这里:
$.validator.methods.number = function (value, element) {
return this.optional(element) ||
!isNaN(Globalize.parseFloat(value));
}
在这里看不到文化用法,可能是某些 classes 没有正确的文化。根据标准。 999,999
在德语中代表 999 999
- 精度部分没有任何数字的大整数。检查 javascript 的输入并尝试调试它。
首先,enableClientBasedCulture
当前未使用 (MSDN)。看起来您的全球化 class 总是被设置为 ENG
文化,即使您在 document.ready
事件中执行了明确的 all。尝试在 web.config
中明确设置德国文化并检查您的逻辑。它应该适用于 DE
但不适用于 EN
。如果是这样,那么您每次尝试解析数据时都应该提供正确的文化。
但是,如果不是,则问题不在全球化中。
看来我还需要创建自己的范围方法..... 现在好像可以用了。
$.validator.methods.range = function(value, element, param) {
console.log('----------------');
console.log(value);
console.log(element);
var globalizedValue = value.replace(",", ".");
console.log(param[0]);
var globalizedValueParam0 = param[0];
console.log(param[1]);
var globalizedValueParam1 = param[1];
console.log(globalizedValueParam0);
console.log(globalizedValueParam1);
//return this.optional(element) || (globalizedValue >= param[0] && globalizedValue <= param[1]);
return this.optional(element) || (globalizedValue >= globalizedValueParam0 && globalizedValue <= globalizedValueParam1);
};