将值传回控制器时出现 ASP.NET MVC 日期时间区域性问题

发布于 2024-12-11 23:00:16 字数 1701 浏览 2 评论 0原文

我如何告诉我的控制器/模型解析日期时间时应该采用哪种文化?

我使用了一些这篇文章 将 jquery datepicker 实现到我的 mvc 应用程序中。

当我提交日期时,它会“在翻译中丢失”,我没有使用美国格式的日期,因此当它发送到我的控制器时,它只是变成空。

我有一个用户选择日期的表单:

@using (Html.BeginForm("List", "Meter", FormMethod.Get))
{
    @Html.LabelFor(m => m.StartDate, "From:")
    <div>@Html.EditorFor(m => m.StartDate)</div>

    @Html.LabelFor(m => m.EndDate, "To:")
    <div>@Html.EditorFor(m => m.EndDate)</div>
}

我为此制作了一个编辑模板,以实现 jquery datepicker:

@model DateTime
@Html.TextBox("", Model.ToString("dd-MM-yyyy"), new { @class = "date" }) 

然后我创建像这样的 datepicker 小部件。

$(document).ready(function () {
    $('.date').datepicker({ dateFormat: "dd-mm-yy" });
});

这一切都运行良好。

这就是问题开始的地方,这是我的控制器:

[HttpGet]
public ActionResult List(DateTime? startDate = null, DateTime? endDate = null)
{
    //This is where startDate and endDate becomes null if the dates dont have the expected formatting.
}

这就是为什么我想以某种方式告诉我的控制器它应该期望什么文化? 难道是我的模型不对?我可以以某种方式告诉它使用哪种文化,比如数据注释属性吗?

public class MeterViewModel {
    [Required]
    public DateTime StartDate { get; set; }
    [Required]
    public DateTime EndDate { get; set; }
}

编辑:此链接解释我的问题以及一个很好的解决方案。感谢格多隆

How can i tell my controller/model what kind of culture it should expect for parsing a datetime?

I was using some of this post to implement jquery datepicker into my mvc application.

When i submit the date it gets "lost in translation" i'm not using the US formatting for the date, so when it gets sent to my controller it simply becomes null.

I have a form where the user chooses a date:

@using (Html.BeginForm("List", "Meter", FormMethod.Get))
{
    @Html.LabelFor(m => m.StartDate, "From:")
    <div>@Html.EditorFor(m => m.StartDate)</div>

    @Html.LabelFor(m => m.EndDate, "To:")
    <div>@Html.EditorFor(m => m.EndDate)</div>
}

I've made an edit template for this, to implement the jquery datepicker:

@model DateTime
@Html.TextBox("", Model.ToString("dd-MM-yyyy"), new { @class = "date" }) 

I then create the datepicker widgets like this.

$(document).ready(function () {
    $('.date').datepicker({ dateFormat: "dd-mm-yy" });
});

All this works fine.

Here is where the problems start, this is my controller:

[HttpGet]
public ActionResult List(DateTime? startDate = null, DateTime? endDate = null)
{
    //This is where startDate and endDate becomes null if the dates dont have the expected formatting.
}

This is why i would like to somehow tell my controller what culture it should expect?
Is my model wrong? can i somehow tell it which culture to use, like with the data annotation attributes?

public class MeterViewModel {
    [Required]
    public DateTime StartDate { get; set; }
    [Required]
    public DateTime EndDate { get; set; }
}

Edit: this link explains my issue and a very good solution to it aswell. Thanks to gdoron

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

浅语花开 2024-12-18 23:00:16

您可以使用 IModelBinder 更改默认模型绑定器以使用用户区域性

   public class DateTimeBinder : IModelBinder
   {
       public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
       {
           var value = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);
           var date = value.ConvertTo(typeof(DateTime), CultureInfo.CurrentCulture);

           return date;    
       }
   }

并在 Global.Asax 中写入:

ModelBinders.Binders.Add(typeof(DateTime), new DateTimeBinder());
ModelBinders.Binders.Add(typeof(DateTime?), new DateTimeBinder());

阅读更多信息 这个优秀的博客,描述了为什么 Mvc 框架团队向所有用户实施默认文化。

