CurlDotNet Tutorial Projects: Running curl Commands in Real C# Applications
These CurlDotNet tutorial projects show curl commands running inside real C# applications: an ASP.NET Core minimal API, a System.CommandLine tool, a mock server, a message-bus publisher and more. Each snippet is a starting point you can paste into a .NET 8 or later project.
Install the package first:
dotnet add package CurlDotNet
Every snippet assumes using CurlDotNet; (and using CurlDotNet.Core; where CurlResult is named). Results come back as CurlResult with StatusCode, Headers, Body and IsSuccess.
Tutorial 1: Self-Service API Explorer
Why it matters: Store curated curl commands and let engineers run them from an ASP.NET Core endpoint without leaving .NET tooling.
using CurlDotNet;
var app = WebApplication.CreateBuilder(args).Build();
var templates = new Dictionary<Guid, string>();
app.MapPost("/requests", (string command) =>
{
var id = Guid.NewGuid(); templates[id] = command;
return Results.Created($"/requests/{id}", new { id, command });
});
app.MapPost("/requests/{id}/execute", async (Guid id) =>
{
var r = await Curl.ExecuteAsync(templates[id]);
return Results.Json(new { r.StatusCode, r.Body });
});
app.Run();
Validate submitted commands with Curl.Validate(command).IsValid before storing them.
Tutorial 2: Disaster Recovery Runbook CLI
Why it matters: Wrap recovery curl calls in a System.CommandLine app so operators run them from reviewed C# code, with a dry-run switch.
System.CommandLine is a package your own tool adds (dotnet add package System.CommandLine --prerelease); CurlDotNet itself does not depend on it. The snippet uses the 2.0 beta 4 API (SetHandler, InvokeAsync); later 2.0 releases renamed these.
using System.CommandLine;
using CurlDotNet;
var envOption = new Option<string>("--environment", () => "staging");
var dryRun = new Option<bool>("--dry-run", () => false);
var runbook = new RootCommand("DR runbook") { envOption, dryRun };
runbook.SetHandler(async (string env, bool dry) =>
{
var command = $"curl -X POST https://{env}.example.com/api/restore";
if (dry) { Console.WriteLine($"Would execute: {command}"); return; }
var result = await Curl.ExecuteAsync(command);
Console.WriteLine($"Restore status {result.StatusCode}");
}, envOption, dryRun);
await runbook.InvokeAsync(args);
Tutorial 3: Compliance Evidence Generator
Why it matters: Persist each command and its response as JSON so auditors can see exactly which workflows ran.
using System.Text.Json;
using CurlDotNet;
async Task CaptureAsync(string workflow, string command, string directory)
{
Directory.CreateDirectory(directory);
var result = await Curl.ExecuteAsync(command);
var bundle = new { workflow, command, result.StatusCode, result.Headers, result.Body,
timestamp = DateTimeOffset.UtcNow };
var path = Path.Combine(directory, $"{workflow}-{DateTime.UtcNow:yyyyMMddHHmmss}.json");
await File.WriteAllTextAsync(path, JsonSerializer.Serialize(bundle,
new JsonSerializerOptions { WriteIndented = true }));
}
await CaptureAsync("delete-user", "curl -X DELETE https://httpbin.org/delete", "evidence");
Tutorial 4: Migrating Bash Scripts to C#
Why it matters: Port a legacy bash script that shells out to curl into a console app; the curl strings stay exactly as they were.
using CurlDotNet;
string[] commands =
[
"curl https://httpbin.org/post -d 'user=svc&password=$SVC_PASSWORD'",
"curl https://httpbin.org/get -H 'Accept: application/json'",
"curl https://httpbin.org/status/204"
];
foreach (var command in commands)
{
var result = await Curl.ExecuteAsync(command);
Console.WriteLine($"{command} => {result.StatusCode}");
}
$SVC_PASSWORD is expanded from the environment by the parser, as it would be in bash.
Tutorial 5: Full-Fidelity Mock Server
Why it matters: Host a mock partner API in ASP.NET Core and exercise it with the same curl commands your integration uses.
using CurlDotNet;
var app = WebApplication.CreateBuilder(args).Build();
app.MapGet("/orders", () => Results.Json(new { items = new[] { new { id = 1, total = 99 } } }));
app.MapPost("/orders", (Order o) => Results.Created($"/orders/{o.Id}", o));
await app.StartAsync();
var list = await Curl.ExecuteAsync("curl http://localhost:5000/orders");
Console.WriteLine(list.Body);
await app.StopAsync();
public sealed record Order(int Id, decimal Total);
Tutorial 6: Publishing Results to a Message Bus
Why it matters: Emit an event after each request so other services can react to API outcomes.
using System.Text.Json;
using Azure.Messaging.ServiceBus;
using CurlDotNet;
var client = new ServiceBusClient(Environment.GetEnvironmentVariable("SERVICEBUS")!);
var sender = client.CreateSender("curl-events");
var result = await Curl.ExecuteAsync("curl https://httpbin.org/get");
var payload = JsonSerializer.Serialize(new { Command = "payments", result.StatusCode,
Timestamp = DateTimeOffset.UtcNow });
await sender.SendMessageAsync(new ServiceBusMessage(payload));
Tutorial 7: Mobile Backend Proxy
Why it matters: Expose one endpoint that forwards a mobile client’s request upstream with device metadata added as headers.
using CurlDotNet;
var app = WebApplication.CreateBuilder(args).Build();
app.MapPost("/mobile/proxy", async (MobileRequest request) =>
{
var command = $"curl {request.UpstreamUrl} -H 'X-App-Version: {request.AppVersion}'" +
$" -H 'X-Platform: {request.Platform}'";
var result = await Curl.ExecuteAsync(command);
return Results.Json(new { result.StatusCode, result.Body });
});
app.Run();
public sealed record MobileRequest(string UpstreamUrl, string AppVersion, string Platform);
Validate UpstreamUrl against an allow-list before interpolating it into a command.
Tutorial 8: Multi-Cloud Health Bridge
Why it matters: Run the same health probes against several regions from one worker and fail over on the result.
using CurlDotNet;
var results = await Curl.ExecuteManyAsync(
"curl --max-time 5 https://azure-region.example.com/bridge",
"curl --max-time 5 https://aws-region.example.com/bridge");
foreach (var r in results)
Console.WriteLine($"{r.Command} => {r.StatusCode}");
Tutorial 9: Line-Oriented Analytics Pipeline
Why it matters: Push each line of a response through System.Threading.Channels for downstream processing.
using System.Threading.Channels;
using CurlDotNet;
var channel = Channel.CreateUnbounded<string>();
var producer = Task.Run(async () =>
{
var result = await Curl.ExecuteAsync("curl https://httpbin.org/stream/5");
foreach (var line in result.Body.Split('\n', StringSplitOptions.RemoveEmptyEntries))
await channel.Writer.WriteAsync(line);
channel.Writer.Complete();
});
await foreach (var line in channel.Reader.ReadAllAsync())
Console.WriteLine(line);
await producer;
The whole response is read before lines are emitted; CurlDotNet does not stream partial bodies.
Tutorial 10: Support Troubleshooting Console
Why it matters: Give support agents a console that runs pre-approved commands instead of raw curl on their laptops.
using CurlDotNet;
var recipes = new Dictionary<int, string>
{
[1] = "curl https://httpbin.org/get?lookup=account",
[2] = "curl https://httpbin.org/uuid"
};
Console.WriteLine("Select action: 1) Lookup account 2) Latest order");
var choice = int.Parse(Console.ReadLine() ?? "1");
var result = await Curl.ExecuteAsync(recipes[choice]);
Console.WriteLine($"{result.StatusCode}\n{result.Body}");