-
Notifications
You must be signed in to change notification settings - Fork 611
Description
I was looking to integrate MiniProfiler to an ASP.net Core 2.2 MVC app and have come across a rendering issue. This issue only affects rendering of the MiniProfiler widget and seems to be related to the format that Started is being provided in JSON back to the widget.
Setup
- .Net Core 2.2
- ASP.Net Core 2.2.0 MVC
- MiniProfiler.AspNetCore.Mvc 4.0.165
- MiniProfiler.Providers.MySql 4.0.165
- Default config from the getting started page, with the exception of:
options.Storage = new MySqlStorage(Configuration["ConnectionStrings:DefaultConnection"]);The Full Error
Uncaught TypeError: i.Started.toUTCString is not a function
at n.renderProfiler (includes.min.js?v=4.0.165+g04f8ac4653:formatted:4790)
at n.buttonShow (includes.min.js?v=4.0.165+g04f8ac4653:formatted:4800)
at Object.success (includes.min.js?v=4.0.165+g04f8ac4653:formatted:4552)
at l (includes.min.js?v=4.0.165+g04f8ac4653:formatted:1825)
at Object.fireWith [as resolveWith] (includes.min.js?v=4.0.165+g04f8ac4653:formatted:1876)
at b (includes.min.js?v=4.0.165+g04f8ac4653:formatted:3539)
at XMLHttpRequest.<anonymous> (includes.min.js?v=4.0.165+g04f8ac4653:formatted:3757)
Example JS Response
{"Id":"d0d344d0-fed2-4215-9bcd-71039db08141","Name":"Home/Index","Started":"2019-03-16T15:54:50Z","DurationMilliseconds":3.0,"MachineName":"LDCL139658","Root":{"Id":"4649f8ab-5119-483c-9025-aedcf7c58ac4","Name":"https://localhost:44326/","DurationMilliseconds":3.000,"StartMilliseconds":0.000,"Children":[{"Id":"60d2bc5d-6ca1-4f55-b551-9a90b707eef4","Name":"MiniProfiler Prep","DurationMilliseconds":1.400,"StartMilliseconds":0.000},{"Id":"7c551655-915b-4995-82a8-b620f9b41937","Name":"Controller: Home.Index","DurationMilliseconds":0.000,"StartMilliseconds":1.600},{"Id":"6dba533e-96df-425d-a81f-6583fdc89146","Name":"Find: Index","DurationMilliseconds":0.000,"StartMilliseconds":1.600},{"Id":"0fb7b931-b50d-43a4-bf01-9184033e9fc7","Name":"Render: /Views/Home/Index.cshtml","DurationMilliseconds":0.700,"StartMilliseconds":1.700,"Children":[{"Id":"7b4023c6-4c7b-40c2-855b-b87b7f1c3939","Name":"Find: _CookieConsentPartial","DurationMilliseconds":0.000,"StartMilliseconds":2.200},{"Id":"ecbdccb1-b652-42d5-b8c1-0550a8290190","Name":"Render: /Views/Shared/_CookieConsentPartial.cshtml","DurationMilliseconds":0.000,"StartMilliseconds":2.300}]}]},"ClientTimings":{"RedirectCount":0,"Timings":[{"Name":"fetchStart","Start":0.0,"Duration":0.0},{"Name":"domainLookupStart","Start":0.0,"Duration":0.0},{"Name":"connectStart","Start":0.0,"Duration":0.0},{"Name":"requestStart","Start":3.0,"Duration":11.0},{"Name":"responseStart","Start":14.0,"Duration":13.0},{"Name":"domLoading","Start":27.0,"Duration":36.0},{"Name":"firstPaintTime","Start":220.0,"Duration":0.0},{"Name":"domInteractive","Start":282.0,"Duration":0.0},{"Name":"domContentLoadedEventStart","Start":282.0,"Duration":1.0},{"Name":"domComplete","Start":316.0,"Duration":0.0},{"Name":"loadEventStart","Start":316.0,"Duration":0.0}]},"User":"4","HasUserViewed":true}The key value being "Started":"2019-03-16T15:54:50Z"
Looking through the source I think I may have traced this to how the js handles the full page rendering and button rendering specifically JSON Ajax response is parsed using this regex:
dotnet/src/MiniProfiler.Shared/ui/lib/MiniProfiler.ts
Lines 418 to 431 in 04f8ac4
| const isoDate = /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*))(?:Z|(\+|-)([\d|:]*))?$/; | |
| const parseDates = (key: string, value: any) => | |
| key === 'Started' && typeof value === 'string' && isoDate.exec(value) ? new Date(value) : value; | |
| mp.fetchStatus[id] = 'Starting fetch'; | |
| this.jq.ajax({ | |
| url: this.options.path + 'results', | |
| data: JSON.stringify(request), | |
| dataType: 'json', | |
| contentType: 'application/json', | |
| type: 'POST', | |
| converters: { | |
| 'text json': (result) => JSON.parse(result, parseDates), | |
| }, |
Vs the full html page which takes a JS object, assumes the string is correct and just parses it:
dotnet/src/MiniProfiler.Shared/ui/lib/MiniProfiler.ts
Lines 325 to 326 in 04f8ac4
| // profiler will be defined in the full page's head | |
| window.profiler.Started = new Date('' + window.profiler.Started); // Ugh, JavaScript |
The Underlying Fault
The underlying fault seems to be the lack of precision returned from MySQL causing the iso date format to be simplified when converted to a string, ignoring the micro seconds.
A small change to the regex string used to Parse the dates to allow for optional micro seconds resolves the parsing issue:
isoDate = /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)(?:Z|(\+|-)([\d|:]*))?$/;Sorry if this was not enough detail or incorrectly formatted, this is my first OSS issue.
I will try and create a pull request for this too, I haven't built the full repo to test the fix, as I don't know how and have never used typescript, I'm still learning C# with simple repos.