you can change the default model binder to use the user culture using IModelBinder

   public class DateTimeBinder : IModelBinder
   {
       public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
       {
           var value = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);
           var date = value.ConvertTo(typeof(DateTime), CultureInfo.CurrentCulture);

           return date;    
       }
   }

And in the Global.Asax write:

ModelBinders.Binders.Add(typeof(DateTime), new DateTimeBinder());
ModelBinders.Binders.Add(typeof(DateTime?), new DateTimeBinder());

Read more at this excellent blog that describe why Mvc framework team implemented a default Culture to all users.

终遇你 2024-12-18 23:00:16

您可以创建 Binder 扩展来处理区域性格式的日期。

这是我编写的一个示例,用于处理 Decimal 类型的相同问题,希望您明白

 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
     {
       actualValue = Convert.ToDecimal(valueResult.AttemptedValue, CultureInfo.CurrentCulture);
     }
     catch (FormatException e)
     {
       modelState.Errors.Add(e);
     }

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

更新

要使用它,只需像这样在 Global.asax 中声明绑定器

protected void Application_Start()
{
  AreaRegistration.RegisterAllAreas();
  RegisterGlobalFilters(GlobalFilters.Filters);
  RegisterRoutes(RouteTable.Routes);

  //HERE you tell the framework how to handle decimal values
  ModelBinders.Binders.Add(typeof(decimal), new DecimalModelBinder());

  DependencyResolver.SetResolver(new ETAutofacDependencyResolver());
}

然后当模型绑定器必须做一些工作时,它会自动知道要做什么。
例如,这是一个包含一些十进制类型属性的模型的操作。我只是什么都不做

[HttpPost]
public ActionResult Edit(int id, MyViewModel viewModel)
{
  if (ModelState.IsValid)
  {
    try
    {
      var model = new MyDomainModelEntity();
      model.DecimalValue = viewModel.DecimalValue;
      repository.Save(model);
      return RedirectToAction("Index");
    }
    catch (RulesException ex)
    {
      ex.CopyTo(ModelState);
    }
    catch
    {
      ModelState.AddModelError("", "My generic error message");
    }
  }
  return View(model);
}

You can create a Binder extension to handle the date in the culture format.

This is a sample I wrote to handle the same problem with Decimal type, hope you get the idea

 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
     {
       actualValue = Convert.ToDecimal(valueResult.AttemptedValue, CultureInfo.CurrentCulture);
     }
     catch (FormatException e)
     {
       modelState.Errors.Add(e);
     }

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

Update

To use it simply declare the binder in Global.asax like this

protected void Application_Start()
{
  AreaRegistration.RegisterAllAreas();
  RegisterGlobalFilters(GlobalFilters.Filters);
  RegisterRoutes(RouteTable.Routes);

  //HERE you tell the framework how to handle decimal values
  ModelBinders.Binders.Add(typeof(decimal), new DecimalModelBinder());

  DependencyResolver.SetResolver(new ETAutofacDependencyResolver());
}

Then when the modelbinder has to do some work, it will know automatically what to do.
For example, this is an action with a model containing some properties of type decimal. I simply do nothing

[HttpPost]
public ActionResult Edit(int id, MyViewModel viewModel)
{
  if (ModelState.IsValid)
  {
    try
    {
      var model = new MyDomainModelEntity();
      model.DecimalValue = viewModel.DecimalValue;
      repository.Save(model);
      return RedirectToAction("Index");
    }
    catch (RulesException ex)
    {
      ex.CopyTo(ModelState);
    }
    catch
    {
      ModelState.AddModelError("", "My generic error message");
    }
  }
  return View(model);
}
卸妝后依然美 2024-12-18 23:00:16

出现此问题的原因是您在表单上使用 GET 方法。 MVC 中的 QueryString 值提供程序始终使用 Invariant/US 日期格式。请参阅:MVC DateTime 绑定日期格式不正确

