Skip to content

Versions and Upgrading

m4bwav edited this page Sep 30, 2026 · 1 revision

Releases

From nuget.org, read on 2026-09-29:

Version Published State Downloads
2.0.0 2026-09-27 current 64
2.0.0-beta.1 2026-09-27 prerelease, the release rehearsal of 2.0.0 not shown in the search index
1.0.2 2016-06-05 deprecated (legacy) 2,396
1.0.0 2016-05-30 deprecated (legacy) 2,159

1.0.2 still installs: a .NET 10 app restored it on 2026-09-29, with NuGet's NU1701 warning because it ships only a .NET Framework 4.5 build (lib/net45). The CHANGELOG has every change.

What changed in 2.0.0 for callers

  • New: ImageUrl.HasImageExtension, ImageUrl.IsImageUrlAsync (four overloads) and ImageUrl.ImageExtensions. The old API gained nothing and lost nothing.
  • IsImageUrl is marked obsolete (a warning, CS0618 in C# and FS0044 in F#). Its answers and requests are unchanged.
  • The package declares FSharp.Core 6.0.7 or later. 1.0.2 needed FSharp.Core 4.4.0.0 without declaring it.
  • Targets: net10.0, net462 and netstandard2.0, where 1.0.2 had net45 only. .NET Framework 4.5 to 4.6.1 are no longer supported.

1.0.2 and 2.0.0 side by side

The same inputs went through the published 1.0.2 and the published 2.0.0, on .NET 10, against the local stand-in. There, /avatar answers image/png, /about text/html, /upper IMAGE/PNG, /x-imagefile application/x-imagefile, and /gone and any unknown path a 404:

"image.png"
  1.0.2 IsImageUrl         True
  2.0.0 IsImageUrl         True
  2.0.0 HasImageExtension  True
  2.0.0 IsImageUrlAsync    False
"x.webp"
  1.0.2 IsImageUrl         UriFormatException
  2.0.0 IsImageUrl         UriFormatException
  2.0.0 HasImageExtension  True
  2.0.0 IsImageUrlAsync    False
"a"
  1.0.2 IsImageUrl         UriFormatException
  2.0.0 IsImageUrl         UriFormatException
  2.0.0 HasImageExtension  False
  2.0.0 IsImageUrlAsync    False
"photos/cat.png?w=1"
  1.0.2 IsImageUrl         UriFormatException
  2.0.0 IsImageUrl         UriFormatException
  2.0.0 HasImageExtension  True
  2.0.0 IsImageUrlAsync    False
"ftp://images.test/a.png"
  1.0.2 IsImageUrl         True
  2.0.0 IsImageUrl         True
  2.0.0 HasImageExtension  False
  2.0.0 IsImageUrlAsync    False
"http://images.test/avatar"
  1.0.2 IsImageUrl         True  (1 request)
  2.0.0 IsImageUrl         True  (1 request)
  2.0.0 HasImageExtension  False
  2.0.0 IsImageUrlAsync    True  (1 request)
"http://images.test/a.png?size=1"
  1.0.2 IsImageUrl         WebException (ProtocolError)  (1 request)
  2.0.0 IsImageUrl         WebException (ProtocolError)  (1 request)
  2.0.0 HasImageExtension  True
  2.0.0 IsImageUrlAsync    True
"http://images.test/about"
  1.0.2 IsImageUrl         False  (1 request)
  2.0.0 IsImageUrl         False  (1 request)
  2.0.0 HasImageExtension  False
  2.0.0 IsImageUrlAsync    False  (1 request)
"http://images.test/upper"
  1.0.2 IsImageUrl         False  (1 request)
  2.0.0 IsImageUrl         False  (1 request)
  2.0.0 HasImageExtension  False
  2.0.0 IsImageUrlAsync    True  (1 request)
"http://images.test/x-imagefile"
  1.0.2 IsImageUrl         True  (1 request)
  2.0.0 IsImageUrl         True  (1 request)
  2.0.0 HasImageExtension  False
  2.0.0 IsImageUrlAsync    False  (1 request)
"http://images.test/gone"
  1.0.2 IsImageUrl         WebException (ProtocolError)  (1 request)
  2.0.0 IsImageUrl         WebException (ProtocolError)  (1 request)
  2.0.0 HasImageExtension  False
  2.0.0 IsImageUrlAsync    False  (1 request)
"x.GIF" under tr-TR
  1.0.2 IsImageUrl         UriFormatException
  2.0.0 IsImageUrl         UriFormatException
  2.0.0 HasImageExtension  True
  2.0.0 IsImageUrlAsync    False
same answer 12 of 12; same request heads 12 of 12

That last line compares 2.0.0's IsImageUrl with 1.0.2 run the same day, request headers included.

What 1.0.2's IsImageUrl does

These are the behaviours 2.0.0 keeps under the old name, as the rows above and the golden recordings show:

  • It decides by the text after the last dot of the whole string, query included, so a.png?size=1 sends a request, and ftp://images.test/a.png is true without one.
  • It throws for input it cannot decide: UriFormatException for relative strings such as a or x.webp, and WebException for every 4xx and 5xx answer.
  • It matches the Content-Type as a case-sensitive substring: IMAGE/PNG is false and application/x-imagefile is true.
  • It depends on the current culture: under tr-TR, x.GIF lowercases to a dotless ı, misses the list and throws.
  • It reads local files for file: URLs, follows redirects to any host and waits up to 100 seconds (from the README and the recordings; not rerun for this page).

Moving from 1.x

Where 1.x was used as Use in 2.0.0
an offline check of a name or URL url.HasImageExtension()
a check that may ask the server await url.IsImageUrlAsync(), or with your HttpClient and a token
a way to catch exceptions for "not sure" catch HttpRequestException and TaskCanceledException from IsImageUrlAsync only; everything else is false

Check the rows above where the answer changes: relative names are false in IsImageUrlAsync (use HasImageExtension for them), IMAGE/PNG becomes true, application/x-imagefile becomes false, and ftp: URLs become false. Code that must keep 1.0.2's exact answers can keep calling IsImageUrl and suppress the warning.

The obsolete warning from C# is on the API reference. From F#, this script:

#r "nuget: IsImageUrlDotNet, 2.0.0"
open IsImageUrlDotNet

printfn "%b" (IsImageUrlDotNetLib.IsImageUrl "image.png")

prints true and warns:

fs-obsolete.fsx(4,15): warning FS0044: This construct is deprecated. IsImageUrl keeps 1.0.2's answers, bugs included. Use ImageUrl.HasImageExtension (offline) or ImageUrl.IsImageUrlAsync (http and https only).

FSharp.Core

A C# project that used 1.0.2 without adding FSharp.Core itself failed on the first call. A .NET 10 app with 1.0.2 and nothing else printed:

FileNotFoundException: Could not load file or assembly 'FSharp.Core, Version=4.4.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'. The system cannot find the file specified.

2.0.0 brings FSharp.Core through its dependency, so an explicit FSharp.Core reference added for 1.x can go, unless something else in the project needs it.

The golden recordings replayed

The repository keeps recordings of the published 1.0.2 in tests/Golden/: 117 cases each, with every request each case sent. There is one for .NET 10 on Windows, Linux and macOS, one for .NET Framework 4.8 on Windows, and a Mono 6.8 reference. On 2026-09-29 the capture program in tests/Golden/Capture ran again, once against 1.0.2 and once against 2.0.0, one run after the other. The 2.0.0 run used a copy with only the package version changed. Each result was compared with the recording, answers and requests apart. On Windows:

## net10.0 against 1.0.2.net10.0-windows.json (recorded 2026-09-27, .NET 10.0.12, assembly IsImageUrlDotNet 1.0.2.0)
1.0.2 today: same answer 117 of 117, same requests 117 of 117, both 117 of 117 (1 answers equal once the drive letter is swapped, as the golden test does)
2.0.0 today: same answer 117 of 117, same requests 117 of 117, both 117 of 117 (1 answers equal once the drive letter is swapped, as the golden test does)
2.0.0 against 1.0.2, both today: same answer 117, same requests 117, both 117 of 117
## net48 against 1.0.2.net48-windows.json (recorded 2026-09-27, .NET Framework 4.8.9345.0, assembly IsImageUrlDotNet 1.0.2.0)
1.0.2 today: same answer 117 of 117, same requests 117 of 117, both 117 of 117 (1 answers equal once the drive letter is swapped, as the golden test does)
2.0.0 today: same answer 117 of 117, same requests 117 of 117, both 117 of 117 (1 answers equal once the drive letter is swapped, as the golden test does)
2.0.0 against 1.0.2, both today: same answer 117, same requests 117, both 117 of 117

and on Linux (WSL, Ubuntu 26.04):

## net10.0 against 1.0.2.net10.0-linux.json (recorded 2026-09-27, .NET 10.0.12, assembly IsImageUrlDotNet 1.0.2.0)
1.0.2 today: same answer 117 of 117, same requests 117 of 117, both 117 of 117
2.0.0 today: same answer 117 of 117, same requests 117 of 117, both 117 of 117
2.0.0 against 1.0.2, both today: same answer 117, same requests 117, both 117 of 117

The drive letter case is file:///nonexistent-isimageurl/file, whose message names the current drive. The macOS recording and the Mono reference were not replayed.

Clone this wiki locally