A388

iOS Safari is Worthless

,

SPOKE TOO SOON

It appears that Apple decided to finally fix the issue in the latest iOS 14 update which came out ~2 weeks after I wrote this.

In one of my projects I was using flatpickr as a date/time picker and I've been slowly removing it in favor of the built-in browser types. All was going well, except one of the forms I updated recently is primarily used by our users on their iPads. After the update was published I started getting hammered with error reports from users.

Turns out iOS Safari is stupid when it comes to datetime-local fields. It validates the field's value as yyyy-MM-ddTHH:mm, but it generates the value from its picker as yyyy-MM-ddTHH:mm:ss.fff. Since all you get is a picker control, there's no way to correct the value and Safari traps itself into a never ending invalid state.

To fix the issue, you have to use JavaScript to correct the value after its generated, which defeats the purpose of just using the browser's built-in functionality. Using jQuery and Date.js you can do something like this:

$("[type='datetime-local']").on(elementBlur, (
    e: JQueryEventObject) => {
        const $field = $(e.currentTarget);
        let oldValue = $field.val();
        const lastPeriod = oldValue.lastIndexOf(".");

        oldValue = oldValue.substring(0, lastPeriod);

        const newValue = Date.parse(oldValue).toString("yyyy-MM-ddTHH:mm");

        $field.val(newValue);
    });

What makes it worse is that it's been an issue since iOS 10 released in September of 2016. All Apple has to do is fix the format of the generated value. Arguably a very simple fix in the grand scheme of things, yet its been ignored for four years now. Apple has succeeded in becoming the Microsoft of the old days, and Safari has succeeded in becoming the new Internet Explorer.

That is one of the reasons why I think iOS Safari is worthless, but that will never change on iOS since Apple likes to be in absolute control.

Arex388.OpenWeatherMap 1.0.0 Released

I've started a very complicated update for the project at my work and it had a requirement to get the current weather conditions (specifically temperature and humidity) for a geocoordinate point. I settled on using the OpenWeatherMap API because it provided what I needed and the free account allows for more than enough daily usage for my needs.

I looked at what was available on NuGet, but the packages available were years out of date and I didn't like having to rely on potentially abandoned packages. So, in the spirit of all the other API clients I've made so far, I rolled my own package: Arex388.OpenWeatherMap. Since I only needed the current weather conditions for a geocoordinate point, I only implemented that functionality, but if there's demand for more I'll expand the functionality. NuGet is available here.

Arex388.Extensions.CsvHelper 1.0.1 Released

The Arex388.Extensions.CsvHelper package has been updated to 1.0.1. This is a minor update that does a simple check if the contents of a CsvDbSet<T> have changed by summing their hash codes. If the hash code hasn't changed then the call to Save() will return early.

Since this blog is now backed by this package I've noticed that the CSV files are always being updated on the server regardless if there was changes or not. This was caused by the Save() method always running because the CSV file that tracks visits to a post is always changing. With this update I'm hoping that only it will be getting updated instead of everything.

Preventing Browser Requests for favicon.ico

In my Arex388.AspNetCore NuGet package I have an AntiFavicon middleware that would intercept requests to /favicon.ico and return a 404 early in the request processing pipeline. At some point I forgot about it and in one of my bigger projects I implemented a different fix that I found on this StackOverflow question.

It is a much better approach than my middleware because it stops the browser from making the request from the very beginning. All you have to do is add this link element to the head element:

<link rel="icon" href="data:;base64,iVBORw0KGgo=" type="image/png" />

I'm going to remove my interception middleware since this is the better approach. Thank you Diego Perini for providing this solution!

Announcing Cleaner10.io to Clean and Configure Windows 10 and Windows Server 2016 and Newer

I have revised the Windows 10 Cleaner tool I had on the blog here, and extracted it into its own website, cleaner10.io. The revision brings it up to version 3 which includes many more configuration options and software installations while being even more hands-off than v2. The script is compatible with Windows 10 version 1803, 1809, 1903, 1909, and 2004 and with Windows Server 2016 and 2019.

Introducing Arex388.AspNet.Mvc.Startup NuGet Package to Simulate ASP.NET Core's Startup.cs in Classic ASP.NET MVC Applications

It's been a while since I wrote anything on here because I've been busy out of my mind, but I recently made something that I want to share with everyone.

At my work, our main application is an ASP.NET MVC 5 app (5.2.7 to be exact). I use Ninject for dependency injection and it has been working well for ~6 years so far. That being said, I was having a very hard time getting AutoMapper's instance API registered with Ninject. I tried the example in AutoMapper's docs, but I couldn't get it to work. Because of this, I couldn't update to AutoMapper 9 where the static API no longer exists.

After spending a few days reading and researching how to get AutoMapper and Ninject to work I ran across a blog post from Scott Dorman about using the Microsoft.Extensions.DependencyInjection NuGet package with classic ASP.NET MVC apps. I tried implementing his example, but couldn't get it to work at all either.

After several more days of research, I stumbled onto a Gist from David Fowler where he takes a slightly different approach. I decided to give it a shot and this time it worked! Finally, I had a working code sample. From there I just messed around with it in a throw-away app to get the hang of it. Once I was satisfied I decided to extract it out into its own library so I could share it on NuGet.

Originally it was called Arex388.AspNet.Mvc.DependencyInjection, but as I progressed through it, I realized that using Owin I can make it essentially simulate ASP.NET Core's Startup.cs file, except it would all be in the Global.asax.cs. So the current version is a mix of code examples from David Fowler and Scott Dorman with some sprinkles of code from me here and there to glue it together.