解决方案有以下三种:

  1. 将方法更改为 POST。
  2. 正如其他人所说,在提交之前将日期格式更改为 ISO 8601“yyyy-mm-dd”。
  3. 使用自定义绑定器始终将查询字符串日期视为 GB。如果您这样做,您必须确保所有日期都采用这种形式:

    公共类 UKDateTimeModelBinder : IModelBinder
    {
    私有静态只读 ILog 记录器 = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
    
    /// <摘要>
    /// 修复使用 GET 方法时的日期解析问题。根据此处给出的答案进行修改:
    /// https://stackoverflow.com/questions/528545/mvc-datetime-binding-with-in Correct-date-format
    /// 
    /// 控制器上下文。
    /// 绑定上下文。
    /// <返回>;
    /// 如果原始值为 null 或空或无法解析,则转换后的绑定值或 null。
    /// 
    公共对象 BindModel(ControllerContext 控制器上下文,ModelBindingContext 绑定上下文)
    {
        var vpr = BindingContext.ValueProvider.GetValue(BindingContext.ModelName);
    
        if (vpr == null)
        {
            返回空值;
    
        }
    
        var date = vpr.AttemptedValue;
    
        if (String.IsNullOrEmpty(日期))
        {
            返回空值;
        }
    
        logger.DebugFormat("将绑定日期'{0}'解析为英国格式。", date);
    
        // 在转换日期之前将 ModelState 设置为第一个尝试的值。这是为了确保 ModelState 有
        // 一个值。当我们转换它时,我们将用完整的通用日期覆盖它。
        绑定Context.ModelState.SetModelValue(绑定Context.ModelName,绑定Context.ValueProvider.GetValue(绑定Context.ModelName));
    
        尝试
        {
            var realDate = DateTime.Parse(日期, System.Globalization.CultureInfo.GetCultureInfoByIetfLanguageTag("en-GB"));
    
            // 现在将 ModelState 值设置为完整值,以便始终可以使用 InvarianCulture 进行解析,这是
            // QueryStringValueProvider 的默认值。
            bindingContext.ModelState.SetModelValue(bindingContext.ModelName, new ValueProviderResult(date, realDate.ToString("yyyy-MM-dd hh:mm:ss"), System.Globalization.CultureInfo.GetCultureInfoByIetfLanguageTag("en-GB")));
    
            返回真实日期;
        }
        捕获(异常)
        {
            logger.ErrorFormat("将绑定日期 '{0}' 解析为英国格式时出错。", date);
    
            bindingContext.ModelState.AddModelError(bindingContext.ModelName, String.Format("\"{0}\" 无效。", bindingContext.ModelName));
            返回空值;
        }
    }
    }
    

This issue arises because you are using the GET method on your Form. The QueryString Value Provider in MVC always uses Invariant/US date format. See: MVC DateTime binding with incorrect date format

There are three solutions:

  1. Change your method to POST.
  2. As someone else says, change the date format to ISO 8601 "yyyy-mm-dd" before submission.
  3. Use a custom binder to always treat Query String dates as GB. If you do this you have to make sure that all dates are in that form:

    public class UKDateTimeModelBinder : IModelBinder
    {
    private static readonly ILog logger = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
    
    /// <summary>
    /// Fixes date parsing issue when using GET method. Modified from the answer given here:
    /// https://stackoverflow.com/questions/528545/mvc-datetime-binding-with-incorrect-date-format
    /// </summary>
    /// <param name="controllerContext">The controller context.</param>
    /// <param name="bindingContext">The binding context.</param>
    /// <returns>
    /// The converted bound value or null if the raw value is null or empty or cannot be parsed.
    /// </returns>
    public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
        var vpr = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);
    
        if (vpr == null)
        {
            return null;
    
        }
    
        var date = vpr.AttemptedValue;
    
        if (String.IsNullOrEmpty(date))
        {
            return null;
        }
    
        logger.DebugFormat("Parsing bound date '{0}' as UK format.", date);
    
        // Set the ModelState to the first attempted value before we have converted the date. This is to ensure that the ModelState has
        // a value. When we have converted it, we will override it with a full universal date.
        bindingContext.ModelState.SetModelValue(bindingContext.ModelName, bindingContext.ValueProvider.GetValue(bindingContext.ModelName));
    
        try
        {
            var realDate = DateTime.Parse(date, System.Globalization.CultureInfo.GetCultureInfoByIetfLanguageTag("en-GB"));
    
            // Now set the ModelState value to a full value so that it can always be parsed using InvarianCulture, which is the
            // default for QueryStringValueProvider.
            bindingContext.ModelState.SetModelValue(bindingContext.ModelName, new ValueProviderResult(date, realDate.ToString("yyyy-MM-dd hh:mm:ss"), System.Globalization.CultureInfo.GetCultureInfoByIetfLanguageTag("en-GB")));
    
            return realDate;
        }
        catch (Exception)
        {
            logger.ErrorFormat("Error parsing bound date '{0}' as UK format.", date);
    
            bindingContext.ModelState.AddModelError(bindingContext.ModelName, String.Format("\"{0}\" is invalid.", bindingContext.ModelName));
            return null;
        }
    }
    }
    
