【问题标题】:MVC datetime localization issueMVC 日期时间本地化问题
【发布时间】:2014-12-17 12:11:01
【问题描述】:

在我们的应用程序中,我们为用户存储了一种文化。例如“en-US”。 然后,当我们在视图中显示日期时,我们会这样做:

date.ToString("d", user.CultureInfo)

这导致用户以他喜欢的方式查看日期。 当然,用户以同样的方式输入日期。例如,美国日期“2014 年 12 月 1 日”是 2014 年 12 月 1 日。 当 MVC 尝试自动转换它并且 MVC 线程运行另一种文化时,用户不喜欢它一开始转换错误。该日期被意外转换为 2014 年 1 月 12 日。 为了解决这个问题,我创建了一个自定义 DateTime 模型绑定器:

public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
    var value = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);
    var date = value.ConvertTo(typeof(DateTime), HttpContext.Current.UserContext().CultureInfo);
    return date;
}

现在我们在用户实际文化的帮助下转换日期。 但这不幸地导致了另一个问题。例如,在不同的视图中,我们有一个隐藏的输入,如下所示:

@Html.HiddenFor(m => m.created)

这将使用 MVC 线程 (1.12.2014) 的默认区域性编写。但是当用户返回数据时,自定义模型绑定器将尝试按用户文化转换日期,并且转换出错。 我可以通过像这样的视图代码来解决这个问题(或使用用户文化对其进行格式化):

@Html.HiddenFor(m => m.created, new { @Value = Model.created.ToString("yyyy-MM-dd") })

因为这种日期格式看起来总是正确的。 但我想避免搜索所有代码来试图找到我需要更改的所有地方。而且我肯定会留下错误。 本地化很常见,所以我似乎遗漏了一些东西。这应该是解决问题的一种更简单的方法。 如果不是,那么我该如何解决我创建的后一个问题?有没有办法通过一些全局设置以相同的方式输出这些日期,而不必更改受此影响的每一行代码?

【问题讨论】:

  • 您能否在自定义活页夹中使用DateTimepRaseExact 将字符串转换为DateTime 从用户的文化中传递格式(CultureInfo.DateTimeFormat.ShortDatePattern 适用于隐藏字段?但是,如果隐藏字段是 dd/mm/yyyy 和客户的 mm/dd/yyyy 含糊不清且不可避免(最好对所有非用户显示日期使用 ISO 格式)。

标签: c# asp.net-mvc datetime localization


【解决方案1】:

我建议您使用视图模型仅显示您想要显示和编辑的属性。当数据甚至不应该被修改时,没有必要将数据发送到客户端并将其发送回服务器。您只是增加了双向数据传输,并使自己面临过度发布攻击。在 POST 方法中,获取数据模型,只更新你需要的属性并保存。

这将解决问题(预防胜于治疗),但如果您确实想包含隐藏的输入,您可以通过创建自定义 html 帮助器来使其更健壮,比如

 public static MvcHtmlString UIDateForFor<TModel, TValue>(this HtmlHelper<TModel> helper, Expression<Func<TModel, TValue>> expression, isHidden = false)

根据isHidden 的值,输出将是隐藏输入或文本框,其 value 属性设置为自定义模型绑定器的正确值。例如,对于隐藏输入的 sn-p 可能看起来像

ModelMetadata metaData = ModelMetadata.FromLambdaExpression(expression, helper.ViewData);
string name = ExpressionHelper.GetExpressionText(expression);
if (isHidden)
{
  TagBuilder input = new Tagbuilder("input");
  input.MergeAttribute("type", "hidden");
  input.MergeAttribute("name", name);
  input.MergeAttribute("value", metaData.Model.ToString(helper.ViewContext.HttpContext.UserContext().CultureInfo));
  return MvcHtmlString.Create(input.ToString());
}
else
{
  ....

然后用作

@Html.UIDateFor(m => m.MyEditableDate) // input type=text" (or browser datepicker if type="date")
@Html.UIDateFor(m => m.Created, true) // input type="hidden"

您还可以考虑包含另一个元素或标签,以呈现有关预期格式的信息(如果其可编辑)。

但是,即使这样也没有解决您尝试做的其他潜在问题,特别是不显眼的客户端验证。

【讨论】:

  • 我想在验证模型等时来回发布数据可以让我免于另一个数据库调用。也许最好改变这个策略。我可能应该研究为什么我会这样做,正如你所指出的,我将不可编辑的数据发布到客户端并返回的事实本身可能是一个问题。为此 +1。
【解决方案2】:

你有两个选择 -

  1. 使用标准日期时间模式

    Model.created.ToString("d")
    

    在这种情况下,您的日期将被格式化为特定文化。

  2. 对日期使用自定义解析器 - 例如

    public static DateTime ParseExactLocalized(string dateString)
    {
        try
        {
            var mask = DateMaskServer;
            var formatsCulture = Thread.CurrentThread.CurrentCulture;
            DateTime dt;
            if (DateTime.TryParseExact(dateString, mask, formatsCulture, DateTimeStyles.None, out dt))
                return dt;
    
            return DateTime.ParseExact(dateString, mask, CultureInfo.InvariantCulture);
        }
        catch (FormatException ex)
        {
            throw new FormatException("Wrong date format.", new FormatException(string.Format("Could not parse DateTime. Value: {0}", dateString), ex));
            //throw new FormatException(string.Format("Could not parse DateTime. Value: {0}, Mask: {1}", dateString, mask), ex);                
        }
    }
    

在您的情况下,美国格式将使用不变文化进行解析

【讨论】:

  • 这怎么能行。如果用户在他们的文化 (dd/MM/yyyy) 中发回 31/12/2014 代表 12 月 31 日,如果服务器文化是不变的或 en-US (MM/dd/yyy),这将失败。无论如何为时已晚,因为模型绑定器已经尝试绑定日期但失败。
  • 如果您的模型属性为字符串,而不是日期时间,这将起作用。将属性作为字符串可能对上述日期选择器和案例很有用。
  • 使用string 代替DateTime - 你是认真的吗? (它不应该与日期选择器一起使用)
  • 为什么不呢?如果您需要自定义日期格式,那有什么问题?实际上,您有日期时间字段,但不要将它们公开给您的视图。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-12-18
  • 2011-11-17
  • 2011-09-25
  • 1970-01-01
  • 2020-06-02
  • 1970-01-01
  • 2017-06-23
相关资源
最近更新 更多