How to Use

  1. Add the Arex388.AspNet.Mvc.Startup NuGet package.
  2. Change your Global.asax.cs to inherit from StartupApplication.
  3. Add [assembly: OwinStartup(typeof(YourNamespace.MvcApplication))] attribute to the namespace of your Global.asax.cs.
  4. Implement the Configure and ConfigureServices methods inherited from StartupApplication.
  5. Add ConfigureServices() to the end of Application_Start.

Here's a more complete example:

[assembly: OwinStartup(typeof(YourNamespace.MvcApplication))]
namespace YourNamespace {
    public class MvcApplication :
        StartupApplication {
        public void Application_Start() {
            //	Other setup and configuration code here...

            ConfigureServices();
        }

		public override Configure(
			IAppBuilder app) {
			//	Add IAppBuilder configurations
        }

        public override void ConfigureServices(
            IServiceCollection services) {
            var assembly = typeof(MvcApplication).Assembly;

            //	Add your controllers
            services.AddControllers(assembly);

            //	Add other services that have IServiceCollection extensions
        }
    }
}

Conclusion

As you can see it's very simple and easy, seems to "just work" and for me at least has helped me make sure my ~6 year old project is still keeping up with ASP.NET Core somehow.

And with that here's a delayed Christmas present to end off 2019! The source code is available on GitHub, and the package is available on NuGet.

Fixing the "Keyset does not exist" exception with the Data Protection API in .NET Core

Looks like I'm going to be making a series of posts based on the project I'm currently working on. In this post, we're going explore an infuriating exception I was getting.

In the project, I'm using the Data Protection API for the first time. I settled on using it with a certificate for protecting the keys. On first run of any of the apps in the project, they would create a new starter key fine, but every run after that they would be unable to decrypt the key and would just keep making new ones. The exception was:

Key {020d5bfa-9105-4854-84ce-f097763d47d5} is ineligible to be the default key because its CreateEncryptor method failed. Internal.Cryptography.CryptoThrowHelper+WindowsCryptographicException: Keyset does not exist

This was puzzling. Why would it fail to create the encryptor when it just did so on the previous run and generated the key?

After struggling with this for a while, it turned out to be just permissions. For whatever reason, when its time to decrypt the key, the app was unable to read the certificate out of the certificate store. It apparently works perfectly fine when writing new keys, but not for reading them.

Answers on StackOverflow suggested giving IIS_IUSER permissions on the certificate, and I'm sure that would probably work fine in a production environment, but I was not always testing the apps from IIS. In fact, in order to resolve some other even more infuriating issues, I had to manually run the apps through the console. At that point, they were inheriting my user account's permissions.

In the end, I just gave Everyone permissions on the certificate and it worked out. I also did the same for the certificate on the production server (it's the same certificate). To do so:

  • Open the Certificates MMC
  • Find your certificate, probably in Personal > Certificates
  • Right-click on the certificate All Tasks > Manage Private Keys
  • Add Everyone as a new user with full permissions

Make sure the certificate is valid. You can use a self-signed certificate if you go through some annoying hoops and implement your own version of the class that finds the certificate by its thumbprint. You'll have to do that just so you can tell it to ignore invalid certificates. I did that in the beginning, but eventually just spent the $16 it was to get a valid certificate for two years over at Namecheap.

Hangfire JobStorage.Current is Not Set Even Though you Registered its Services for Dependency Injection

I'm currently working on a project that is split into five ASP.NET Core 2.2 web apps. One of the web apps is a dedicated Hangfire server, and another one is a dedicated IoT server. The IoT server needs to occasionally enqueue background jobs to the Hangfire server. When I was testing it out the other day for the first time, I got a weird exception that Hangfire's JobStorage.Current property is not set.

This surprised me because I made sure to add it and use it in the Startup class. I went back into the class and sure enough, it was there. I then compared it with the Startup classes for the other web apps (which were working fine) and it was identical. Nevertheless the exception was being thrown.

After some searching online and getting nowhere I stumbled, really by chance, onto a bug report on Hangfire's site. It's by a user named Poly and he had the exact same issue as me. Luckily for me, he had a workaround for it by injecting the IServiceProvider in the Configure method and then using it to get an instance of the JobStorage. This initializes the property and everything works great after that.

Sadly, his post is from October of 2016, so almost three years later, this still seems to be an issue in Hangfire.

Review: Star Wars - Thrawn Trilogy Series

I recently finished listening to the Star Wars: Thrawn Trilogy audiobook series, and I really liked it. At times I didn‘t do anything but sit and listen to it, that‘s how captivated I was with it.

Going into it I didn‘t know much about Thrawn except that he was a tactical genius and that he was the "bad guy". Now, having completed the series, I find him to be quite likable. He‘s still the "bad guy", but he’s the lovable bad guy. Also, yes, he is a tactical genius. His ability to piece together the big picture out of bread crumbs is amazing.

The series also introduced Talon Karrde, whom I already liked, but didn‘t know much about either. He‘s Thrawn‘s "good guy" counterpart in my opinion, even though he resists fully committing to being that.

Mara Jade is also introduced, but she‘s kind of mean, although that‘s because she was still under the influence of Palpatine. I‘m glad she finally breaks out of it, although I have a feeling that Palpatine‘s influence was to undermine Joruus C‘baoth‘s plan to claim the Empire as his own.

Overall I really enjoyed the series and I highly recommend it to any Star Wars fan.

Arex388.Geocodio 1.2.2 Released

This is a minor update to add the Metropolitan Divisions property for Census data.