When using [DataType(DataType.Date)] on my model - if it is populated with a value, and I let the "modern" browser show it with its built in calendar, it is never populated with the date.
public class StockPurchase
{
public int StockPurchaseId { get; set; }
[Required]
[Display(Name = "Date of purchase")]
[DataType(DataType.Date)]
public DateTime Date { get; set; }
[Required]
public decimal Amount { get; set; }
}
Is this a known problem (I'm using Chrome).
Thanks, Mark
It's not a "known problem" per se, just an issue with how the HTML5 date control works. Date inputs are expected in ISO 8601 format, which for a date would be YYYY-MM-DD. The format you see, dd/mm/yyyy is localized to make it easier for the user to input the date, but once selected, the browser sets the value in ISO 8601.
The problem is that ASP.NET MVC doesn't set the value in ISO 8601, but rather in the system localized version, which the browser then doesn't understand. The solution is force MVC to set the right value by adding the following attribute to your date property:
[DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)]
The value for the field will then be set at render with the correct ISO 8601 formatted date, and the browser will understand it.
Note: this is yet another reason to use view models instead of your entity directly in your views. More likely than not, you wouldn't actually want your purchase date to always be displayed as YYYY-MM-DD. If you use a create/edit-specific view model for this, you can set the above attribute on the property there, and then a more appropriate version on your actual entity's property, or on the view model specific to displaying the already entered information to the user.
How to pass UTC dates to Web API?
Passing 2010-01-01 works fine, but when I pass a UTC date such as 2014-12-31T22:00:00.000Z (with a time component), I get a HTTP 404 response. So
http://domain/api/controller/action/2012-12-31T22:00:00.000Z
yields a 404 error response, while
http://domain/api/controller/action/2012-12-31
works fine.
How to pass UTC dates to Web API then - or at least specify date and time?
The problem is twofold:
1. The . in the route
By default, IIS treats all URI's with a dot in them as static resource, tries to return it and skip further processing (by Web API) altogether. This is configured in your Web.config in the section system.webServer.handlers: the default handler handles path="*.". You won't find much documentation regarding the strange syntax in this path attribute (regex would have made more sense), but what this apparently means is "anything that doesn't contain a dot" (and any character from point 2 below). Hence the 'Extensionless' in the name ExtensionlessUrlHandler-Integrated-4.0.
Multiple solutions are possible, in my opinion in the order of 'correctness':
Add a new handler specifically for the routes that must allow a dot. Be sure to add it before the default. To do this, make sure you remove the default handler first, and add it back after yours.
Change the path="*." attribute to path="*". It will then catch everything. Note that from then on, your web api will no longer interpret incoming calls with dots as static resources! If you are hosting static resources on your web api, this is therefor not advised!
Add the following to your Web.config to unconditionally handle all requests: under <system.webserver>: <modules runAllManagedModulesForAllRequests="true">
2. The : in the route
After you've changed the above, by default, you'd get the following error:
A potentially dangerous Request.Path value was detected from the client (:).
You can change the predefined disallowed/invalid characters in your Web.config. Under <system.web>, add the following: <httpRuntime requestPathInvalidCharacters="<,>,%,&,*,\,?" />. I've removed the : from the standard list of invalid characters.
Easier/safer solutions
Although not an answer to your question, a safer and easier solution would be to change the request so that all this is not required. This can be done in two ways:
Pass the date as a query string parameter, like ?date=2012-12-31T22:00:00.000Z.
Strip the .000 from every request, and encode the url, so replace all :'s with %3A, e.g. by using HttpUtility.UrlEncode().
in your Product Web API controller:
[RoutePrefix("api/product")]
public class ProductController : ApiController
{
private readonly IProductRepository _repository;
public ProductController(IProductRepository repository)
{
this._repository = repository;
}
[HttpGet, Route("orders")]
public async Task<IHttpActionResult> GetProductPeriodOrders(string productCode, DateTime dateStart, DateTime dateEnd)
{
try
{
IList<Order> orders = await _repository.GetPeriodOrdersAsync(productCode, dateStart.ToUniversalTime(), dateEnd.ToUniversalTime());
return Ok(orders);
}
catch(Exception ex)
{
return NotFound();
}
}
}
test GetProductPeriodOrders method in Fiddler - Composer:
http://localhost:46017/api/product/orders?productCode=100&dateStart=2016-12-01T00:00:00&dateEnd=2016-12-31T23:59:59
DateTime format:
yyyy-MM-ddTHH:mm:ss
javascript pass parameter use moment.js
const dateStart = moment(startDate).format('YYYY-MM-DDTHH:mm:ss');
const dateEnd = moment(endDate).format('YYYY-MM-DDTHH:mm:ss');
I feel your pain ... yet another date time format... just what you needed!
Using Web Api 2 you can use route attributes to specify parameters.
so with attributes on your class and your method you can code up a REST URL using this utc format you are having trouble with (apparently its ISO8601, presumably arrived at using startDate.toISOString())
[Route(#"daterange/{startDate:regex(^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}.\d{3}Z$)}/{endDate:regex(^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}.\d{3}Z$)}")]
[HttpGet]
public IEnumerable<MyRecordType> GetByDateRange(DateTime startDate, DateTime endDate)
.... BUT, although this works with one date (startDate), for some reason it doesnt work when the endDate is in this format ... debugged for hours, only clue is exception says it doesnt like colon ":" (even though web.config is set with :
<system.web>
<compilation debug="true" targetFramework="4.5.1" />
<httpRuntime targetFramework="4.5.1" requestPathInvalidCharacters="" />
</system.web>
So, lets make another date format (taken from the polyfill for the ISO date format) and add it to the Javascript date (for brevity, only convert up to minutes):
if (!Date.prototype.toUTCDateTimeDigits) {
(function () {
function pad(number) {
if (number < 10) {
return '0' + number;
}
return number;
}
Date.prototype.toUTCDateTimeDigits = function () {
return this.getUTCFullYear() +
pad(this.getUTCMonth() + 1) +
pad(this.getUTCDate()) +
'T' +
pad(this.getUTCHours()) +
pad(this.getUTCMinutes()) +
'Z';
};
}());
}
Then when you send the dates to the Web API 2 method, you can convert them from string to date:
[RoutePrefix("api/myrecordtype")]
public class MyRecordTypeController : ApiController
{
[Route(#"daterange/{startDateString}/{endDateString}")]
[HttpGet]
public IEnumerable<MyRecordType> GetByDateRange([FromUri]string startDateString, [FromUri]string endDateString)
{
var startDate = BuildDateTimeFromYAFormat(startDateString);
var endDate = BuildDateTimeFromYAFormat(endDateString);
...
}
/// <summary>
/// Convert a UTC Date String of format yyyyMMddThhmmZ into a Local Date
/// </summary>
/// <param name="dateString"></param>
/// <returns></returns>
private DateTime BuildDateTimeFromYAFormat(string dateString)
{
Regex r = new Regex(#"^\d{4}\d{2}\d{2}T\d{2}\d{2}Z$");
if (!r.IsMatch(dateString))
{
throw new FormatException(
string.Format("{0} is not the correct format. Should be yyyyMMddThhmmZ", dateString));
}
DateTime dt = DateTime.ParseExact(dateString, "yyyyMMddThhmmZ", CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal);
return dt;
}
so the url would be
http://domain/api/myrecordtype/daterange/20140302T0003Z/20140302T1603Z
Hanselman gives some related info here:
http://www.hanselman.com/blog/OnTheNightmareThatIsJSONDatesPlusJSONNETAndASPNETWebAPI.aspx
As a similar alternative to s k's answer, I am able to pass a date formatted by Date.prototype.toISOString() in the query string. This is the standard ISO 8601 format, and it is accepted by .Net Web API controllers without any additional configuration of the route or action.
e.g.
var dateString = dateObject.toISOString(); // "2019-07-01T04:00:00.000Z"
This is a solution and a model for possible solutions. Use Moment.js in your client to format dates, convert to unix time.
$scope.startDate.unix()
Setup your route parameters to be long.
[Route("{startDate:long?}")]
public async Task<object[]> Get(long? startDate)
{
DateTime? sDate = new DateTime();
if (startDate != null)
{
sDate = new DateTime().FromUnixTime(startDate.Value);
}
else
{
sDate = null;
}
... your code here!
}
Create an extension method for Unix time. Unix DateTime Method
It used to be a painful task, but now we can use toUTCString():
Example:
[HttpPost]
public ActionResult Query(DateTime Start, DateTime End)
Put the below into Ajax post request
data: {
Start: new Date().toUTCString(),
End: new Date().toUTCString()
},
As a matter of fact, specifying parameters explicitly as ?date='fulldatetime' worked like a charm. So this will be a solution for now: don't use commas, but use old GET approach.
One possible solution is to use Ticks:
public long Ticks { get; }
Then in the controller's method:
public DateTime(long ticks);
Since I have encoding ISO-8859-1 operating system the date format "dd.MM.yyyy HH:mm:sss" was not recognised what did work was to use InvariantCulture string.
string url = "GetData?DagsPr=" + DagsProfs.ToString(CultureInfo.InvariantCulture)
By looking at your code, I assume you do not have a concern about the 'Time' of the DateTime object. If so, you can pass the date, month and the year as integer parameters. Please see the following code. This is a working example from my current project.
The advantage is; this method helps me to avoid DateTime format issues and culture incompatibilities.
/// <summary>
/// Get Arrivals Report Seven Day Forecast
/// </summary>
/// <param name="day"></param>
/// <param name="month"></param>
/// <param name="year"></param>
/// <returns></returns>
[HttpGet("arrivalreportsevendayforecast/{day:int}/{month:int}/{year:int}")]
public async Task<ActionResult<List<ArrivalsReportSevenDayForecastModel>>> GetArrivalsReportSevenDayForecast(int day, int month, int year)
{
DateTime selectedDate = new DateTime(year, month, day);
IList<ArrivalsReportSevenDayForecastModel> arrivingStudents = await _applicationService.Value.GetArrivalsReportSevenDayForecast(selectedDate);
return Ok(arrivingStudents);
}
If you are keen to see the front-end as well, feel free to read the code below. Unfortunately, that is written in Angular. This is how I normally pass a DateTime as a query parameter in Angular GET requests.
public getArrivalsReportSevenDayForecast(selectedDate1 : Date): Observable<ArrivalsReportSevenDayForecastModel[]> {
const params = new HttpParams();
const day = selectedDate1.getDate();
const month = selectedDate1.getMonth() + 1
const year = selectedDate1.getFullYear();
const data = this.svcHttp.get<ArrivalsReportSevenDayForecastModel[]>(this.routePrefix +
`/arrivalreportsevendayforecast/${day}/${month}/${year}`, { params: params }).pipe(
map<ArrivalsReportSevenDayForecastModel[], ArrivalsReportSevenDayForecastModel[]>(arrivingList => {
// do mapping here if needed
return arrivingList;
}),
catchError((err) => this.svcError.handleError(err)));
return data;
}
Passing the date as a string and then parsing it worked for me. Probably want to add try catch on the parse, but this is the basic code.
[HttpGet("name={name}/date={date}", Name = "GetByNameAndDate")]
public IActionResult GetByNameAndDate(string name, string date) {
DateTimeOffset dto = DateTimeOffset.Parse(date);
}
Then the request can look like this
https://localhost/api/Contoller/name=test/date=2022-02-18T13:45:37.000Z
For external APIs (where you do not know what type of client will call your service), Unix Time should be used both on the input parameters and outputted date fields.
https://learn.microsoft.com/en-us/dotnet/api/system.datetimeoffset.tounixtimeseconds?view=net-6.0
.Net provides ToUnixtimeSeconds and FromUnixtimeSeconds to easily convert to DateTime or DateTimeOff
Unix Time should be preferred over ISO formats because it is just a integer and can be passed in the URL string without encoding.
The 'Ticks' property is similar to Unix time but (I believe) should only be use between a .net client and server.
Most well know APIs will use Unix Time, for example see Stripe's API:
https://stripe.com/docs/api
The obvious downsides of using Unix time are:
They are not human readable
They cannot be created by humans - making it difficult to call the API without code
Use binary format.
to send the info in url use dateTimeVar.ToBinary()
it will be something like
http://domain/api/controller/action/637774955400000000
when you reciebe the data will get like Long variable and use the static function of DateTime Class to transform to DateTime type again.
DateTime MyDateTime = DateTime.FromBinary(BinaryDateTime);
Cheers
I need a way to fake DateTime.Parse with Typemock and have it return the same date when called with any parameters.
I have a DB field that stores an encrypted string that is parsed as date when loaded. The class that holds the data has a Load() method where it copies DB data into its properties, decrypts what's encrypted and does some basic validation, such as:
public class BusinessObject{
public DateTime ImportantDate{get;set;}
...
public void Load(DBObject dbSource){
...
ImportantDate = DateTime.Parse(DecryptionService.Decrypt(dbSource.ImportantDate));
}
}
Runtime all works well.
I'm trying to write a unit test using TypeMock to load some fake data into BusinessObject using its Load method. BusinessObject has way too many properties and can not be deserialized from XML, but DBObject can, so I've stored some XMLs that represent valid data.
It all works well until DecryptionService is called to decrypt the data - it doesn't work because my dev machine doesn't have the DB certificates used in the encryption process. I can't get those on my machine just for testing, that would be a security breach.
I added this to my unit test:
Isolate.Fake.StaticMethods<DecryptionService>(Members.ReturnRecursiveFakes);
Isolate.WhenCalled(() => DecryptionService .Decrypt(null)).WillReturn("***");
Isolate.Fake.StaticMethods<DateTime>(Members.ReturnNulls);
Isolate.WhenCalled(() => DateTime.Parse("***" /*DateStr*/)).WillReturn(DateTime.Now.AddYears(2));
The first part where DecryptionService is faked works, social security and other sensitive strings are "decrypting", but no matter what parameters I give to DateTime I still get one exception or another (ArgumentNullException: String reference not set to an instance of an object if DateStr is null, FormatException when it's "*")
How (if) can I override DateTime.Parse with typemock so that it returns valid DateTime with any invalid paramters passed?
My name is Nofar and i'm from Typemock's support team.
DateTime.Parse is not supported in the WhenCalled API, so in order to fake it's returned value you need to wrap it with a method from your class, for example:
public class BusinessObject
{
public DateTime Load (string s)
{
return DateTime.Parse(s);
}
}
And you test will look like this:
[TestMethod]
public void TestMethodDateTime()
{
BusinessObject b = new BusinessObject();
DateTime now= DateTime.Now.AddYears(2);
Isolate.WhenCalled(()=>b.Load(null)).WillReturn(now);
Assert.AreEqual(now, b.Load(null));
}
Supporting DateTime.Parse in the WhenCalled API is in our backlog.
Please feel free to contact us via mail at support#typemock.com
Nofar
Typemock Support
I'm using linq to SQL and MVC2 with data annotations and I'm having some problems on validation of some types.
For example:
[DisplayName("Geplande sessies")]
[PositiefGeheelGetal(ErrorMessage = "Ongeldige ingave. Positief geheel getal verwacht")]
public string Proj_GeplandeSessies { get; set; }
This is an integer, and I'm validating to get a positive number from the form.
public class PositiefGeheelGetalAttribute : RegularExpressionAttribute {
public PositiefGeheelGetalAttribute() : base(#"\d{1,7}") { }
}
Now the problem is that when I write text in the input, I don't get to see THIS error, but I get the errormessage from the modelbinder saying "The value 'Tomorrow' is not valid for Geplande sessies."
The code in the controller:
[HttpPost]
public ActionResult Create(Projecten p)
{
if (ModelState.IsValid)
{
_db.Projectens.InsertOnSubmit(p);
_db.SubmitChanges();
return RedirectToAction("Index");
}
else
{
SelectList s = new SelectList(_db.Verbonds, "Verb_ID", "Verb_Naam");
ViewData["Verbonden"] = s;
}
return View();
}
What I want is being able to run the Data Annotations before the Model binder, but that sounds pretty much impossible. What I really want is that my self-written error messages show up on the screen.
I have the same problem with a DateTime, which i want the users to write in the specific form 'dd/MM/yyyy' and i have a regex for that. but again, by the time the data-annotations do their job, all i get is a DateTime Object, and not the original string. So if the input is not a date, the regex does not even run, cos the data annotations just get a null, cos the model binder couldn't make it to a DateTime.
Does anyone have an idea how to make this work?
Two options:
(1) You can make a Projecten viewModel where all fields are strings. This way the viewModel will always be created from the posted data and your dataannotations validation will always be evaluated. Obviously, you would then map the viewModel to your properly typed business objects maybe using AutoMapper.
(2) You can subclass the model binder.
I'm building an ASP.NET web service.
I've got my code defined as below, but I can't figure out how to the the wsdl to specify the minOccurs of the FirstName and LastName properties. I want those as required, and can not be empty. Is it possible?
[WebMethod()]
public void TestMethod(TestClass Test)
{
...
}
[Serializable]
public class TestClass
{
public string FirstName { get; set; }
public string LastName { get; set; }
}
It turns out that the WSDL is not used to validate incoming XML. It wouldn't matter whether or not you could specify minOccurs - it would not be used to validate the input.
I have posted the detailed answer on another thread with the same problem: How to make a dotnet webservice set minOccurs=“1” on a string value.
However the answer for strings is no.
The only way make minOccurs=1 without nullable=true is to declare a property with no default value (string has a default value of String.Empty) and without a property to check if the value was specified (making an identical property name with "Specified" word appended to it's name).
And you are still limited if John Saunders' answer is true.
It turns out that the WSDL is not used to validate incoming XML. It wouldn't matter whether or not you could specify minOccurs - it would not be used to validate the input.
Strings are reference types and so by definition nullable. If your property was an integer minoccurs would have been 1.
You can force the Serializer not to allow it to be null, by putting.
[XmlElement("name", IsNullable=false)]
above the property.
Edit: I meant reference types instead of value types. Thnx Joren!