Class CurlResult
- Namespace
- CurlDotNet.Core
- Assembly
- CurlDotNet.dll
🎯 The response from your curl command - everything you need is here!
After running any curl command, you get this object back. It has the status code, response body, headers, and helpful methods to work with the data.
The API is designed to be intuitive - just type what you want to do:
- Want the body? →
result.Body - Want JSON? →
result.ParseJson<T>()orresult.AsJson<T>() - Want to save? →
result.SaveToFile("path") - Want headers? →
result.Headers["Content-Type"] - Check success? →
result.IsSuccessorresult.EnsureSuccess()
Quick Example:
var result = await Curl.Execute("curl https://api.github.com/users/octocat");
if (result.IsSuccess) // Was it 200-299?
{
var user = result.ParseJson<User>(); // Parse JSON to your type
result.SaveToFile("user.json"); // Save for later
}
public class CurlResult
- Inheritance
-
CurlResult
- Inherited Members
Remarks
Design Philosophy: Every method name tells you exactly what it does. No surprises. If you guess a method name, it probably exists and does what you expect.
Fluent API: Most methods return 'this' so you can chain operations:
result
.EnsureSuccess() // Throw if not 200-299
.SaveToFile("backup.json") // Save a copy
.ParseJson<Data>() // Parse and return data
Properties
BinaryData
Binary data for files like images, PDFs, downloads.
When you download non-text files, the bytes are here:
// Download an image
var result = await Curl.Execute("curl https://example.com/logo.png");
if (result.IsBinary)
{
File.WriteAllBytes("logo.png", result.BinaryData);
Console.WriteLine($"Saved {result.BinaryData.Length} bytes");
}
public byte[] BinaryData { get; set; }
Property Value
- byte[]
Body
The response body as a string - this is your data!
Contains whatever the server sent back: JSON, HTML, XML, plain text, etc.
Common patterns:
// JSON API response (most common)
if (result.Body.StartsWith("{"))
{
var data = result.ParseJson<MyClass>();
}
// HTML webpage
if (result.Body.Contains("<html"))
{
result.SaveToFile("page.html");
}
// Plain text
Console.WriteLine(result.Body);
Note: For binary data (images, PDFs), use BinaryData instead.
Note: Can be null for 204 No Content or binary responses.
public string Body { get; set; }
Property Value
Command
The original curl command that was executed.
Useful for debugging or retrying:
Console.WriteLine($"Executed: {result.Command}");
// Retry the same command
var retry = await Curl.Execute(result.Command);
public string Command { get; set; }
Property Value
Exception
Any exception if the request failed completely.
Only set for network failures, not HTTP errors:
if (result.Exception != null)
{
// Network/DNS/Timeout failure
Console.WriteLine($"Failed: {result.Exception.Message}");
}
else if (!result.IsSuccess)
{
// HTTP error (404, 500, etc)
Console.WriteLine($"HTTP {result.StatusCode}");
}
public Exception Exception { get; set; }
Property Value
Headers
All HTTP headers from the response - contains metadata about the response.
Headers tell you things like content type, cache rules, rate limits, etc. Access them like a dictionary (case-insensitive keys).
Get a specific header:
// These all work (case-insensitive):
var type = result.Headers["Content-Type"];
var type = result.Headers["content-type"];
var type = result.Headers["CONTENT-TYPE"];
// Or use the helper:
var type = result.GetHeader("Content-Type");
Check rate limits (common in APIs):
if (result.Headers.ContainsKey("X-RateLimit-Remaining"))
{
var remaining = int.Parse(result.Headers["X-RateLimit-Remaining"]);
if (remaining < 10)
Console.WriteLine("⚠️ Only {0} API calls left!", remaining);
}
Common headers:
- Content-Type - Format of the data (application/json, text/html)
- Content-Length - Size in bytes
- Location - Where you got redirected to
- Set-Cookie - Cookies to store
- Cache-Control - How long to cache
public Dictionary<string, string> Headers { get; set; }
Property Value
IsBinary
Is this binary data? (images, PDFs, etc.)
Quick check before accessing BinaryData:
if (result.IsBinary)
File.WriteAllBytes("file.bin", result.BinaryData);
else
File.WriteAllText("file.txt", result.Body);
public bool IsBinary { get; }
Property Value
IsSuccess
Quick success check - true if status is 200-299.
The easiest way to check if your request worked:
if (result.IsSuccess)
{
// It worked! Do something with result.Body
}
else
{
// Something went wrong, check result.StatusCode
}
What's considered success: 200 OK, 201 Created, 204 No Content, etc.
What's NOT success: 404 Not Found, 500 Server Error, etc.
public bool IsSuccess { get; }
Property Value
OutputFiles
Files that were saved (if using -o flag).
Track what files were created:
foreach (var file in result.OutputFiles)
{
Console.WriteLine($"Saved: {file}");
}
public List<string> OutputFiles { get; set; }
Property Value
StatusCode
The HTTP status code - tells you what happened.
Common codes you'll see:
200 = OK, it worked!
201 = Created something new
204 = Success, but no content returned
400 = Bad request (you sent something wrong)
401 = Unauthorized (need to login)
403 = Forbidden (not allowed)
404 = Not found
429 = Too many requests (slow down!)
500 = Server error (their fault, not yours)
503 = Service unavailable (try again later)
Example - Handle different statuses:
switch (result.StatusCode)
{
case 200: ProcessData(result.Body); break;
case 404: Console.WriteLine("Not found"); break;
case 401: RedirectToLogin(); break;
case >= 500: Console.WriteLine("Server error, retry later"); break;
}
public int StatusCode { get; set; }
Property Value
Timings
Detailed timing information (like curl -w).
See how long each phase took:
Console.WriteLine($"DNS lookup: {result.Timings.NameLookup}ms");
Console.WriteLine($"Connect: {result.Timings.Connect}ms");
Console.WriteLine($"Total: {result.Timings.Total}ms");
public CurlTimings Timings { get; set; }
Property Value
Methods
AppendToFile(string)
Append response to an existing file.
Add to a file without overwriting:
// Log all responses
result.AppendToFile("api-log.txt");
// Build up a file over time
foreach (var url in urls)
{
var r = await Curl.Execute($"curl {url}");
r.AppendToFile("combined.txt");
}
public CurlResult AppendToFile(string filePath)
Parameters
filePathstring
Returns
AsJsonDynamic()
Parse JSON as dynamic object (when you don't have a class).
Useful for quick exploration or simple JSON structures. This method returns a dynamic object that allows you to access JSON properties without defining a C# class. However, there's no compile-time checking, so prefer ParseJson<T>() with typed classes when possible.
Example:
dynamic json = result.AsJsonDynamic();
Console.WriteLine(json.name); // Access properties directly
Console.WriteLine(json.users[0].email); // Navigate arrays
// Iterate dynamic arrays
foreach (var item in json.items)
{
Console.WriteLine(item.title);
}
public dynamic AsJsonDynamic()
Returns
- dynamic
A dynamic object representing the JSON. In .NET 6+, this is a JsonDocument. In .NET Standard 2.0, this is a
JObjectfrom Newtonsoft.Json.Access properties like:
dynamicObj.propertyNameordynamicObj["propertyName"]
Remarks
⚠️ Warning: No compile-time checking! If a property doesn't exist, you'll get a runtime exception.
For production code, prefer ParseJson<T>() with typed classes for better safety and IntelliSense support.
This method is useful for:
- Quick prototyping and exploration
- Working with highly dynamic JSON structures
- One-off scripts and tools
Exceptions
- ArgumentNullException
Thrown when Body is null or empty.
- See Also
AsJson<T>()
Parse JSON response (alternative name for ParseJson<T>()).
Some people prefer AsJson, some prefer ParseJson. Both methods are identical and produce the same result.
var data = result.AsJson<MyData>();
// Exactly the same as: result.ParseJson<MyData>()
public T AsJson<T>()
Returns
- T
An instance of
Twith data from the JSON Body.
Type Parameters
TThe type to deserialize to. See ParseJson<T>() for details.
Remarks
This is simply an alias for ParseJson<T>(). Use whichever method name you prefer.
Exceptions
- ArgumentNullException
Thrown when Body is null or empty.
- InvalidOperationException
Thrown when JSON deserialization fails.
- See Also
EnsureContains(string)
Throw if response body doesn't contain expected text.
Validate response content:
// Make sure we got the right response
result.EnsureContains("success");
// Check for error messages
if (result.Body.Contains("error"))
{
result.EnsureContains("recoverable"); // Make sure it's recoverable
}
public CurlResult EnsureContains(string expectedText)
Parameters
expectedTextstring
Returns
EnsureStatus(int)
Throw if status doesn't match what you expect.
Validate specific status codes:
// Expect 201 Created
result.EnsureStatus(201);
// Expect 204 No Content
result.EnsureStatus(204);
public CurlResult EnsureStatus(int expectedStatus)
Parameters
expectedStatusintThe status code you expect
Returns
- CurlResult
This result if status matches (for chaining)
Exceptions
- CurlHttpException
Thrown if status doesn't match
EnsureSuccess()
Throw an exception if the request wasn't successful (not 200-299).
Use this when you expect success and want to fail fast. This matches curl's -f (fail) flag behavior.
public CurlResult EnsureSuccess()
Returns
- CurlResult
This result if successful (for chaining)
Examples
// Fail fast pattern
try
{
var data = result
.EnsureSuccess() // Throws if not 200-299
.ParseJson<Data>(); // Only runs if successful
}
catch (CurlHttpException ex)
{
Console.WriteLine($"HTTP {ex.StatusCode}: {ex.Message}");
Console.WriteLine($"Response body: {ex.ResponseBody}");
}
// Common API pattern - get user data
var user = (await Curl.ExecuteAsync("curl https://api.example.com/user/123"))
.EnsureSuccess() // Throws on 404, 500, etc.
.ParseJson<User>(); // Safe to parse, we know it's 200
// Chain multiple operations
var response = await Curl.ExecuteAsync("curl https://api.example.com/data");
var processed = response
.EnsureSuccess() // Ensure 200-299
.SaveToFile("backup.json") // Save backup
.ParseJson<DataModel>(); // Then parse
// Different handling for different status codes
try
{
result.EnsureSuccess();
ProcessData(result.Body);
}
catch (CurlHttpException ex) when (ex.StatusCode == 404)
{
Console.WriteLine("Resource not found");
}
catch (CurlHttpException ex) when (ex.StatusCode >= 500)
{
Console.WriteLine("Server error - retry later");
}
Exceptions
- CurlHttpException
Thrown if status is not 200-299. The exception contains StatusCode and ResponseBody.
- See Also
FilterLines(Func<string, bool>)
Extract lines that match a condition.
Filter text responses:
// Keep only error lines
result.FilterLines(line => line.Contains("ERROR"));
// Remove empty lines
result.FilterLines(line => !string.IsNullOrWhiteSpace(line));
// Keep lines starting with data
result.FilterLines(line => line.StartsWith("data:"));
public CurlResult FilterLines(Func<string, bool> predicate)
Parameters
Returns
GetHeader(string)
Get a specific header value (case-insensitive).
Easy header access with null safety. This matches curl's header behavior exactly.
public string GetHeader(string headerName)
Parameters
headerNamestringName of the header (case doesn't matter)
Returns
- string
Header value or null if not found
Examples
// Get content type
var contentType = result.GetHeader("Content-Type");
if (contentType?.Contains("json") == true)
{
var data = result.ParseJson<MyData>();
}
// Check rate limits (common in APIs)
var remaining = result.GetHeader("X-RateLimit-Remaining");
if (remaining != null && int.Parse(remaining) < 10)
{
Console.WriteLine("⚠️ Only {0} API calls left!", remaining);
}
// Check cache control
var cacheControl = result.GetHeader("Cache-Control");
if (cacheControl?.Contains("no-cache") == true)
{
Console.WriteLine("Response should not be cached");
}
// Get redirect location
var location = result.GetHeader("Location");
if (location != null)
{
Console.WriteLine($"Redirected to: {location}");
}
- See Also
HasHeader(string)
Check if a header exists.
Test for header presence before accessing. This is case-insensitive, matching curl's behavior.
public bool HasHeader(string headerName)
Parameters
headerNamestringName of the header to check (case-insensitive)
Returns
- bool
true if the header exists, false otherwise
Examples
// Check for cookies
if (result.HasHeader("Set-Cookie"))
{
var cookie = result.GetHeader("Set-Cookie");
Console.WriteLine($"Cookie received: {cookie}");
}
// Check for authentication requirements
if (result.HasHeader("WWW-Authenticate"))
{
Console.WriteLine("Authentication required");
}
// Check for custom headers
if (result.HasHeader("X-Custom-Header"))
{
var value = result.GetHeader("X-Custom-Header");
ProcessCustomValue(value);
}
// Conditional logic based on headers
if (result.HasHeader("Content-Encoding") &&
result.GetHeader("Content-Encoding").Contains("gzip"))
{
Console.WriteLine("Response is gzip compressed");
}
- See Also
ParseJson<T>()
Parse the JSON response into your C# class.
The most common operation - turning JSON into objects. This method uses JsonSerializer in .NET 6+ and Newtonsoft.Json.JsonConvert in .NET Standard 2.0 for maximum compatibility.
Example:
// Define your class matching the JSON structure
public class User
{
public string Name { get; set; }
public string Email { get; set; }
public int Id { get; set; }
}
// Parse the response
var user = result.ParseJson<User>();
Console.WriteLine($"Hello {user.Name}!");
// Or parse arrays
var users = result.ParseJson<List<User>>();
Console.WriteLine($"Found {users.Count} users");
Tip: Use https://json2csharp.com to generate C# classes from JSON!
public T ParseJson<T>()
Returns
- T
An instance of
Twith data from the JSON Body.
Type Parameters
TThe type to deserialize to. Must match the JSON structure. Can be a class, struct, or collection type like List<T> or Dictionary<TKey, TValue>.
Remarks
This method automatically detects whether to use System.Text.Json or Newtonsoft.Json based on the target framework.
For complex JSON structures, consider using AsJsonDynamic() for exploration, then creating a typed class.
If T doesn't match the JSON structure, properties that don't match will be left at their default values.
Exceptions
- ArgumentNullException
Thrown when Body is null or empty.
- InvalidOperationException
Thrown when JSON deserialization fails or JSON doesn't match type
T.
- See Also
Print()
Print status code and body to console.
More detailed debug output:
result.Print();
// Output:
// Status: 200
// {"name":"John","age":30}
public CurlResult Print()
Returns
PrintBody()
Print the response body to console.
Quick debugging output:
result.PrintBody(); // Just prints the body
// Chain with other operations
result
.PrintBody() // Debug output
.SaveToFile("out.txt") // Also save it
.ParseJson<Data>(); // Then parse
public CurlResult PrintBody()
Returns
- CurlResult
This result (for chaining)
PrintVerbose()
Print everything - status, headers, and body (like curl -v).
Full debug output:
result.PrintVerbose();
// Output:
// Status: 200
// Headers:
// Content-Type: application/json
// Content-Length: 123
// Body:
// {"name":"John"}
public CurlResult PrintVerbose()
Returns
Retry()
Retry the same curl command again.
Simple retry for transient failures:
// First attempt
var result = await Curl.Execute("curl https://flaky-api.example.com");
// Retry if it failed
if (!result.IsSuccess)
{
result = await result.Retry();
}
// Retry with delay
if (result.StatusCode == 429) // Too many requests
{
await Task.Delay(5000);
result = await result.Retry();
}
public Task<CurlResult> Retry()
Returns
- Task<CurlResult>
New result from retrying the command
RetryWith(Action<CurlSettings>)
Retry with modifications to the original command.
Retry with different settings:
// Retry with longer timeout
var result = await result.RetryWith(settings =>
{
settings.Timeout = TimeSpan.FromSeconds(60);
});
// Retry with authentication
var result = await result.RetryWith(settings =>
{
settings.AddHeader("Authorization", "Bearer " + token);
});
public Task<CurlResult> RetryWith(Action<CurlSettings> configure)
Parameters
configureAction<CurlSettings>
Returns
SaveAsCsv(string)
Save JSON response as CSV file (for JSON arrays).
Converts JSON arrays to CSV for Excel:
// JSON: [{"name":"John","age":30}, {"name":"Jane","age":25}]
result.SaveAsCsv("users.csv");
// Creates CSV:
// name,age
// John,30
// Jane,25
// Open in Excel
Process.Start("users.csv");
Note: Only works with JSON arrays of objects.
public CurlResult SaveAsCsv(string filePath)
Parameters
filePathstring
Returns
SaveAsJson(string, bool)
Save as formatted JSON file (pretty-printed).
Makes JSON human-readable with indentation:
// Save with nice formatting
result.SaveAsJson("data.json"); // Pretty-printed
result.SaveAsJson("data.json", false); // Minified
// Before: {"name":"John","age":30}
// After: {
// "name": "John",
// "age": 30
// }
public CurlResult SaveAsJson(string filePath, bool indented = true)
Parameters
filePathstringWhere to save the JSON file
indentedbooltrue for pretty formatting (default), false for minified
Returns
- CurlResult
This result (for chaining)
SaveToFile(string)
Save the response to a file - works for both text and binary!
Smart saving - automatically handles text vs binary:
// Save any response
result.SaveToFile("output.txt"); // Text saved as text
result.SaveToFile("image.png"); // Binary saved as binary
// Chain operations (returns this)
result
.SaveToFile("backup.json") // Save a backup
.ParseJson<Data>(); // Then parse it
Path examples:
result.SaveToFile("file.txt"); // Current directory
result.SaveToFile("data/file.txt"); // Relative path
result.SaveToFile(@"C:\temp\file.txt"); // Absolute path
result.SaveToFile("/home/user/file.txt"); // Linux/Mac path
public CurlResult SaveToFile(string filePath)
Parameters
filePathstringWhere to save the file
Returns
- CurlResult
This result (for chaining)
Examples
// Download and save JSON response
var result = await Curl.ExecuteAsync("curl https://api.example.com/data.json");
result.SaveToFile("data.json");
// File is now saved to disk AND still available in result.Body
// Download image and save
var result = await Curl.ExecuteAsync("curl https://example.com/logo.png");
result.SaveToFile("logo.png");
Console.WriteLine($"Saved {result.BinaryData.Length} bytes");
// Chain with parsing
var result = await Curl.ExecuteAsync("curl https://api.example.com/users");
var users = result
.SaveToFile("backup-users.json") // Save backup
.ParseJson<List<User>>(); // Then parse
// Save with relative path
result.SaveToFile("downloads/report.pdf");
// Save with absolute path
result.SaveToFile(@"C:\Temp\output.txt"); // Windows
result.SaveToFile("/tmp/output.txt"); // Linux/Mac
- See Also
SaveToFileAsync(string)
Save the response to a file asynchronously.
Same as SaveToFile but doesn't block:
await result.SaveToFileAsync("large-file.json");
// Or chain async operations
await result
.SaveToFileAsync("backup.json")
.ContinueWith(_ => Console.WriteLine("Saved!"));
public Task<CurlResult> SaveToFileAsync(string filePath)
Parameters
filePathstring
Returns
ToStream()
Convert the response to a Stream for reading.
Useful for streaming or processing data:
using var stream = result.ToStream();
using var reader = new StreamReader(stream);
var line = await reader.ReadLineAsync();
// Or for binary data
using var stream = result.ToStream();
var buffer = new byte[1024];
var bytesRead = await stream.ReadAsync(buffer, 0, buffer.Length);
public Stream ToStream()
Returns
- Stream
A MemoryStream containing the response data
Transform<T>(Func<CurlResult, T>)
Transform the result using your own function.
Extract or convert data however you need:
// Extract just what you need
var name = result.Transform(r =>
{
var user = r.ParseJson<User>();
return user.Name;
});
// Convert to your own type
var summary = result.Transform(r => new
{
Success = r.IsSuccess,
Size = r.Body?.Length ?? 0,
Type = r.GetHeader("Content-Type")
});
public T Transform<T>(Func<CurlResult, T> transformer)
Parameters
transformerFunc<CurlResult, T>
Returns
- T
Type Parameters
T