development

날짜 형식이 잘못된 MVC DateTime 바인딩

big-blog 2020. 7. 5. 07:39
반응형

날짜 형식이 잘못된 MVC DateTime 바인딩


Asp.net-MVC는 이제 DateTime 객체의 암시 적 바인딩을 허용합니다. 나는 라인을 따라 행동한다

public ActionResult DoSomething(DateTime startDate) 
{ 
... 
}

성공적으로 문자열을 ajax 호출에서 DateTime으로 변환합니다. 그러나 날짜 형식 dd / MM / yyyy를 사용합니다. MVC가 MM / dd / yyyy로 변환 중입니다. 예를 들어 문자열이 '09 / 02 / 2009 '인 액션에 대한 호출을 제출하면 DateTime이 '02 / 09 / 2009 00:00:00'이거나 로컬 설정에서 9 월 2 일이됩니다.

날짜 형식을 위해 자체 모델 바인더를 롤링하고 싶지 않습니다. 그러나 MVC가 나를 위해이 작업을 수행 할 수 있으면 문자열을 수락하고 DateTime.Parse를 사용하도록 작업을 변경해야 할 필요가없는 것 같습니다.

DateTime의 기본 모델 바인더에 사용되는 날짜 형식을 변경하는 방법이 있습니까? 기본 모델 바인더가 현지화 설정을 사용해서는 안됩니까?


방금 더 철저한 인터넷 검색으로 이에 대한 답을 찾았습니다.

Melvyn Harbor는 MVC가 날짜와 작동하는 방식과 필요한 경우이를 무시할 수있는 방법에 대한 철저한 설명을 제공합니다.

http://weblogs.asp.net/melvynharbour/archive/2008/11/21/mvc-modelbinder-and-localization.aspx

구문 분석 할 값을 찾을 때 프레임 워크는 특정 순서 즉, 다음을 찾습니다.

  1. RouteData (위에 표시되지 않음)
  2. URI 쿼리 문자열
  3. 요청 양식

그러나 이들 중 마지막 만이 문화를 인식 할 것입니다. 현지화 관점에서 볼 때 매우 좋은 이유가 있습니다. 온라인으로 발행 한 항공사 항공편 정보를 보여주는 웹 응용 프로그램을 작성했다고 가정하십시오. 해당 날짜의 링크 (예 : http://www.melsflighttimes.com/Flights/2008-11-21 ) 를 클릭하여 특정 날짜의 항공편을 찾은 다음 해당 동료에게 해당 링크를 이메일로 보내려고합니다. 미국. InvariantCulture를 사용하는 경우 동일한 데이터 페이지를 모두 볼 수있는 유일한 방법입니다. 반대로, 항공편을 예약하기 위해 양식을 사용하는 경우 모든 과정이 타이트한 주기로 진행됩니다. 데이터는 양식에 기록 될 때 CurrentCulture를 존중할 수 있으므로 양식에서 돌아올 때이를 존중해야합니다.


전 세계적으로 문화를 설정하겠습니다. ModelBinder가 그것을 픽업!

  <system.web>
    <globalization uiCulture="en-AU" culture="en-AU" />

또는이 페이지에 대해 이것을 변경하십시오.
그러나 전 세계적으로 web.config에서는 더 나은 것으로 생각합니다.


DateTime 모델 속성에 짧은 날짜 형식 바인딩과 동일한 문제가 있습니다. 많은 다른 예제 (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;
            }
        }
    }
}

Global ASAX 파일에서 경로 등이 등록되는 방식을 유지하기 위해 CustomModelBinderConfig라는 MVC4 프로젝트의 App_Start 폴더에 새로운 sytatic 클래스를 추가했습니다.

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

그런 다음 Global ASASX Application_Start에서 정적 RegisterCustomModelBinders를 다음과 같이 호출합니다.

protected void Application_Start()
{
    /* bla blah bla the usual stuff and then */

    CustomModelBindersConfig.RegisterCustomModelBinders();
}

여기서 중요한 점은 DateTime 값을 숨겨진 필드에 다음과 같이 쓰는 경우입니다.

@Html.HiddenFor(model => model.SomeDate) // a DateTime property
@Html.Hiddenfor(model => model) // a model that is of type DateTime

I did that and the actual value on the page was in the format "MM/dd/yyyy hh:mm:ss tt" instead of "dd/MM/yyyy hh:mm:ss tt" like I wanted. This caused my model validation to either fail or return the wrong date (obviously swapping the day and month values around).

After a lot of head scratching and failed attempts the solution was to set the culture info for every request by doing this in the 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;
}

It won't work if you stick it in Application_Start or even Session_Start since that assigns it to the current thread for the session. As you well know, web applications are stateless so the thread that serviced your request previously is ot the same thread serviceing your current request hence your culture info has gone to the great GC in the digital sky.

Thanks go to: 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


It going to be slightly different in MVC 3.

Suppose we have a controller and a view with Get method

public ActionResult DoSomething(DateTime dateTime)
{
    return View();
}

We should add 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
}

and the command in Application_Start() of Global.asax

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

It is also worth noting that even without creating your own model binder multiple different formats may be parsable.

For instance in the US all the following strings are equivalent and automatically get bound to the same DateTime value:

/company/press/may%2001%202008

/company/press/2008-05-01

/company/press/05-01-2008

I'd strongly suggest using yyyy-mm-dd because its a lot more portable. You really dont want to deal with handling multiple localized formats. If someone books a flight on 1st May instead of 5th January you're going to have big issues!

NB: I'm not clear exaclty if yyyy-mm-dd is universally parsed in all cultures so maybe someone who knows can add a comment.


I set the below config on my MVC4 and it works like a charm

<globalization uiCulture="auto" culture="auto" />

Try to use toISOString(). It returns string in ISO8601 format.

GET method

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 method

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;
}

I set CurrentCulture and CurrentUICulture my custom base controller

    protected override void Initialize(RequestContext requestContext)
    {
        base.Initialize(requestContext);

        Thread.CurrentThread.CurrentCulture = CultureInfo.GetCultureInfo("en-GB");
        Thread.CurrentThread.CurrentUICulture = CultureInfo.GetCultureInfo("en-GB");
    }

참고URL : https://stackoverflow.com/questions/528545/mvc-datetime-binding-with-incorrect-date-format

반응형