Reputation: 7547
I am using Asp.Net MVC 4 and this is my custom model binder:
public class DateTimeModelBinder : DefaultModelBinder
{
private string _customFormat;
public DateTimeModelBinder(string customFormat)
{
_customFormat = customFormat;
}
public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
var value = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);
if(value != null)
return DateTime.ParseExact(value.AttemptedValue, _customFormat, CultureInfo.InvariantCulture);
return null;
}
}
and this is my Web API method:
[HttpPost]
public HttpResponseMessage Register(RegistrationUser registrationUser)
{
if (ModelState.IsValid)
{
return new HttpResponseMessage(HttpStatusCode.OK) { Content = new StringContent("User is created") };
}
else
{
return new HttpResponseMessage(HttpStatusCode.BadRequest)
{
Content =
new ObjectContent<IEnumerable<string>>(ModelState.Values.SelectMany(f => f.Errors.Select(s => s.ErrorMessage)),
new JsonMediaTypeFormatter())
};
}
}
registrationUser has BirthDate
of type DateTime?
. When I submit a valid value 23/05/2014
it won't accept and model binder is never executed.
I have this in global.asax:
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
ModelBinders.Binders.Add(typeof(DateTime), new DateTimeModelBinder("dd/mm/yyyy"));
ModelBinders.Binders.Add(typeof(DateTime?), new DateTimeModelBinder("dd/mm/yyyy"));
WebApiConfig.Register(GlobalConfiguration.Configuration);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
AuthConfig.RegisterAuth();
}
This is my RegistrationUser POCO:
public class RegistrationUser
{
public int UserID { get; set; }
[Required(ErrorMessage = "Email address is required")]
[EmailAddress(ErrorMessage = "Invalid Email Address")]
public string EmailAddress { get; set; }
[Required(ErrorMessage = "First Name is required")]
public string FirstName { get; set; }
[Required(ErrorMessage = "Last Name is required")]
public string LastName { get; set; }
[Required(ErrorMessage = "Password is required")]
public string Password { get; set; }
[NullableRequired(ErrorMessage = "Gender is required")]
public short? GenderID { get; set; }
[NullableRequired(ErrorMessage = "Birth Date is required")]
public DateTime? BirthDate { get; set; }
[NullableRequired(ErrorMessage = "Profile For is required")]
public short? ProfileForID { get; set; }
}
What am I missing?
PS: If I add an attribute
[ModelBinder(typeof(DateTimeModelBinder))]
public class RegistrationUser
it would work fine but this is tedious and time consuming plus error prone. I don't want to add this to each model. I want that DateTimeModelBinder
should be used always for DateTime
and DateTime?
Update:
Looks like I needed to use JSON.Net's converter feature:
public class DateTimeConverter : DateTimeConverterBase
{
private string _dateFormat;
public DateTimeConverter(string dateFormat)
{
_dateFormat = dateFormat;
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
try
{
if (reader.Value != null && reader.Value.ToString().Trim() != string.Empty)
return DateTime.ParseExact(reader.Value.ToString(), _dateFormat, CultureInfo.InvariantCulture);
}
catch
{
}
return null;
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
writer.WriteValue(((DateTime)value).ToString(_dateFormat));
}
}
and then I had to add this in WebApiConfig:
config.Formatters.JsonFormatter.SerializerSettings.Converters.Add(
new DateTimeConverter("dd/mm/yyyy"));
and it works!
Upvotes: 1
Views: 2412
Reputation: 25704
It looks like you are writing an MVC model binder and trying to use it in a Web API controller. The two systems are similar, but not compatible with each other in terms of the types being used.
The types in the System.Web.Mvc
namespace are all MVC, whereas the types in the System.Web.Http
namespaces are all Web API.
The registration of MVC and Web API model binders is also similar in some ways, and different in others. For example, you can use the [ModelBinder(typeof(XyzModelBinder))]
attribute on the type in question to declare a model binder in both systems. But registration of a global model binder is different.
In MVC you register a global model binder like so:
ModelBinders.Binders.Add(typeof(Xyz), new XyzModelBinder(...));
In Web API it is like so:
GlobalConfiguration.Configuration.Services.Add(typeof(ModelBinderProvider), new XyzModelBinderProvider());
With regard to MVC vs. Web API, please check that you're not mixing up those types - many types have the same or similar name except that they're in different namespaces.
Upvotes: 4