终难愈 2024-12-18 23:00:16

提交日期时,您应始终尝试以“yyyy-MM-dd”格式提交。这将使其变得独立于文化。

我通常有一个隐藏字段以这种格式维护日期。使用 jQuery UI 的日期选择器这相对简单。

When submitting a date you should always try and submit it in the format "yyyy-MM-dd". This will allow for it to become culture independent.

I normally have a hidden field which maintains the date in this format. This is relatively simple using jQuery UI's datepicker.

清眉祭 2024-12-18 23:00:16

为什么不简单地检查数据的文化并对其进行转换呢?这种简单的方法使我能够在模型中使用强类型日期,显示操作链接并在所需区域设置中编辑字段,而不必大惊小怪地将其绑定回强类型日期时间:

public class DateTimeBinder : IModelBinder
{
    public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
        var value = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);
        return value.ConvertTo(typeof(DateTime), value.Culture);
    }
}

Why not simply inspect the culture of the data and convert it as such? This simple approach allowed me to use strongly typed dates in models, show action links and edit fields in the desired locale and not have to fuss at all binding it back into a strongly typed DateTime:

public class DateTimeBinder : IModelBinder
{
    public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
        var value = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);
        return value.ConvertTo(typeof(DateTime), value.Culture);
    }
}
时光沙漏 2024-12-18 23:00:16

这对我有用

    <system.web>     
       <globalization enableClientBasedCulture="true" uiCulture="Auto" culture="Auto" />
    </system.web>

that did the trick for me

    <system.web>     
       <globalization enableClientBasedCulture="true" uiCulture="Auto" culture="Auto" />
    </system.web>
手长情犹 2024-12-18 23:00:16

我有一个基于 @gdoron 的帖子的 MVC5 更新解决方案。如果其他人正在寻找这个,我会分享它。该类继承自 DefaultModelBinder,并对无效日期进行异常处理。它还可以处理 null 值:

public class DateTimeModelBinder : DefaultModelBinder
{
    public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
        object result = null;

        var modelName = bindingContext.ModelName;
        var attemptedValue = bindingContext.ValueProvider.GetValue(modelName)?.AttemptedValue;

        // in datetime? binding attemptedValue can be Null
        if (attemptedValue != null && !string.IsNullOrWhiteSpace(attemptedValue))
        {
            try
            {
                var value = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);
                result = DateTime.Parse(value.AttemptedValue, CultureInfo.CurrentCulture);
            }
            catch (FormatException e)
            {
                bindingContext.ModelState.AddModelError(modelName, e);
            }
        }

        return result;
    }
}

就像 Global.Asax 中提到的示例一样,编写

ModelBinders.Binders.Add(typeof(DateTime), new DateTimeBinder());
ModelBinders.Binders.Add(typeof(DateTime?), new DateTimeBinder());

I have a updated solution for MVC5 based on the Post of @gdoron. I will share it in case anyone else is looking for this. The class inherits from DefaultModelBinder and has exception handling for invalid dates. It also can handle null values:

public class DateTimeModelBinder : DefaultModelBinder
{
    public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
        object result = null;

        var modelName = bindingContext.ModelName;
        var attemptedValue = bindingContext.ValueProvider.GetValue(modelName)?.AttemptedValue;

        // in datetime? binding attemptedValue can be Null
        if (attemptedValue != null && !string.IsNullOrWhiteSpace(attemptedValue))
        {
            try
            {
                var value = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);
                result = DateTime.Parse(value.AttemptedValue, CultureInfo.CurrentCulture);
            }
            catch (FormatException e)
            {
                bindingContext.ModelState.AddModelError(modelName, e);
            }
        }

        return result;
    }
}

And just like the mentioned sample in the Global.Asax write

ModelBinders.Binders.Add(typeof(DateTime), new DateTimeBinder());
ModelBinders.Binders.Add(typeof(DateTime?), new DateTimeBinder());

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文