Loading

Low level Transport example

This page demonstrates how to use the low level transport to send requests.

public class MyRequestParameters : RequestParameters
{
    public bool Pretty
    {
        get => Q("pretty");
        init => Q("pretty", value);
    }
}
using Elastic.Transport;

var body = """
           {
             "name": "my-api-key",
             "expiration": "1d",
             "...": "..."
           }
           """;

MyRequestParameters requestParameters = new()
{
    Pretty = true
};

var pathAndQuery = requestParameters.CreatePathWithQueryStrings("/_security/api_key",
    client.ElasticsearchClientSettings);
var endpointPath = new EndpointPath(Elastic.Transport.HttpMethod.POST, pathAndQuery);

// Or, if the path does not contain query parameters:
// new EndpointPath(Elastic.Transport.HttpMethod.POST, "my_path")

var response = await client.Transport
    .RequestAsync(
        endpointPath,
        PostData.String(body),
        null,
        null,
        cancellationToken: default)
    .ConfigureAwait(false);

The OnBeforeRequest callback in IElasticsearchClientSettings can be used to dynamically modify requests.

var settings = new ElasticsearchClientSettings(new Uri("http://localhost:9200))
    .OnBeforeRequest(OnBeforeRequest);

RequestConfiguration? globalRequestConfiguration = null;
ConditionalWeakTable? globalRequestConfigurations = null;

void OnBeforeRequest(ElasticsearchClient client, Request request, EndpointPath endpointPath, ref PostData? postData, ref IRequestConfiguration? requestConfiguration)
{
    // Each time a request is made, the transport creates a new `BoundConfiguration` for every `IRequestConfiguration`
    // that is not in the cache (based on reference equality).

    // To prevent frequent allocations of our mutated request configurations (and the secondary allocations for
    // `BoundConfiguration`), we have to maintain a custom cache that maps every original request configuration to the
    // mutated one.

    if (requestConfiguration is null)
    {
        globalRequestConfiguration = Interlocked.CompareExchange(
            ref globalRequestConfiguration,
            new RequestConfiguration
            {
                UserAgent = UserAgent.Create("my-custom-user-agent")
            },
            null) ?? globalRequestConfiguration;

        requestConfiguration = globalRequestConfiguration;
        return;
    }

    if (requestConfiguration is not RequestConfiguration rc)
    {
        // Only `RequestConfiguration` (not all implementations of `IRequestConfiguration`) gets cached in the
        // internal cache.
        requestConfiguration = MutateRequestConfiguration(requestConfiguration);
        return;
    }

    // ReSharper disable InconsistentlySynchronizedField

    var cache = (Interlocked.CompareExchange(
        ref globalRequestConfigurations,
        new ConditionalWeakTable(),
        null
    ) ?? globalRequestConfigurations);

    if (cache.TryGetValue(rc, out var mutatedRequestConfiguration))
    {
        requestConfiguration = mutatedRequestConfiguration;
        return;
    }

    mutatedRequestConfiguration = MutateRequestConfiguration(rc);

#if NET8_0_OR_GREATER
    cache.TryAdd(rc, mutatedRequestConfiguration);
#else
    lock (cache)
    {
        cache.Add(rc, mutatedRequestConfiguration);
    }
#endif

    // ReSharper restore InconsistentlySynchronizedField

    return;

    RequestConfiguration MutateRequestConfiguration(IRequestConfiguration requestConfiguration)
    {
        return new RequestConfiguration(requestConfiguration)
        {
            UserAgent = UserAgent.Create("my-custom-user-agent")
        };
    }
}
  1. Register the OnBeforeRequest callback.