Привязка MVC DateTime с неверным форматом даты
Asp.net-MVC теперь допускает неявную привязку объектов DateTime. У меня есть действие по линии
public ActionResult DoSomething(DateTime startDate)
{
...
}
это успешно преобразует строку из вызова ajax в DateTime. Однако мы используем формат даты dd/MM/yyyy; MVC преобразуется в MM/dd / yyyy. Например, отправка вызова действия со строкой '09/02/2009' приводит к дате и времени '02/09/2009 00:00: 00', или 2 сентября в наших локальных настройках.
Я не хочу сворачивать свою собственную модель переплет ради формата даты. Но, кажется, нет необходимости иметь, чтобы изменить действие, чтобы принять строку, а затем использовать тип datetime.Разбор, если MVC способен сделать это для меня.
есть ли способ изменить формат даты, используемый в привязке модели по умолчанию для DateTime? Разве связыватель модели по умолчанию не должен использовать ваши настройки локализации?
10 ответов:
Я только что нашел ответ на этот вопрос с более исчерпывающим googling:
Melvyn Harbour имеет подробное объяснение того, почему MVC работает с датами так, как он это делает, и как вы можете переопределить это при необходимости:
http://weblogs.asp.net/melvynharbour/archive/2008/11/21/mvc-modelbinder-and-localization.aspx
при поиске значения для разбора фреймворк выглядит в определенном порядке, а именно:
- RouteData (не показано выше)
- строка запроса URI
- запрос вида
однако только последний из них будет знать культуру. Для этого есть очень веская причина, с точки зрения локализации. Представьте, что я написал веб-приложение, показывающее информацию о рейсах авиакомпаний, которую я публикую в интернете. Я смотрю рейсы на определенную дату, нажав на ссылку на этот день (возможно, что-то вроде http://www.melsflighttimes.com/Flights/2008-11-21), а затем хотите отправить эту ссылку моему коллеге в США. Единственный способ гарантировать, что мы оба будем смотреть на одну и ту же страницу данных, - это использование инвариантной культуры. Напротив, если я использую форму для бронирования своего рейса, все происходит в плотном цикле. Данные могут уважать CurrentCulture, когда они записываются в форму, и поэтому должны уважать его при возвращении из формы форма.
Я бы глобально установил ваши культуры. ModelBinder поднять!
<system.web> <globalization uiCulture="en-AU" culture="en-AU" />
или вы просто измените это на этой странице.
Но глобально в сети.конфиг я думаю лучше
у меня была такая же проблема с привязкой короткого формата даты к свойствам модели DateTime. После просмотра многих различных примеров (не только в отношении даты и времени) я собрал следующее:
using System; using System.Globalization; using System.Web.Mvc; namespace YourNamespaceHere { public class CustomDateBinder : IModelBinder { public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) { if (controllerContext == null) throw new ArgumentNullException("controllerContext", "controllerContext is null."); if (bindingContext == null) throw new ArgumentNullException("bindingContext", "bindingContext is null."); var value = bindingContext.ValueProvider.GetValue(bindingContext.ModelName); if (value == null) throw new ArgumentNullException(bindingContext.ModelName); CultureInfo cultureInf = (CultureInfo)CultureInfo.CurrentCulture.Clone(); cultureInf.DateTimeFormat.ShortDatePattern = "dd/MM/yyyy"; bindingContext.ModelState.SetModelValue(bindingContext.ModelName, value); try { var date = value.ConvertTo(typeof(DateTime), cultureInf); return date; } catch (Exception ex) { bindingContext.ModelState.AddModelError(bindingContext.ModelName, ex); return null; } } } public class NullableCustomDateBinder : IModelBinder { public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) { if (controllerContext == null) throw new ArgumentNullException("controllerContext", "controllerContext is null."); if (bindingContext == null) throw new ArgumentNullException("bindingContext", "bindingContext is null."); var value = bindingContext.ValueProvider.GetValue(bindingContext.ModelName); if (value == null) return null; CultureInfo cultureInf = (CultureInfo)CultureInfo.CurrentCulture.Clone(); cultureInf.DateTimeFormat.ShortDatePattern = "dd/MM/yyyy"; bindingContext.ModelState.SetModelValue(bindingContext.ModelName, value); try { var date = value.ConvertTo(typeof(DateTime), cultureInf); return date; } catch (Exception ex) { bindingContext.ModelState.AddModelError(bindingContext.ModelName, ex); return null; } } } }
чтобы сохранить способ, которым маршруты и т. д. являются regiseterd в глобальном файле ASAX, я также добавил новый класс sytatic в папку App_Start моего проекта MVC4 с именем CustomModelBinderConfig:
using System; using System.Web.Mvc; namespace YourNamespaceHere { public static class CustomModelBindersConfig { public static void RegisterCustomModelBinders() { ModelBinders.Binders.Add(typeof(DateTime), new CustomModelBinders.CustomDateBinder()); ModelBinders.Binders.Add(typeof(DateTime?), new CustomModelBinders.NullableCustomDateBinder()); } } }
затем я просто вызываю статические RegisterCustomModelBinders из моего глобального Asasx Application_Start, как это:
protected void Application_Start() { /* bla blah bla the usual stuff and then */ CustomModelBindersConfig.RegisterCustomModelBinders(); }
важное замечание здесь заключается в том, что если вы пишете значение DateTime в hiddenfield следующим образом:
@Html.HiddenFor(model => model.SomeDate) // a DateTime property @Html.Hiddenfor(model => model) // a model that is of type DateTime
Я сделал это, и фактическое значение на странице было в формате "MM/dd/yyyy hh:mm:ss tt" вместо "dd/MM / yyyy hh:mm:ss tt", как я хотел. Это привело к тому, что моя проверка модели либо завершилась неудачей, либо вернула неправильную дату (очевидно, поменяв значения дня и месяца).
После много головной царапины и неудачные попытки решение состояло в том, чтобы установить информацию о культуре для каждого запроса, сделав это в глобальном масштабе.АСАКС:
protected void Application_BeginRequest() { CultureInfo cInf = new CultureInfo("en-ZA", false); // NOTE: change the culture name en-ZA to whatever culture suits your needs cInf.DateTimeFormat.DateSeparator = "/"; cInf.DateTimeFormat.ShortDatePattern = "dd/MM/yyyy"; cInf.DateTimeFormat.LongDatePattern = "dd/MM/yyyy hh:mm:ss tt"; System.Threading.Thread.CurrentThread.CurrentCulture = cInf; System.Threading.Thread.CurrentThread.CurrentUICulture = cInf; }
Он не будет работать, если вы вставляете его в Application_Start или даже Session_Start, так как это назначает его текущему потоку для сеанса. Как вы хорошо знаете, веб-приложения не имеют состояния, поэтому поток, который обслуживал ваш запрос ранее, является тем же потоком, обслуживающим ваш текущий запрос, поэтому ваша информация о культуре отправилась в большой GC в цифровое небо.
спасибо: Иван Златев - http://ivanz.com/2010/11/03/custom-model-binding-using-imodelbinder-in-asp-net-mvc-two-gotchas/
это будет немного отличаться в MVC 3.
предположим, что у нас есть контроллер и представление с Get method
public ActionResult DoSomething(DateTime dateTime) { return View(); }
мы должны добавить ModelBinder
public class DateTimeBinder : IModelBinder { #region IModelBinder Members public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) { DateTime dateTime; if (DateTime.TryParse(controllerContext.HttpContext.Request.QueryString["dateTime"], CultureInfo.GetCultureInfo("en-GB"), DateTimeStyles.None, out dateTime)) return dateTime; //else return new DateTime();//or another appropriate default ; } #endregion }
и команда в Application_Start () глобального.асакс
ModelBinders.Binders.Add(typeof(DateTime), new DateTimeBinder());
также стоит отметить, что даже без создания собственной модели binder несколько различных форматов могут быть проанализированы.
например, в США все следующие строки эквивалентны и автоматически привязывается к тот же значение даты и времени:
/ company / press / may%2001%202008
/ company / press / 2008-05-01
/компании/пресс/05-01-2008
Я настоятельно рекомендую использовать гггг-ММ-ДД потому что его гораздо более портативный. Вы действительно не хотите иметь дело с обработкой нескольких локализованных форматов. Если кто-то заказывает рейс на 1 мая вместо 5 января у вас будут большие проблемы!
NB: мне не ясно exaclty если yyyy-mm-dd универсально анализируется во всех культурах, поэтому, возможно, кто-то, кто знает, может добавить комментарий.
Я установил ниже конфигурации на моем MVC4 и он работает как шарм
<globalization uiCulture="auto" culture="auto" />
попробуйте использовать toISOString(). Он возвращает строку в формате ISO8601.
метод GET
javascript
$.get('/example/doGet?date=' + new Date().toISOString(), function (result) { console.log(result); });
c#
[HttpGet] public JsonResult DoGet(DateTime date) { return Json(date.ToString(), JsonRequestBehavior.AllowGet); }
метод POST
javascript
$.post('/example/do', { date: date.toISOString() }, function (result) { console.log(result); });
c#
[HttpPost] public JsonResult Do(DateTime date) { return Json(date.ToString()); }
public class DateTimeFilter : ActionFilterAttribute { public override void OnActionExecuting(ActionExecutingContext filterContext) { if (filterContext.HttpContext.Request.RequestType == "GET") { foreach (var parameter in filterContext.ActionParameters) { var properties = parameter.Value.GetType().GetProperties(); foreach (var property in properties) { Type type = Nullable.GetUnderlyingType(property.PropertyType) ?? property.PropertyType; if (property.PropertyType == typeof(System.DateTime) || property.PropertyType == typeof(DateTime?)) { DateTime dateTime; if (DateTime.TryParse(filterContext.HttpContext.Request.QueryString[property.Name], CultureInfo.CurrentUICulture, DateTimeStyles.None, out dateTime)) property.SetValue(parameter.Value, dateTime,null); } } } } } }
public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) { var str = controllerContext.HttpContext.Request.QueryString[bindingContext.ModelName]; if (string.IsNullOrEmpty(str)) return null; var date = DateTime.ParseExact(str, "dd.MM.yyyy", null); return date; }
Я
CurrentCulture
иCurrentUICulture
мой пользовательский базовый контроллерprotected override void Initialize(RequestContext requestContext) { base.Initialize(requestContext); Thread.CurrentThread.CurrentCulture = CultureInfo.GetCultureInfo("en-GB"); Thread.CurrentThread.CurrentUICulture = CultureInfo.GetCultureInfo("en-GB"); }