Question

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?

Was it helpful?

Solution

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;
        }
    }
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top