Asp.net-MVC umożliwia teraz niejawne wiązanie obiektów DateTime. Mam działanie podobne do
public ActionResult DoSomething(DateTime startDate)
{
...
}
To pomyślnie konwertuje ciąg z wywołania AJAX na DateTime. Jednak używamy formatu daty dd / MM / rrrr; MVC konwertuje na MM / dd / rrrr. Na przykład przesłanie wezwania do działania z ciągiem „09/02/2009” skutkuje datą i godziną „02/09/2009 00:00:00” lub 2 września w naszych ustawieniach lokalnych.
Nie chcę zwijać własnego segregatora ze względu na format daty. Ale wydaje się niepotrzebne, aby musieć zmieniać akcję, aby zaakceptować ciąg, a następnie użyć DateTime.Parse, jeśli MVC jest w stanie zrobić to za mnie.
Czy istnieje sposób na zmianę formatu daty używanego w domyślnym segregatorze modelu dla DateTime? Czy i tak domyślny segregator modelu nie powinien używać ustawień lokalizacji?
źródło
Odpowiedzi:
Właśnie znalazłem odpowiedź na to pytanie w bardziej wyczerpujących googlach:
Melvyn Harbor ma dokładne wyjaśnienie, dlaczego MVC działa z datami w taki sposób, w jaki działa, i jak możesz to zmienić, jeśli to konieczne:
http://weblogs.asp.net/melvynharbour/archive/2008/11/21/mvc-modelbinder-and-localization.aspx
źródło
MM/dd/yyyy
formatu, w przeciwnym razie wyświetla błąd walidacjiThe field BeginDate must be a date.
, jak mogę sprawić, by serwer zaakceptowałdd/MM/yyyy
format?Ustawiłbym wasze kultury na całym świecie. ModelBinder podnieś to!
<system.web> <globalization uiCulture="en-AU" culture="en-AU" />
Lub po prostu zmień to dla tej strony.
Ale myślę, że globalnie w web.config jest lepiej
źródło
Mam ten sam problem z powiązaniem formatu krótkiej daty z właściwościami modelu DateTime. Po zapoznaniu się z wieloma różnymi przykładami (nie tylko dotyczącymi DateTime) zestawiłem następujące:
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; } } } }
Aby zachować sposób, w jaki trasy itp są rejestrowane w globalnym pliku ASAX, dodałem również nową klasę sytatic do folderu App_Start mojego projektu MVC4 o nazwie 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()); } } }
Następnie po prostu wywołuję statyczne RegisterCustomModelBinders z mojej Global ASASX Application_Start w następujący sposób:
protected void Application_Start() { /* bla blah bla the usual stuff and then */ CustomModelBindersConfig.RegisterCustomModelBinders(); }
Ważną informacją jest to, że jeśli napiszesz wartość DateTime w ukrytym polu, jak to:
@Html.HiddenFor(model => model.SomeDate) // a DateTime property @Html.Hiddenfor(model => model) // a model that is of type DateTime
Zrobiłem to, a rzeczywista wartość na stronie była w formacie „MM / dd / rrrr gg: mm: ss tt” zamiast „dd / MM / rrrr gg: mm: ss tt”, tak jak chciałem. To spowodowało, że walidacja mojego modelu zakończyła się niepowodzeniem lub zwróceniem niewłaściwej daty (oczywiście zamieniając wartości dnia i miesiąca).
Po wielu drapaniu w głowę i nieudanych próbach rozwiązaniem było ustawienie informacji o kulturze dla każdego żądania, robiąc to w Global.ASAX:
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; }
Nie zadziała, jeśli umieścisz go w Application_Start lub nawet Session_Start, ponieważ przypisuje go to do bieżącego wątku sesji. Jak dobrze wiesz, aplikacje internetowe są bezstanowe, więc wątek, który wcześniej obsługiwał twoje żądanie, nie jest tym samym wątkiem obsługującym twoje bieżące żądanie, dlatego informacje o kulturze trafiły do wielkiego GC w cyfrowym niebie.
Podziękowania dla: Ivan Zlatev - http://ivanz.com/2010/11/03/custom-model-binding-using-imodelbinder-in-asp-net-mvc-two-gotchas/
garik - https://stackoverflow.com/a/2468447/578208
Dmitry - https://stackoverflow.com/a/11903896/578208
źródło
W MVC 3 będzie trochę inaczej.
Załóżmy, że mamy kontroler i widok z metodą Get
public ActionResult DoSomething(DateTime dateTime) { return View(); }
Powinniśmy dodać 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 }
i polecenie w Application_Start () w Global.asax
ModelBinders.Binders.Add(typeof(DateTime), new DateTimeBinder());
źródło
IModelBinder
, szczególnie jeśli chodzi o walidację. Działa również tylko wtedy, gdy nazwaDateTime
to dateTime .DateTime?
działa to tylko wtedy, gdy dodasz kolejne wywołanie do zaModelBinders.Binders.Add
pomocątypeof(DateTime?)
.Warto również zauważyć, że nawet bez tworzenia własnego segregatora modelu można przeanalizować wiele różnych formatów.
Na przykład w Stanach Zjednoczonych wszystkie poniższe ciągi są równoważne i są automatycznie przypisywane do tej samej wartości DateTime:
Zdecydowanie sugerowałbym użycie rrrr-mm-dd, ponieważ jest dużo bardziej przenośny. Naprawdę nie chcesz zajmować się obsługą wielu zlokalizowanych formatów. Jeśli ktoś zarezerwuje lot 1 maja zamiast 5 stycznia, będziesz miał duże problemy!
NB: Nie wiem dokładnie, czy rrrr-mm-dd jest powszechnie analizowane we wszystkich kulturach, więc może ktoś, kto wie, może dodać komentarz.
źródło
Spróbuj użyć toISOString (). Zwraca ciąg w formacie ISO8601.
GET metoda
javascript
$.get('/example/doGet?date=' + new Date().toISOString(), function (result) { console.log(result); });
do#
[HttpGet] public JsonResult DoGet(DateTime date) { return Json(date.ToString(), JsonRequestBehavior.AllowGet); }
Metoda POST
javascript
$.post('/example/do', { date: date.toISOString() }, function (result) { console.log(result); });
do#
[HttpPost] public JsonResult Do(DateTime date) { return Json(date.ToString()); }
źródło
Ustawiłem poniższą konfigurację na moim MVC4 i działa to jak urok
<globalization uiCulture="auto" culture="auto" />
źródło
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); } } } } } }
źródło
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; }
źródło
Ustawiłem
CurrentCulture
iCurrentUICulture
mój niestandardowy kontroler podstawowyprotected override void Initialize(RequestContext requestContext) { base.Initialize(requestContext); Thread.CurrentThread.CurrentCulture = CultureInfo.GetCultureInfo("en-GB"); Thread.CurrentThread.CurrentUICulture = CultureInfo.GetCultureInfo("en-GB"); }
źródło