ASP.NET MVC datetime culture issue when passing value back to controller

前端 未结 7 1057
既然无缘
既然无缘 2020-12-05 18:32

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 d

相关标签:
7条回答
  • 2020-12-05 19:13

    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.

    0 讨论(0)
  • 2020-12-05 19:15

    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;
          }
      }
      }
      
    0 讨论(0)
  • 2020-12-05 19:17

    that did the trick for me

        <system.web>     
           <globalization enableClientBasedCulture="true" uiCulture="Auto" culture="Auto" />
        </system.web>
    
    0 讨论(0)
  • 2020-12-05 19:17

    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());

    0 讨论(0)
  • 2020-12-05 19:28

    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);
    }
    
    0 讨论(0)
  • 2020-12-05 19:32

    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);
        }
    }
    
    0 讨论(0)
提交回复
热议问题