So I have a WebAPI 2 controller written in C# that takes among other things a query parameter of type DateTime. This is an API that returns all values from the data store based on a date filter. Something like, let's say:
public MyThing GetThing([FromUri]DateTime startTime)
{
// filter and return some results
}
I am running into 2 problems:
In short, I want to figure out how to make sure that
WebAPI 2 does provide wonderful hooks for formatting JSON, which I do make use of, so simply returning a DateTime in a JSON body formats it as desired using the ISO8601 format, and as well it is correctly understood in a [FromBody] JSON body. I can't find ways for pulling strings around URI handling though, and I would really like to!
You can use modelbinder to transforming incoming data to your model.
GetThings([ModelBinder(typeof(UtcDateTimeModelBinder)), FromUri] DateTime dt){//do somthing}
public class UtcDateTimeModelBinder : IModelBinder
{
public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
if (bindingContext == null)
{
throw new ArgumentNullException(nameof(bindingContext));
}
if (bindingContext.ModelMetadata.ModelType == typeof(DateTime))
{
var valueProviderResult = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);
var str = valueProviderResult.AttemptedValue;
return DateTime.Parse(str).ToUniversalTime();
}
return null;
}
In that way you can set it as default modelbilder of DateTime.
ModelBinders.Binders.Add(typeof(DateTime), new UtcDateTimeModelBinder());
Why not using DateTimeOffset instead of DateTime if you want to keep UTC offset? Here are some code working with JSON serialization:
Api Controller:
public class ValuesController : ApiController
{
public object Get(DateTimeOffset dt)
{
return new {
Input = dt,
Local = dt.LocalDateTime,
Utc = dt.UtcDateTime
};
}
}
Razor View sample code (assuming you have the default api route created in a MVC + Web API Visual studio template )
<a href="@Url.RouteUrl("DefaultApi",new {httproute = "",controller = "values",dt = DateTimeOffset.UtcNow})">Utc Now</a>
Rendered as:
<a href="/api/values?dt=06%2F26%2F2018%2009%3A37%3A24%20%2B00%3A00">Utc Now</a>
And you can call your API with datetime offset:
2018-06-26T08:25:48Z: http://localhost:1353/api/values?dt=2018-06-26T08:25:48Z
{"Input":"2018-06-26T08:25:48+00:00","Local":"2018-06-26T10:25:48+02:00","Utc":"2018-06-26T08:25:48Z"}
2018-06-26T08:25:48+01:00: http://localhost:1353/api/values?dt=2018-06-26T08%3A25%3A48%2B01%3A00 (note that : and + must be url encoded)
{"Input":"2018-06-26T08:25:48+01:00","Local":"2018-06-26T09:25:48+02:00","Utc":"2018-06-26T07:25:48Z"}
1.
You should check the timezone of your parameter 'startTime' (which should be the timezone of your server/computer).
The DateTime provided by Web API is correct, it just depends on YOUR timezone.
2.
Create a Json DateTime serializer in order to generate ISO8601 formatted date.
The query string parameter value you are sending is UTC time. So, the same gets translated to a time based on your local clock and if you call
ToUniversalTime()
, it gets converted back to UTC.So, what exactly is the question? If the question is why is this happening if sent in as query string but not when posted in request body, the answer to that question is that ASP.NET Web API binds the URI path, query string, etc using model binding and the body using parameter binding. For latter, it uses a media formatter. If you send JSON, the JSON media formatter is used and it is based on JSON.NET.
Since you have specified DateTimeZoneHandling.Utc, it uses that setting and you get the date time kind you want. BTW, if you change this setting to DateTimeZoneHandling.Local, then you will see the same behavior as model binding.
Meaning in order to get the desired formatting you want, all you need to do is call the ToUniversalTime()
method.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With