Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

MVC Model Binding for DateTime is different using GET or POST

I encountered following unwanted behaviour while using "Remote" validation attribute for a certain DateTime Model property.

Server-side, my Application Culture is defined as described below:

protected void Application_PreRequestHandlerExecute()
{
    if (!(Context.Handler is IRequiresSessionState)){ return; }
    Thread.CurrentThread.CurrentCulture = new CultureInfo("nl-BE");
    Thread.CurrentThread.CurrentUICulture = new CultureInfo("nl-BE");
}

Client-side, my Application Culture is defined as described below:

Globalize.culture("nl-BE");

Case 1:

  • Model Property

    [Remote("IsDateValid", "Home")]
    public DateTime? MyDate { get; set; }
    
  • Controller Action

    public JsonResult IsDateValid(DateTime? MyDate)
    {
        // some validation code here
        return Json(true, JsonRequestBehavior.AllowGet);
    }
    
  • While debugging the IsDateValid method, a date entered in the UI as 05/10/2013 (October 5th 2013) is incorrectly interpreted as 10/05/2013 (May 10th, 2013)

Case 2:

  • Model Property

    [Remote("IsDateValid", "Home", HttpMethod = "POST")]
    public DateTime? MyDate { get; set; }
    
  • Controller Action

    [HttpPost]
    public JsonResult IsDateValid(DateTime? MyDate)
    {
        // some validation code here
        return Json(true);
    }
    
  • While debugging the IsDateValid method, a date entered in the UI as 05/10/2013 (October 5th 2013) is correctly interpreted as 05/10/2013 (October 5th 2013)

Am I missing some configuration for making the "standard" GET remote validation work as desired?

like image 714
StevenQ Avatar asked Oct 10 '13 09:10

StevenQ


People also ask

How does model binding work in MVC?

How Model Binding Works. Model binding is a simplistic way to correlate C# code with an HTTP request. The model binding applies to transforming the HTTP request data in the query's form string and form collection of the action method parameters. We can consider these parameters to be primitive type or complex type.

Is MVC two way binding?

So the two-way data binding means we can perform both read and write operations.


1 Answers

When binding data for GET, InvariantCulture is used(which is "en-US"), whereas for POST Thread.CurrentThread.CurrentCulture is. The reason behind is that GET urls may be shared by users and hence should be invariant. Whereas POST is never shared and it is safe to use server's culture for binding there.

If you are sure your application does not need the option of sharing urls between people coming from different countries, you are safe to create your own ModelBinder that will force to use server locale even for GET requests.

Here is the sample how it may look like in Global.asax.cs:

protected void Application_Start()
{
    /*some code*/

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

/// <summary>
/// Allows to pass date using get using current server's culture instead of invariant culture.
/// </summary>
public class DateTimeModelBinder : IModelBinder
{
    public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
        var valueProviderResult = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);
        var date = valueProviderResult.AttemptedValue;

        if (String.IsNullOrEmpty(date))
        {
            return null;
        }

        bindingContext.ModelState.SetModelValue(bindingContext.ModelName, valueProviderResult);

        try
        {
            // Parse DateTimeusing current culture.
            return DateTime.Parse(date);
        }
        catch (Exception)
        {
            bindingContext.ModelState.AddModelError(bindingContext.ModelName, String.Format("\"{0}\" is invalid.", bindingContext.ModelName));
            return null;
        }
    }
}
like image 80
Li0liQ Avatar answered Nov 14 '22 23:11

Li0liQ