locked
Route Parameter Validation with Date Format RRS feed

  • Question

  • User-424537423 posted

    I am getting the validation failure message (status code 400) for all inputs, when i change the parameter type from DateTime to string, then the regex validation works but DateType validation not working. It accepts 2019-02-31 as a valid input. Any idea how to make it work DateTime parameter type?

    [HttpGet("{date}")]
        public ActionResult<string> Get( [RegularExpression(@"^[0-9]{4}-[0-9]{2}-[0-9]{2}$"), DataType(DataType.Date)] DateTime date)
        {
             return Ok();
        }
    Monday, July 22, 2019 6:28 PM

Answers

  • User711641945 posted

    Hi senthile,

    You need to custom a DateTimeModelBinder to enforce the customer to pass the date in "yyyy-MM-dd".Here is a working demo as follow: 

    1.DateTimeModelBinder.cs:

    public class DateTimeModelBinder : IModelBinder
        {
            public Task BindModelAsync(ModelBindingContext bindingContext)
    
            {
                if (bindingContext == null)
                    throw new ArgumentNullException(nameof(bindingContext));
    
                // Try to fetch the value of the argument by name
                var modelName = bindingContext.ModelName;
                var valueProviderResult = bindingContext.ValueProvider.GetValue(modelName);
                if (valueProviderResult == ValueProviderResult.None)
                    return Task.CompletedTask;
    
                bindingContext.ModelState.SetModelValue(modelName, valueProviderResult);
                var dateStr = valueProviderResult.FirstValue;
    
                // Here you define your custom parsing logic
                if (!DateTime.TryParseExact(dateStr, "yyyy-MM-dd", null, DateTimeStyles.None, out DateTime date))
                {
                    bindingContext.ModelState.TryAddModelError(bindingContext.ModelName, "DateTime should be in format 'yyyy-mm-dd' or it is invalid");
                    return Task.CompletedTask;
                }
                bindingContext.Result = ModelBindingResult.Success(date);
                return Task.CompletedTask;
    
            }
        }

    2.Controller(You could use [ModelBinder] to specify the action):

     [HttpGet("{date}")] 
            public ActionResult<string> Get([ModelBinder(BinderType =typeof(DateTimeModelBinder))]DateTime date)
            {
                if (!ModelState.IsValid)
                {
                    return BadRequest(ModelState);
                }
                return Ok();
            }

    Best Regards,

    Rena

    • Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
    Wednesday, July 24, 2019 9:44 AM

All replies

  • User711641945 posted

    Hi senthile,

    Could you share why do you use this RegularExpression?

    [RegularExpression] attribute is used with string types. When you have a DateTime property the default model binder will use the current culture setting to parse the request value into a DateTime and it will use the yyyy-MM-dd format . So as you can see it's the default model binder responsible for converting the user HTTP request into an instance of the DateTime field and the RegularExpression validator doesn't come into play.

    Actually I suggest that using [DataType(DataType.Date)] is enough.

    Best Regards,

    Rena

    Tuesday, July 23, 2019 3:59 AM
  • User-424537423 posted

    Thank you for the providing the explanation using regex.    It coverts date input "201-01-01" to default date as "0001-01-01.

    I want to enforce the consumer to pass the date in "yyyy-MM-dd" format and that the reason used the regex.

    is the any way to achieve this?

    Tuesday, July 23, 2019 12:11 PM
  • User475983607 posted

    senthile

    Thank you for the providing the explanation using regex.    It coverts date input "201-01-01" to default date as "0001-01-01.

    I want to enforce the consumer to pass the date in "yyyy-MM-dd" format and that the reason used the regex.

    is the any way to achieve this?

    Use an HTTP POST and standard model validation.  Model validation will generate the code needed in the browser to validate the user's input.  

    https://docs.microsoft.com/en-us/aspnet/core/mvc/models/validation?view=aspnetcore-2.2

    Model binding will validate the type.

    https://docs.microsoft.com/en-us/aspnet/core/mvc/models/model-binding?view=aspnetcore-2.2

    Use standard route constraint if you still wish to use route parameters and are NOT validating user input.  Keep in mind an invalid format will result in a 404 not a 400.

    https://docs.microsoft.com/en-us/aspnet/core/fundamentals/routing?view=aspnetcore-2.2

    Tuesday, July 23, 2019 2:18 PM
  • User711641945 posted

    Hi senthile,

    You need to custom a DateTimeModelBinder to enforce the customer to pass the date in "yyyy-MM-dd".Here is a working demo as follow: 

    1.DateTimeModelBinder.cs:

    public class DateTimeModelBinder : IModelBinder
        {
            public Task BindModelAsync(ModelBindingContext bindingContext)
    
            {
                if (bindingContext == null)
                    throw new ArgumentNullException(nameof(bindingContext));
    
                // Try to fetch the value of the argument by name
                var modelName = bindingContext.ModelName;
                var valueProviderResult = bindingContext.ValueProvider.GetValue(modelName);
                if (valueProviderResult == ValueProviderResult.None)
                    return Task.CompletedTask;
    
                bindingContext.ModelState.SetModelValue(modelName, valueProviderResult);
                var dateStr = valueProviderResult.FirstValue;
    
                // Here you define your custom parsing logic
                if (!DateTime.TryParseExact(dateStr, "yyyy-MM-dd", null, DateTimeStyles.None, out DateTime date))
                {
                    bindingContext.ModelState.TryAddModelError(bindingContext.ModelName, "DateTime should be in format 'yyyy-mm-dd' or it is invalid");
                    return Task.CompletedTask;
                }
                bindingContext.Result = ModelBindingResult.Success(date);
                return Task.CompletedTask;
    
            }
        }

    2.Controller(You could use [ModelBinder] to specify the action):

     [HttpGet("{date}")] 
            public ActionResult<string> Get([ModelBinder(BinderType =typeof(DateTimeModelBinder))]DateTime date)
            {
                if (!ModelState.IsValid)
                {
                    return BadRequest(ModelState);
                }
                return Ok();
            }

    Best Regards,

    Rena

    • Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
    Wednesday, July 24, 2019 9:44 AM
  • User753101303 posted

    Hi,

    Seems surprising. You are sure you are not ending up in trying to insert an empty string into your db which usually gives the lowest date. My first thought would be rather to add  maybe a db level check constraints to restrict the date range for those columns...

    Edit: don't have Core handy but it works as expected for MVC 5 ie I do have a year 201 date in this case (and so for this  I would use a range check rather than a regular expression validation).

    Wednesday, July 24, 2019 9:57 AM