I have this url
http://example.com/api/record/getall?startdate=1994-11-05T17:15:30Z
and this webapi endpoint
[ActionName("GetAll")] public object GetAll(DateTime startDate) { ... }
The problem I face is that the startDate received the deserialized string as a local time, "11/5/1994 9:15:30 AM" instead of stay in UTC time which what I wanted "11/5/1994 5:15:30 PM".
I'm using VS2012 update2, latest Json.net nuget package. However, if I use json.net in a separate console app to test, the same string "1994-11-05T17:15:30Z" is able to deserialize correctly into "11/5/1994 5:15:30 PM".
Anyone know what is wrong here?
Although you have already found a solution for your question, I thought I would take a shot at explaining why it did not work as you expected.
WebApi uses content type negotiation to determine what parser to use when reading data. That means it will look at the Content-Type
header of the request to make the determination. If the Content-Type
header is set to application/json
then it will use Json.Net to parse to content and feed it to your method.
An HTTP GET request, such as the one you are making here, does not have a content type set. The "content" in this case is really just the query string from the URL. WebApi does not expect to find JSON data here, so it is not going to try to use a JSON parser to make sense of it. Even if it did, the string you are passing to your GetAll method isn't even valid JSON. (It would need to be quoted to be valid.)
Now, if you were to change your method to accept a POST request, and you set the content type header to application/json
and passed the date as a JSON string in the body, then WebApi will use Json.Net to parse it, and it will work like you expect.
For example, say your method looked like this:
[HttpPost] public object GetAll([FromBody]DateTime startDate) { try { return new { StartDate = startDate.ToString("yyyy-MM-dd HH:mm:ss"), StartDateKind = startDate.Kind.ToString(), }; } catch (Exception ex) { return ex.Message; } }
And you made a request like this (note the POST):
POST http://localhost:57524/api/values/GetAll HTTP/1.1 Content-Type: application/json Content-Length: 22 Host: localhost:57524 "1994-11-05T17:15:30Z"
The response would look like this:
HTTP/1.1 200 OK Cache-Control: no-cache Pragma: no-cache Content-Type: application/json; charset=utf-8 Expires: -1 Server: Microsoft-IIS/8.0 X-AspNet-Version: 4.0.30319 X-Powered-By: ASP.NET Date: Fri, 31 May 2013 01:25:48 GMT Content-Length: 57 {"StartDate":"1994-11-05 17:15:30","StartDateKind":"Utc"}
As you can see, it does correctly recognize the date to be UTC in this scenario.
If you want to modify the way Asp WebApi parses uri parameters of your GET requests, you can write custom IModelBinder
like this:
public class UtcDateTimeModelBinder : IModelBinder { public bool BindModel(HttpActionContext actionContext, ModelBindingContext bindingContext) { var stringValue = bindingContext.ValueProvider.GetValue(bindingContext.ModelName)?.RawValue as string; DateTime parsedDate; if (!DateTime.TryParse(stringValue, null, DateTimeStyles.AdjustToUniversal, out parsedDate)) { return false; } bindingContext.Model = parsedDate; return true; } }
And then override default binder:
GlobalConfiguration.Configure(configuration => configuration.BindParameter(typeof(DateTime), new UtcDateTimeModelBinder()););
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