2.17Sending from a cURL Command

Updated on Aug 20, 2026~10 min read

When integrating with or debugging third-party APIs, cURL commands are the most common way to describe requests. The framework has a built-in cURL command parsing engine that supports initiating HTTP requests directly from a native cURL command string in one step, covering common options (such as -X, -H, -d, -F, -u, --data-urlencode, --max-time, --http2, etc.), and can be freely extended with custom flags.

Usage is very simple: pass the cURL command directly into HttpRequestBuilder.FromCurl(), then send it via IHttpRemoteService.

cs
var result = await httpRemoteService.SendAsStringAsync(    HttpRequestBuilder.FromCurl("curl https://furion.net"));

The following examples demonstrate specific usage across several scenarios.

A Regular GET Request

cs
var result = await httpRemoteService.SendAsStringAsync(    HttpRequestBuilder.FromCurl("curl https://furion.net"));

With Query Parameters and a JSON Request Body

cs
var result = await httpRemoteService.SendAsStringAsync(    HttpRequestBuilder.FromCurl("""        curl -k -X POST 'https://localhost:7044/HttpRemote/AddModel?query1=10&query2=hello' \        -H 'Content-Type: application/json' \        -d '{          "id": 1,          "name": "sample"        }'        """));

Multipart Form (File Upload + Regular Fields)

cs
var result = await httpRemoteService.SendAsStringAsync(    HttpRequestBuilder.FromCurl("""        curl -k -X POST 'https://localhost:7044/HttpRemote/AddForm?id=100' \        -F 'Id=100' \        -F 'Name=furion' \        -F 'File=@C:\Workspaces\httptest.jpg'        """));

File uploads use the @ prefix. Paths support local absolute paths (such as C:\...) or network URLs (such as @https://example.com/avatar.png).

URL-Encoded Form (application/x-www-form-urlencoded)

Use -d to send URL-encoded data:

cs
var result = await httpRemoteService.SendAsStringAsync(    HttpRequestBuilder.FromCurl("""        curl -k -X POST 'https://localhost:7044/HttpRemote/AddUrlForm' \        -H 'Content-Type: application/x-www-form-urlencoded' \        -d 'id=200&name=furion'        """));

Use --data-urlencode to automatically encode special characters such as spaces:

cs
var result = await httpRemoteService.SendAsStringAsync(    HttpRequestBuilder.FromCurl("""        curl -k -X POST 'https://localhost:7044/HttpRemote/AddUrlForm' \        --data-urlencode 'id=200' \        --data-urlencode 'name=fu rion'        """));

Uploading a Single File

cs
var result = await httpRemoteService.SendAsStringAsync(    HttpRequestBuilder.FromCurl("""        curl -k -X POST 'https://localhost:7044/HttpRemote/AddFile' \        -F 'file=@C:\Workspaces\httptest.jpg'        """));

Uploading Multiple Files

cs
var result = await httpRemoteService.SendAsStringAsync(    HttpRequestBuilder.FromCurl("""        curl -k -X POST 'https://localhost:7044/HttpRemote/AddFiles' \        -F 'files=@C:\Workspaces\httptest.jpg' \        -F 'files=@C:\Workspaces\httptest.jpg'        """));

Sending a Raw String (such as "This is a raw string")

cs
var result = await httpRemoteService.SendAsStringAsync(    HttpRequestBuilder.FromCurl("""        curl -k -X POST 'https://localhost:7044/HttpRemote/RawString' \        -H 'Content-Type: application/json' \        -d '"This is a raw string"'        """));

A Request with Authentication Information

cs
var result = await httpRemoteService.SendAsStringAsync(    HttpRequestBuilder.FromCurl("""        curl -X POST https://jsonplaceholder.typicode.com/posts \        -H "Content-Type: application/json" \        -u testuser:testpass \        -d '{"title":"Test"}'        """));

Ignoring Output Options (such as -o)

Some cURL options (such as -o, -v, -s) are output controls. They do not affect request building and are automatically ignored. For example:

cs
var result = await httpRemoteService.SendAsStringAsync(    HttpRequestBuilder.FromCurl("curl -o qr.png \"https://api.qrserver.com/v1/create-qr-code/?size=150x150&data=Hello\""));

This command sends the GET request and obtains the response content normally, but it does not save the result to a file (file saving must be handled by you).

Extending Custom cURL Options

The framework's cURL parser uses a pluggable extractor architecture, where each cURL option is handled by an independent IHttpCurlExtractor implementation. You can support private cURL flags (such as --my-flag) by adding custom extractors without modifying the framework source code.

Implementing a Custom Extractor

The most convenient way to create a custom extractor is to inherit from the HttpCurlExtractorBase base class, which already encapsulates cursor advancement and argument consumption logic:

cs
/// <summary>///     Custom --my-flag extractor/// </summary>internal sealed class CurlMyFlagExtractor : HttpCurlExtractorBase{    /// <summary>    ///     The set of flags to match (case-insensitive)    /// </summary>    protected override string[] Flags => ["--my-flag"];    /// <summary>    ///     Whether an argument is required. Defaults to true; set to false if the flag takes no argument.    /// </summary>    protected override bool RequiresArgument => true;    /// <summary>    ///     The specific operation to perform when the flag is matched    /// </summary>    /// <param name="httpRequestBuilder">Request builder</param>    /// <param name="flag">The currently matched flag (already lowercased)</param>    /// <param name="argument">The argument value carried, or null if there is no argument</param>    protected override void Extract(HttpRequestBuilder httpRequestBuilder, string flag, string? argument)    {        // Configure the builder based on the flag here        if (!string.IsNullOrWhiteSpace(argument))        {            // Example: put the argument value into the X-My-Flag request header            httpRequestBuilder.WithHeader("X-My-Flag", argument);        }    }}

For more complex scenarios (such as needing to control priority or manage the cursor manually), you can implement the IHttpCurlExtractor interface directly; if ordering is needed, additionally implement the IOrderedHttpCurlExtractor interface (the smaller the Order, the higher the priority).

Registering a Custom Extractor

Custom extractors are injected through a configuration delegate when calling FromCurl:

cs
var builder = HttpRequestBuilder.FromCurl(    "curl --my-flag hello-world http://example.com",    options => options.AddExtractor(new CurlMyFlagExtractor()));

To remove a built-in extractor, use options.RemoveExtractor<T>(). For example:

cs
var builder = HttpRequestBuilder.FromCurl(    "curl http://example.com",    options => options.RemoveExtractor<CurlHeaderExtractor>());

Context Object Reference

HttpCurlParsingContext provides rich cursor-control methods:

MemberDescription
CurrentTokenGets the Token currently pointed to
PeekNext()Peeks at the next Token (without moving the pointer)
Advance(count)Advances forward by the specified number of steps (default 1)
CurrentTokenMatches(flags)Checks whether the current Token matches the given set of flags (case-insensitive)
IsEndOfTokensWhether the end of the Token list has been reached

When implementing IHttpCurlExtractor directly, you must call Advance yourself to consume Tokens; otherwise it leads to an infinite parsing loop.

Reference Implementations

All built-in extractors (such as CurlMethodExtractor, CurlFormExtractor, etc.) are built on the same interfaces and base classes. You can view their source code in the repository as a reference: View built-in extractor source code