Question: Web API - How to receive in controller DateTime ('dd/MM/yyyy') as Url parameter?

Question

Web API - How to receive in controller DateTime ('dd/MM/yyyy') as Url parameter?

Answers 1
Added at 2017-09-06 21:09
Tags
Question

Everytime my controller receives a date as dd/MM/yyyy it decode as MM/dd/yyyy. Is it possible to tell the controller how to decode the parameter of the url?

My method in controller:

[HttpGet]
public JsonResult<IList<Callers>> GetListOfCallers(DateTime startDate, DateTime endDate)
        {
            // myCode....
        }

My javascript:

var $startDate = $('#startDate').val();
var $endDate = $('#endDate').val();

$.get(rootUrl + "api/report/GetListOfCallers?startDate=" + $startDate + "&endDate=" + $endDate, function (data) {
                // myCode....
            });

I know I can receive the date in controller as string and then parse it, or change it in my javascript to ISO8601 before putting in the url, but I want to know if I can tell my controller how to decode the parameter received.

EDIT: I was using MVC controller and this was not a problem, it started decoding incorrectly after I changed to ApiController, so the code was working and I hope to keep as it is.

Answers to

Web API - How to receive in controller DateTime (&#39;dd/MM/yyyy&#39;) as Url parameter?

nr: #1 dodano: 2017-09-08 21:09

I manage to solve my problem using Model binding as suggested by @Jakotheshadows and @Amy.

I used the code from this answer about ModelBinders in Web Api with a few tweaks from this answer (it's in portuguese, but the code is clear).

So my code right now:

using System;
using System.Web.Http.Controllers;
using System.Web.Http.ModelBinding;

namespace Site.Services
{
    public class DateTimeModelBinder : IModelBinder
    {
        public bool BindModel(HttpActionContext actionContext, ModelBindingContext bindingContext)
        {
            ValidateBindingContext(bindingContext);

            if (!bindingContext.ValueProvider.ContainsPrefix(bindingContext.ModelName) ||
                !CanBindType(bindingContext.ModelType))
            {
                return false;
            }

            var modelName = bindingContext.ModelName;
            var attemptedValue = bindingContext.ValueProvider
                .GetValue(modelName).AttemptedValue;

            try
            {
                bindingContext.Model = DateTime.Parse(attemptedValue);
            }
            catch (FormatException e)
            {
                bindingContext.ModelState.AddModelError(modelName, e);
            }

            return true;
        }

        private static void ValidateBindingContext(ModelBindingContext bindingContext)
        {
            if (bindingContext == null)
            {
                throw new ArgumentNullException("bindingContext");
            }

            if (bindingContext.ModelMetadata == null)
            {
                throw new ArgumentException("ModelMetadata cannot be null", "bindingContext");
            }
        }

        public static bool CanBindType(Type modelType)
        {
            return modelType == typeof(DateTime) || modelType == typeof(DateTime?);
        }
    }
}

I used try and DateTime.Parse as suggested in the second link, because the first always throwed an exception even with try and catch.

The ModelBinderProvider I used as he suggested:

using System;
using System.Web.Http;
using System.Web.Http.ModelBinding;

namespace Site.Services
{
    public class DateTimeModelBinderProvider : ModelBinderProvider
    {
        readonly DateTimeModelBinder binder = new DateTimeModelBinder();

        public override IModelBinder GetBinder(HttpConfiguration configuration, Type modelType)
        {
            if (DateTimeModelBinder.CanBindType(modelType))
            {
                return binder;
            }

            return null;
        }
    }
}

And I configure as suggested here (also an answer for the first link), but in my WebApiConfig.cs (didn't work in Global.asax), like this:

using Site.Services;
using System;
using System.Web.Http;

namespace Site
{
    public static class WebApiConfig
    {
        public static void Register(HttpConfiguration config)
        {
            config.BindParameter(typeof(DateTime), new DateTimeModelBinder());
            config.BindParameter(typeof(DateTime?), new DateTimeModelBinder());

            //Rest of my code
            config.MapHttpAttributeRoutes();

            config.Routes.MapHttpRoute(
                name: "DefaultApi",
                routeTemplate: "api/{controller}/{action}/{id}",
                defaults: new { id = RouteParameter.Optional }
            );
        }
    }
}

I think the globalization of the Web.config, the uiCulture and culture must be set to the culture you want and enableClientBasedCulture be set as true as suggest here, but I'm not sure because I didn't want to change the code to test it.

Source Show
◀ Wstecz