Skip to content

Recipes

m4bwav edited this page Sep 30, 2026 · 1 revision

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.

Keep the URLs that look like images, offline

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.

Ask about several URLs at once

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.

Treat an unreachable server as unknown

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.

A shorter deadline for one call

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 own client with its own headers

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/*

Let the package apply its redirect rules

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.

Test your code without a network

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.

F#: check several URLs in a task

#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.

Clone this wiki locally