Repository navigation
Recipes
Each recipe is a complete .NET program (or F# script) that ran as shown, against 2.0.0 from nuget.org. Online recipes ask .test hosts, which a local stand-in answered (see Home); with real URLs the answers depend on the real servers. Every C# recipe printed the same on .NET 10, .NET Framework 4.8 and .NET 8.
using IsImageUrlDotNet;
string[] urls =
[
"https://example.com/photos/cat.PNG?w=200",
"https://example.com/report.pdf",
"https://example.com/avatar",
"/static/logo.svg",
];
foreach (var url in urls.Where(u => u.HasImageExtension()))
{
Console.WriteLine(url);
}prints:
https://example.com/photos/cat.PNG?w=200
/static/logo.svg
No request is made. https://example.com/avatar has no extension, so only a request could tell.
The shared client can take many calls at a time.
using IsImageUrlDotNet;
string[] urls = ["http://images.test/avatar", "http://images.test/about", "http://images.test/photo"];
bool[] answers = await Task.WhenAll(urls.Select(url => url.IsImageUrlAsync()));
for (var i = 0; i < urls.Length; i++)
{
Console.WriteLine($"{urls[i]} {answers[i]}");
}prints:
http://images.test/avatar True
http://images.test/about False
http://images.test/photo True
The stand-in received four requests, in no fixed order: /photo redirected to /avatar, which was then asked a second time.
using IsImageUrlDotNet;
static async Task<bool?> TryIsImageAsync(string url)
{
try
{
return await url.IsImageUrlAsync();
}
catch (HttpRequestException)
{
return null; // the server could not be reached or did not answer HTTP
}
catch (TaskCanceledException)
{
return null; // timed out
}
}
foreach (var url in new[] { "http://images.test/avatar", "http://images.test/close" })
{
bool? answer = await TryIsImageAsync(url);
Console.WriteLine($"{url} {answer?.ToString() ?? "unknown"}");
}prints:
http://images.test/avatar True
http://images.test/close unknown
The stand-in closes /close without answering.
The shared client waits up to 10 seconds. A token shortens one call:
using IsImageUrlDotNet;
using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(2));
try
{
Console.WriteLine(await "http://images.test/slow".IsImageUrlAsync(cts.Token));
}
catch (TaskCanceledException)
{
Console.WriteLine("gave up after 2 seconds");
}prints:
gave up after 2 seconds
/slow accepts the request and never answers.
Your client's headers replace the package's User-Agent:
using IsImageUrlDotNet;
using var client = new HttpClient();
client.DefaultRequestHeaders.UserAgent.ParseAdd("my-crawler/1.0 (+https://my.example/bot)");
client.DefaultRequestHeaders.Accept.ParseAdd("image/*");
Console.WriteLine(await "http://images.test/avatar".IsImageUrlAsync(client));prints:
True
and the stand-in received:
GET http://images.test/avatar HTTP/1.1
Host: images.test
User-Agent: my-crawler/1.0 (+https://my.example/bot)
Accept: image/*
A handler with automatic redirects off leaves redirects to the package, which follows http and https only and never https to http, on every runtime (why it matters):
using IsImageUrlDotNet;
using var handler = new HttpClientHandler { AllowAutoRedirect = false };
using var client = new HttpClient(handler);
Console.WriteLine(await "http://images.test/photo".IsImageUrlAsync(client));
Console.WriteLine(await "http://images.test/to-ftp".IsImageUrlAsync(client));prints:
True
False
/photo redirects to an image; /to-ftp redirects to an ftp: URL, which is not requested.
A handler of your own can play the server, so tests of code that calls IsImageUrlAsync need no network at all:
using System.Net;
using System.Net.Http.Headers;
using IsImageUrlDotNet;
using var client = new HttpClient(new FakeServer());
Console.WriteLine(await "https://cdn.test/avatar".IsImageUrlAsync(client));
Console.WriteLine(await "https://cdn.test/page".IsImageUrlAsync(client));
sealed class FakeServer : HttpMessageHandler
{
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
var type = request.RequestUri!.AbsolutePath == "/avatar" ? "image/png" : "text/html";
var response = new HttpResponseMessage(HttpStatusCode.OK) { Content = new ByteArrayContent([]) };
response.Content.Headers.ContentType = new MediaTypeHeaderValue(type);
return Task.FromResult(response);
}
}prints:
True
False
Nothing reached the stand-in: the handler answered in memory.
#r "nuget: IsImageUrlDotNet, 2.0.0"
open System.Net.Http
open IsImageUrlDotNet
let check (client: HttpClient) (urls: string list) =
task {
for url in urls do
let! answer = ImageUrl.IsImageUrlAsync(url, client)
printfn "%s %b" url answer
}
let client = new HttpClient()
(check client [ "http://images.test/avatar"; "http://images.test/about" ]).Wait()prints:
http://images.test/avatar true
http://images.test/about false
This ran under dotnet fsi on .NET 10.
This wiki describes IsImageUrlDotNet 2.0.0 and was last updated on 2026-09-29. The library is MIT licensed. Report problems in the issues. On NuGet: IsImageUrlDotNet.
Using it
The releases
Contributing
Elsewhere